mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-01-31 01:15:24 +01:00
Compare commits
77 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4293a40d2 | ||
|
|
9acadb21fe | ||
|
|
e82fa0bba5 | ||
|
|
45490d903a | ||
|
|
76dadf792a | ||
|
|
204829865d | ||
|
|
84dc2959c5 | ||
|
|
a85b203689 | ||
|
|
135d40fb7f | ||
|
|
a0bc7a5d0e | ||
|
|
955b925633 | ||
|
|
cc338cdd9d | ||
|
|
082a28dc13 | ||
|
|
664e14bd6c | ||
|
|
7ea33400a9 | ||
|
|
32a3e8e62d | ||
|
|
fa953d7bb3 | ||
|
|
66cd51bcd5 | ||
|
|
ed7ebb77ca | ||
|
|
10fc9a790d | ||
|
|
c42330eebf | ||
|
|
680e05fead | ||
|
|
1f7b98bf6b | ||
|
|
c3a20d421e | ||
|
|
f03ab6f728 | ||
|
|
e9275d78b5 | ||
|
|
8ababb3890 | ||
|
|
422aba4b20 | ||
|
|
045b9bbf40 | ||
|
|
1f519acf92 | ||
|
|
ac9ebdecba | ||
|
|
1861394216 | ||
|
|
11cc884c96 | ||
|
|
5710c2740c | ||
|
|
ec96feb22e | ||
|
|
a1173c53d3 | ||
|
|
ac0deff9b2 | ||
|
|
3ae707464c | ||
|
|
00ef419023 | ||
|
|
448a279cd4 | ||
|
|
70e13adfde | ||
|
|
25bc280818 | ||
|
|
32e073002a | ||
|
|
911314e948 | ||
|
|
a73fcb343c | ||
|
|
251b2960f8 | ||
|
|
5bb99105c3 | ||
|
|
fa6e1b0949 | ||
|
|
4297918ce2 | ||
|
|
629a58469b | ||
|
|
5ff1eed28c | ||
|
|
4506ff1c46 | ||
|
|
4daa455524 | ||
|
|
433e99baec | ||
|
|
a1ac6662d3 | ||
|
|
87366cda9d | ||
|
|
edb2b37a92 | ||
|
|
4462b3f91d | ||
|
|
57ded8a022 | ||
|
|
304a7f9d30 | ||
|
|
73a09ffe6c | ||
|
|
4e5d7bd407 | ||
|
|
2a3452a489 | ||
|
|
2e7f951399 | ||
|
|
efb66c1d37 | ||
|
|
6fc88a4499 | ||
|
|
238b29836e | ||
|
|
58cbb61aac | ||
|
|
2c7a168029 | ||
|
|
bb4ee5f0fb | ||
|
|
1940fdb3d3 | ||
|
|
bf269e1295 | ||
|
|
262bbdae9f | ||
|
|
6e5c228980 | ||
|
|
62d46797ca | ||
|
|
3b561be221 | ||
|
|
d1e1e59059 |
@@ -25,7 +25,7 @@ LIBBACKTRACE=ad106d5fdd5d960bd33fae1c48a351af567fd075
|
||||
LIBJPEGTURBO=3.1.2
|
||||
LIBPNG=1.6.53
|
||||
LIBWEBP=1.6.0
|
||||
NVENC=11.1.5.3
|
||||
NVENC=13.0.19.0
|
||||
SDL=SDL3-3.4.0
|
||||
QT=6.10.1
|
||||
QTAPNG=1.3.0
|
||||
@@ -57,7 +57,7 @@ e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWE
|
||||
082cbf5f429e0d80820f68dc2b507a94d4cc1b4e70817b119bbb8ec6a69584b8 $SDL.tar.gz
|
||||
452a1a290bd0cf18737fad0057dc17b7fdf10a73eda2d6d4f31ba04fda25ef2c libpng-$LIBPNG-apng.patch.gz
|
||||
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
|
||||
2974b91062197e0527dffa3aadd8fe3bfa6681ae45f5ff9181bc0ca6479abd59 nv-codec-headers-$NVENC.tar.gz
|
||||
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
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<url type="donation">https://github.com/sponsors/PCSX2</url>
|
||||
<url type="faq">https://pcsx2.net/docs/</url>
|
||||
<url type="help">https://pcsx2.net/discord</url>
|
||||
<url type="contribute">https://github.com/PCSX2/pcsx2/blob/master/.github/CONTRIBUTING.md</url>
|
||||
<url type="contribute">https://pcsx2.net/docs/category/contributing</url>
|
||||
<url type="translate">https://crowdin.com/project/pcsx2-emulator</url>
|
||||
<url type="contact">https://mastodon.social/@PCSX2</url>
|
||||
<screenshots>
|
||||
|
||||
@@ -65,6 +65,8 @@ set SHADERC_GLSLANG=7a47e2531cb334982b2a2dd8513dca0a3de4373d
|
||||
set SHADERC_SPIRVHEADERS=b824a462d4256d720bebb40e78b9eb8f78bbb305
|
||||
set SHADERC_SPIRVTOOLS=971a7b6e8d7740035bbff089bbbf9f42951ecfd5
|
||||
|
||||
set AGILITYSDK=1.618.5
|
||||
|
||||
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
|
||||
@@ -84,6 +86,7 @@ call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/r
|
||||
call :downloadfile "KDDockWidgets-%KDDOCKWIDGETS%.zip" "https://github.com/KDAB/KDDockWidgets/archive/v%KDDOCKWIDGETS%.zip" 47ddb48197872055f0adf8e90a7235f8a3b795ca1ee3a28ac2c504c673ae3806 || goto error
|
||||
call :downloadfile "plutovg-%PLUTOVG%.zip" "https://github.com/sammycage/plutovg/archive/v%PLUTOVG%.zip" 4fe4e48f28aa80171b2166d45c0976ab0f21eecedb52cd4c3ef73b5afb48fac9 || goto error
|
||||
call :downloadfile "plutosvg-%PLUTOSVG%.zip" "https://github.com/sammycage/plutosvg/archive/v%PLUTOSVG%.zip" 82dee2c57ad712bdd6d6d81d3e76249d89caa4b5a4214353660fd5adff12201a || goto error
|
||||
call :downloadfile: "agility-sdk-%AGILITYSDK%.nupkg" "https://www.nuget.org/api/v2/package/Microsoft.Direct3D.D3D12/%AGILITYSDK%" 0027fc24f947c48dbded13ada7d280be221eb651644e23a8a476f0f1f0a079dd || goto error
|
||||
|
||||
call :downloadfile "shaderc-%SHADERC%.zip" "https://github.com/google/shaderc/archive/refs/tags/v%SHADERC%.zip" fab72d1a38eacea52710d18edb95dfd75db894ad869675d07a1eb26827da9b15 || goto error
|
||||
call :downloadfile "shaderc-glslang-%SHADERC_GLSLANG%.zip" "https://github.com/KhronosGroup/glslang/archive/%SHADERC_GLSLANG%.zip" 4a118247386ffba9160113f146f2189ba5abe3995db357114d7112ede6bd3cd1 || goto error
|
||||
@@ -305,6 +308,20 @@ cmake --build build --parallel || goto error
|
||||
ninja -C build install || goto error
|
||||
cd .. || goto error
|
||||
|
||||
echo Unpacking Agility SDK
|
||||
rmdir /S /Q "agility-sdk-%AGILITYSDK%"
|
||||
%SEVENZIP% x -o"agility-sdk-%AGILITYSDK%" "agility-sdk-%AGILITYSDK%.nupkg" || goto error
|
||||
cd "agility-sdk-%AGILITYSDK%" || goto error
|
||||
if not exist "%INSTALLDIR%\bin\D3D12" (
|
||||
mkdir "%INSTALLDIR%\bin\D3D12" || goto error
|
||||
)
|
||||
rem the pdbs aren't in the list of distributable files, so only copy the dlls.
|
||||
copy "build\native\bin\arm64\D3D12Core.dll" "%INSTALLDIR%\bin\D3D12\D3D12Core.dll" || goto error
|
||||
if %DEBUG%==1 (
|
||||
copy "build\native\bin\arm64\d3d12SDKLayers.dll" "%INSTALLDIR%\bin\D3D12\d3d12SDKLayers.dll" || goto error
|
||||
)
|
||||
cd .. || goto error
|
||||
|
||||
echo Building shaderc...
|
||||
rmdir /S /Q "shaderc-%SHADERC%"
|
||||
%SEVENZIP% x "shaderc-%SHADERC%.zip" || goto error
|
||||
|
||||
@@ -63,6 +63,8 @@ set SHADERC_GLSLANG=7a47e2531cb334982b2a2dd8513dca0a3de4373d
|
||||
set SHADERC_SPIRVHEADERS=b824a462d4256d720bebb40e78b9eb8f78bbb305
|
||||
set SHADERC_SPIRVTOOLS=971a7b6e8d7740035bbff089bbbf9f42951ecfd5
|
||||
|
||||
set AGILITYSDK=1.618.5
|
||||
|
||||
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
|
||||
@@ -82,6 +84,7 @@ call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/r
|
||||
call :downloadfile "KDDockWidgets-%KDDOCKWIDGETS%.zip" "https://github.com/KDAB/KDDockWidgets/archive/v%KDDOCKWIDGETS%.zip" 47ddb48197872055f0adf8e90a7235f8a3b795ca1ee3a28ac2c504c673ae3806 || goto error
|
||||
call :downloadfile "plutovg-%PLUTOVG%.zip" "https://github.com/sammycage/plutovg/archive/v%PLUTOVG%.zip" 4fe4e48f28aa80171b2166d45c0976ab0f21eecedb52cd4c3ef73b5afb48fac9 || goto error
|
||||
call :downloadfile "plutosvg-%PLUTOSVG%.zip" "https://github.com/sammycage/plutosvg/archive/v%PLUTOSVG%.zip" 82dee2c57ad712bdd6d6d81d3e76249d89caa4b5a4214353660fd5adff12201a || goto error
|
||||
call :downloadfile: "agility-sdk-%AGILITYSDK%.nupkg" "https://www.nuget.org/api/v2/package/Microsoft.Direct3D.D3D12/%AGILITYSDK%" 0027fc24f947c48dbded13ada7d280be221eb651644e23a8a476f0f1f0a079dd || goto error
|
||||
|
||||
call :downloadfile "shaderc-%SHADERC%.zip" "https://github.com/google/shaderc/archive/refs/tags/v%SHADERC%.zip" fab72d1a38eacea52710d18edb95dfd75db894ad869675d07a1eb26827da9b15 || goto error
|
||||
call :downloadfile "shaderc-glslang-%SHADERC_GLSLANG%.zip" "https://github.com/KhronosGroup/glslang/archive/%SHADERC_GLSLANG%.zip" 4a118247386ffba9160113f146f2189ba5abe3995db357114d7112ede6bd3cd1 || goto error
|
||||
@@ -302,6 +305,20 @@ cmake --build build --parallel || goto error
|
||||
ninja -C build install || goto error
|
||||
cd .. || goto error
|
||||
|
||||
echo Unpacking Agility SDK
|
||||
rmdir /S /Q "agility-sdk-%AGILITYSDK%"
|
||||
%SEVENZIP% x -o"agility-sdk-%AGILITYSDK%" "agility-sdk-%AGILITYSDK%.nupkg" || goto error
|
||||
cd "agility-sdk-%AGILITYSDK%" || goto error
|
||||
if not exist "%INSTALLDIR%\bin\D3D12" (
|
||||
mkdir "%INSTALLDIR%\bin\D3D12" || goto error
|
||||
)
|
||||
rem the pdbs aren't in the list of distributable files, so only copy the dlls.
|
||||
copy "build\native\bin\x64\D3D12Core.dll" "%INSTALLDIR%\bin\D3D12\D3D12Core.dll" || goto error
|
||||
if %DEBUG%==1 (
|
||||
copy "build\native\bin\x64\d3d12SDKLayers.dll" "%INSTALLDIR%\bin\D3D12\d3d12SDKLayers.dll" || goto error
|
||||
)
|
||||
cd .. || goto error
|
||||
|
||||
echo Building shaderc...
|
||||
rmdir /S /Q "shaderc-%SHADERC%"
|
||||
%SEVENZIP% x "shaderc-%SHADERC%.zip" || goto error
|
||||
|
||||
4
3rdparty/rcheevos/CHANGELOG.md
vendored
4
3rdparty/rcheevos/CHANGELOG.md
vendored
@@ -1,3 +1,7 @@
|
||||
# v12.2.1
|
||||
* fix parsing of leaderboards with comparisons in legacy-formatted values
|
||||
* fix validation warning on long AddSource chains
|
||||
|
||||
# v12.2.0
|
||||
* add rc_client_create_subset_list
|
||||
* add rc_client_begin_fetch_game_titles
|
||||
|
||||
2
3rdparty/rcheevos/src/rc_version.h
vendored
2
3rdparty/rcheevos/src/rc_version.h
vendored
@@ -9,7 +9,7 @@ RC_BEGIN_C_DECLS
|
||||
|
||||
#define RCHEEVOS_VERSION_MAJOR 12
|
||||
#define RCHEEVOS_VERSION_MINOR 2
|
||||
#define RCHEEVOS_VERSION_PATCH 0
|
||||
#define RCHEEVOS_VERSION_PATCH 1
|
||||
|
||||
#define RCHEEVOS_MAKE_VERSION(major, minor, patch) (major * 1000000 + minor * 1000 + patch)
|
||||
#define RCHEEVOS_VERSION RCHEEVOS_MAKE_VERSION(RCHEEVOS_VERSION_MAJOR, RCHEEVOS_VERSION_MINOR, RCHEEVOS_VERSION_PATCH)
|
||||
|
||||
2
3rdparty/rcheevos/src/rcheevos/rc_validate.c
vendored
2
3rdparty/rcheevos/src/rcheevos/rc_validate.c
vendored
@@ -510,6 +510,7 @@ static void rc_combine_ranges(uint32_t* min_val, uint32_t* max_val, uint8_t oper
|
||||
break;
|
||||
|
||||
case RC_OPERATOR_ADD:
|
||||
case RC_OPERATOR_ADD_ACCUMULATOR:
|
||||
if (*min_val > *max_val) { /* underflow occurred */
|
||||
*max_val += oper_max_val;
|
||||
}
|
||||
@@ -522,6 +523,7 @@ static void rc_combine_ranges(uint32_t* min_val, uint32_t* max_val, uint8_t oper
|
||||
break;
|
||||
|
||||
case RC_OPERATOR_SUB:
|
||||
case RC_OPERATOR_SUB_ACCUMULATOR:
|
||||
*min_val -= oper_max_val;
|
||||
*max_val -= oper_min_val;
|
||||
break;
|
||||
|
||||
8
3rdparty/rcheevos/src/rcheevos/value.c
vendored
8
3rdparty/rcheevos/src/rcheevos/value.c
vendored
@@ -180,9 +180,11 @@ static void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_par
|
||||
return;
|
||||
}
|
||||
|
||||
if (!rc_operator_is_modifying(cond->oper)) {
|
||||
parse->offset = RC_INVALID_OPERATOR;
|
||||
return;
|
||||
if (cond->type == RC_CONDITION_MEASURED && !rc_operator_is_modifying(cond->oper)) {
|
||||
/* ignore non-modifying operator on measured clause. if it were parsed as an AddSource
|
||||
* or SubSource, that would have already happened in rc_parse_condition_internal, and
|
||||
* legacy formatted values are essentially a series of AddSources. */
|
||||
cond->oper = RC_OPERATOR_NONE;
|
||||
}
|
||||
|
||||
rc_condition_update_parse_state(cond, parse);
|
||||
|
||||
Binary file not shown.
@@ -2075,6 +2075,8 @@ SCAJ-20128:
|
||||
SCAJ-20129:
|
||||
name: "Ponkotsu Roman Daikatsugeki Bumpy Trot"
|
||||
region: "NTSC-Unk"
|
||||
roundModes:
|
||||
eeRoundMode: 0 # Fixes broken load triggers.
|
||||
gsHWFixes:
|
||||
getSkipCount: "GSC_IRem"
|
||||
halfPixelOffset: 2 # Aligns effects.
|
||||
@@ -4764,6 +4766,9 @@ SCES-50034:
|
||||
name: "MotoGP"
|
||||
region: "PAL-M5"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
gpuTargetCLUT: 2 # Fixes some (haze?) shuffle colors.
|
||||
textureInsideRT: 1 # Fixes rendering artifacts on racers.
|
||||
SCES-50105:
|
||||
name: "Sky Odyssey"
|
||||
region: "PAL-M5"
|
||||
@@ -7611,6 +7616,8 @@ SCKA-20058:
|
||||
name: "액션 로망 범피 트롯"
|
||||
name-en: "Action Romance Bumpy Trot"
|
||||
region: "NTSC-K"
|
||||
roundModes:
|
||||
eeRoundMode: 0 # Fixes broken load triggers.
|
||||
gsHWFixes:
|
||||
getSkipCount: "GSC_IRem"
|
||||
halfPixelOffset: 2 # Aligns effects.
|
||||
@@ -24774,6 +24781,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"
|
||||
@@ -25575,6 +25587,8 @@ SLES-54137:
|
||||
SLES-54138:
|
||||
name: "Steambot Chronicles"
|
||||
region: "PAL-E"
|
||||
roundModes:
|
||||
eeRoundMode: 0 # Fixes broken load triggers.
|
||||
gsHWFixes:
|
||||
getSkipCount: "GSC_IRem"
|
||||
halfPixelOffset: 2 # Aligns effects.
|
||||
@@ -26225,6 +26239,8 @@ SLES-54333:
|
||||
SLES-54335:
|
||||
name: "Steambot Chronicles"
|
||||
region: "PAL-F"
|
||||
roundModes:
|
||||
eeRoundMode: 0 # Fixes broken load triggers.
|
||||
gsHWFixes:
|
||||
getSkipCount: "GSC_IRem"
|
||||
halfPixelOffset: 2 # Aligns effects.
|
||||
@@ -35850,6 +35866,8 @@ SLPM-60255:
|
||||
name-sort: "ぽんこつろまんだいかつげきばんぴーとろっと [たいけんばん]"
|
||||
name-en: "Ponkotsu Roeman Daikatsugeki Bumpy Trot [Trial]"
|
||||
region: "NTSC-J"
|
||||
roundModes:
|
||||
eeRoundMode: 0 # Fixes broken load triggers.
|
||||
gsHWFixes:
|
||||
getSkipCount: "GSC_IRem"
|
||||
halfPixelOffset: 2 # Aligns effects.
|
||||
@@ -35921,6 +35939,8 @@ SLPM-60266:
|
||||
name-sort: "ぽんこつろまんだいかつげきばんぴーとろっと [たいけんばん]"
|
||||
name-en: "Ponkotsu Roeman Daikatsugeki Bumpy Trot [Trial]"
|
||||
region: "NTSC-J"
|
||||
roundModes:
|
||||
eeRoundMode: 0 # Fixes broken load triggers.
|
||||
gsHWFixes:
|
||||
getSkipCount: "GSC_IRem"
|
||||
halfPixelOffset: 2 # Aligns effects.
|
||||
@@ -40074,6 +40094,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: "おにはまばくそうぐれんたい げきとうへん"
|
||||
@@ -54743,6 +54768,9 @@ SLPS-20040:
|
||||
name-sort: "もとGP"
|
||||
name-en: "MotoGP"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
gpuTargetCLUT: 2 # Fixes some (haze?) shuffle colors.
|
||||
textureInsideRT: 1 # Fixes rendering artifacts on racers.
|
||||
SLPS-20041:
|
||||
name: "麻雀悟空 大聖"
|
||||
name-sort: "まーじゃんごくう たいせい"
|
||||
@@ -59818,6 +59846,8 @@ SLPS-25457:
|
||||
name-sort: "ぽんこつろまんだいかつげきばんぴーとろっと"
|
||||
name-en: "Ponkotsu Roeman Daikatsugeki Bumpy Trot"
|
||||
region: "NTSC-J"
|
||||
roundModes:
|
||||
eeRoundMode: 0 # Fixes broken load triggers.
|
||||
gsHWFixes:
|
||||
getSkipCount: "GSC_IRem"
|
||||
halfPixelOffset: 2 # Aligns effects.
|
||||
@@ -60491,7 +60521,6 @@ SLPS-25574:
|
||||
halfPixelOffset: 2 # Aligns effects.
|
||||
nativeScaling: 1 # Helps align effects.
|
||||
textureInsideRT: 1 # Fixes channel shuffles.
|
||||
preloadFrameData: 1 # Fixes menu graphics.
|
||||
autoFlush: 1 # Fixes motion blur.
|
||||
minimumBlendingLevel: 2 # Improves post effects.
|
||||
SLPS-25575:
|
||||
@@ -61216,7 +61245,6 @@ SLPS-25682:
|
||||
halfPixelOffset: 2 # Aligns effects.
|
||||
nativeScaling: 1 # Helps align effects.
|
||||
textureInsideRT: 1 # Fixes channel shuffles.
|
||||
preloadFrameData: 1 # Fixes menu graphics.
|
||||
autoFlush: 1 # Fixes motion blur.
|
||||
minimumBlendingLevel: 2 # Improves post effects.
|
||||
SLPS-25683:
|
||||
@@ -61224,6 +61252,8 @@ SLPS-25683:
|
||||
name-sort: "ぽんこつろまんだいかつげきばんぴーとろっと [Irem COLLECTION]"
|
||||
name-en: "Ponkotsu Roman Daikatsugeki Bumpy Trot [Irem Collection]"
|
||||
region: "NTSC-J"
|
||||
roundModes:
|
||||
eeRoundMode: 0 # Fixes broken load triggers.
|
||||
gsHWFixes:
|
||||
getSkipCount: "GSC_IRem"
|
||||
halfPixelOffset: 2 # Aligns effects.
|
||||
@@ -61846,7 +61876,6 @@ SLPS-25787:
|
||||
halfPixelOffset: 2 # Aligns effects.
|
||||
nativeScaling: 1 # Helps align effects.
|
||||
textureInsideRT: 1 # Fixes channel shuffles.
|
||||
preloadFrameData: 1 # Fixes menu graphics.
|
||||
autoFlush: 1 # Fixes motion blur.
|
||||
minimumBlendingLevel: 2 # Improves post effects.
|
||||
SLPS-25788:
|
||||
@@ -62258,7 +62287,6 @@ SLPS-25852:
|
||||
halfPixelOffset: 2 # Aligns effects.
|
||||
nativeScaling: 1 # Helps align effects.
|
||||
textureInsideRT: 1 # Fixes channel shuffles.
|
||||
preloadFrameData: 1 # Fixes menu graphics.
|
||||
autoFlush: 1 # Fixes motion blur.
|
||||
minimumBlendingLevel: 2 # Improves post effects.
|
||||
SLPS-25853:
|
||||
@@ -64233,6 +64261,9 @@ SLUS-20058:
|
||||
name: "MotoGP"
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
gpuTargetCLUT: 2 # Fixes some (haze?) shuffle colors.
|
||||
textureInsideRT: 1 # Fixes rendering artifacts on racers.
|
||||
SLUS-20062:
|
||||
name: "Grand Theft Auto III"
|
||||
region: "NTSC-U"
|
||||
@@ -71355,6 +71386,8 @@ SLUS-21344:
|
||||
name: "Steambot Chronicles"
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
roundModes:
|
||||
eeRoundMode: 0 # Fixes broken load triggers.
|
||||
gsHWFixes:
|
||||
getSkipCount: "GSC_IRem"
|
||||
halfPixelOffset: 2 # Aligns effects.
|
||||
@@ -74896,6 +74929,8 @@ SLUS-28059:
|
||||
SLUS-28061:
|
||||
name: "Steambot Chronicles [Trade Demo]"
|
||||
region: "NTSC-U"
|
||||
roundModes:
|
||||
eeRoundMode: 0 # Fixes broken load triggers.
|
||||
gsHWFixes:
|
||||
getSkipCount: "GSC_IRem"
|
||||
halfPixelOffset: 2 # Aligns effects.
|
||||
@@ -75674,6 +75709,8 @@ SLUS-29185:
|
||||
SLUS-29188:
|
||||
name: "Steambot Chronicles [Regular Demo]"
|
||||
region: "NTSC-U"
|
||||
roundModes:
|
||||
eeRoundMode: 0 # Fixes broken load triggers.
|
||||
gsHWFixes:
|
||||
getSkipCount: "GSC_IRem"
|
||||
halfPixelOffset: 2 # Aligns effects.
|
||||
|
||||
@@ -236,6 +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,
|
||||
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,
|
||||
@@ -245,8 +246,8 @@
|
||||
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,Be1 GC101 Controller 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,Be1 GC100XF Controller,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,
|
||||
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,
|
||||
@@ -463,7 +464,7 @@
|
||||
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,
|
||||
030000006b140000010c000000000000,Nacon GC 400ES,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,
|
||||
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,
|
||||
0300000085320000190d000000000000,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,
|
||||
@@ -1305,7 +1306,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000632500007a05000001020000,Cosmic Byte Ares Wired Controller,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,
|
||||
03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Linux,
|
||||
03000000a306000022f6000011010000,Cyborg V3 Rumble,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,
|
||||
030000005e0400008e02000002010000,Data Frog S80,a:b1,b:b0,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:b3,y:b2,platform:Linux,
|
||||
03000000791d00000103000010010000,Dual Box Wii Classic Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
|
||||
030000006f0e00003001000001010000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000c11100000191000011010000,EasySMX,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,
|
||||
@@ -1512,9 +1512,10 @@ 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 GC 400ES,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,
|
||||
03000000853200000706000012010000,Nacon GC-100,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,
|
||||
05000000853200000503000000010000,Nacon MG-X 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,
|
||||
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,
|
||||
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,
|
||||
030000000d0f00000900000010010000,Natec Genesis P44,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
@@ -1799,18 +1800,20 @@ 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,
|
||||
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,
|
||||
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 EasySMX,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,
|
||||
030000005e0400008e02000072050000,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:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
030000006f0e00001304000000010000,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:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
03000000ffff0000ffff000000010000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,
|
||||
030000005e0400000a0b000005040000,Xbox One Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,
|
||||
|
||||
@@ -112,7 +112,7 @@ struct VS_OUTPUT
|
||||
|
||||
struct PS_INPUT
|
||||
{
|
||||
float4 p : SV_Position;
|
||||
noperspective centroid float4 p : SV_Position;
|
||||
float4 t : TEXCOORD0;
|
||||
float4 ti : TEXCOORD2;
|
||||
#if VS_IIP != 0 || GS_IIP != 0 || PS_IIP != 0
|
||||
@@ -140,7 +140,7 @@ struct PS_OUTPUT
|
||||
#endif
|
||||
#endif
|
||||
#if PS_ZCLAMP || PS_ZFLOOR
|
||||
float depth : SV_Depth;
|
||||
float depth : SV_DepthLessEqual;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -168,6 +168,7 @@ cbuffer cb1
|
||||
float4 LODParams;
|
||||
float4 STRange;
|
||||
int4 ChannelShuffle;
|
||||
float2 ChannelShuffleOffset;
|
||||
float2 TC_OffsetHack;
|
||||
float2 STScale;
|
||||
float4x4 DitherMatrix;
|
||||
@@ -740,7 +741,7 @@ float4 fog(float4 c, float f)
|
||||
{
|
||||
if(PS_FOG)
|
||||
{
|
||||
c.rgb = trunc(lerp(FogColor, c.rgb, f));
|
||||
c.rgb = trunc(lerp(FogColor, c.rgb, (f * 255.0f) / 256.0f));
|
||||
}
|
||||
|
||||
return c;
|
||||
@@ -757,17 +758,17 @@ float4 ps_color(PS_INPUT input)
|
||||
#endif
|
||||
|
||||
#if PS_CHANNEL_FETCH == 1
|
||||
float4 T = fetch_red(int2(input.p.xy));
|
||||
float4 T = fetch_red(int2(input.p.xy + ChannelShuffleOffset));
|
||||
#elif PS_CHANNEL_FETCH == 2
|
||||
float4 T = fetch_green(int2(input.p.xy));
|
||||
float4 T = fetch_green(int2(input.p.xy + ChannelShuffleOffset));
|
||||
#elif PS_CHANNEL_FETCH == 3
|
||||
float4 T = fetch_blue(int2(input.p.xy));
|
||||
float4 T = fetch_blue(int2(input.p.xy + ChannelShuffleOffset));
|
||||
#elif PS_CHANNEL_FETCH == 4
|
||||
float4 T = fetch_alpha(int2(input.p.xy));
|
||||
float4 T = fetch_alpha(int2(input.p.xy + ChannelShuffleOffset));
|
||||
#elif PS_CHANNEL_FETCH == 5
|
||||
float4 T = fetch_rgb(int2(input.p.xy));
|
||||
float4 T = fetch_rgb(int2(input.p.xy + ChannelShuffleOffset));
|
||||
#elif PS_CHANNEL_FETCH == 6
|
||||
float4 T = fetch_gXbY(int2(input.p.xy));
|
||||
float4 T = fetch_gXbY(int2(input.p.xy + ChannelShuffleOffset));
|
||||
#elif PS_DEPTH_FMT > 0
|
||||
float4 T = sample_depth(st_int, input.p.xy);
|
||||
#else
|
||||
|
||||
@@ -49,6 +49,7 @@ layout(std140, binding = 0) uniform cb21
|
||||
vec4 STRange;
|
||||
|
||||
ivec4 ChannelShuffle;
|
||||
vec2 ChannelShuffleOffset;
|
||||
|
||||
vec2 TC_OffsetHack;
|
||||
vec2 STScale;
|
||||
@@ -112,6 +113,10 @@ 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
|
||||
layout(depth_less) out float gl_FragDepth;
|
||||
#endif
|
||||
|
||||
vec4 sample_from_rt()
|
||||
{
|
||||
#if !NEEDS_RT
|
||||
@@ -315,7 +320,7 @@ int fetch_raw_depth()
|
||||
#if PS_TEX_IS_FB == 1
|
||||
return int(sample_from_rt().r * multiplier);
|
||||
#else
|
||||
return int(texelFetch(TextureSampler, ivec2(gl_FragCoord.xy), 0).r * multiplier);
|
||||
return int(texelFetch(TextureSampler, ivec2(gl_FragCoord.xy + ChannelShuffleOffset), 0).r * multiplier);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -324,7 +329,7 @@ vec4 fetch_raw_color()
|
||||
#if PS_TEX_IS_FB == 1
|
||||
return sample_from_rt();
|
||||
#else
|
||||
return texelFetch(TextureSampler, ivec2(gl_FragCoord.xy), 0);
|
||||
return texelFetch(TextureSampler, ivec2(gl_FragCoord.xy + ChannelShuffleOffset), 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -646,7 +651,7 @@ bool atst(vec4 C)
|
||||
void fog(inout vec4 C, float f)
|
||||
{
|
||||
#if PS_FOG != 0
|
||||
C.rgb = trunc(mix(FogColor, C.rgb, f));
|
||||
C.rgb = trunc(mix(FogColor, C.rgb, (f * 255.0f) / 256.0f));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -316,6 +316,7 @@ layout(std140, set = 0, binding = 1) uniform cb1
|
||||
vec4 LODParams;
|
||||
vec4 STRange;
|
||||
ivec4 ChannelShuffle;
|
||||
vec2 ChannelShuffleOffset;
|
||||
vec2 TC_OffsetHack;
|
||||
vec2 STScale;
|
||||
mat4 DitherMatrix;
|
||||
@@ -360,6 +361,10 @@ layout(set = 1, binding = 1) uniform texture2D Palette;
|
||||
layout(set = 1, binding = 3) uniform texture2D PrimMinTexture;
|
||||
#endif
|
||||
|
||||
#if PS_ZFLOOR || PS_ZCLAMP
|
||||
layout(depth_less) out float gl_FragDepth;
|
||||
#endif
|
||||
|
||||
#if NEEDS_TEX
|
||||
|
||||
vec4 sample_c(vec2 uv)
|
||||
@@ -907,7 +912,7 @@ bool atst(vec4 C)
|
||||
vec4 fog(vec4 c, float f)
|
||||
{
|
||||
#if PS_FOG
|
||||
c.rgb = trunc(mix(FogColor, c.rgb, f));
|
||||
c.rgb = trunc(mix(FogColor, c.rgb, (f * 255.0f) / 256.0f));
|
||||
#endif
|
||||
|
||||
return c;
|
||||
@@ -926,17 +931,17 @@ vec4 ps_color()
|
||||
#if !NEEDS_TEX
|
||||
vec4 T = vec4(0.0f);
|
||||
#elif PS_CHANNEL_FETCH == 1
|
||||
vec4 T = fetch_red(ivec2(gl_FragCoord.xy));
|
||||
vec4 T = fetch_red(ivec2(gl_FragCoord.xy + ChannelShuffleOffset));
|
||||
#elif PS_CHANNEL_FETCH == 2
|
||||
vec4 T = fetch_green(ivec2(gl_FragCoord.xy));
|
||||
vec4 T = fetch_green(ivec2(gl_FragCoord.xy + ChannelShuffleOffset));
|
||||
#elif PS_CHANNEL_FETCH == 3
|
||||
vec4 T = fetch_blue(ivec2(gl_FragCoord.xy));
|
||||
vec4 T = fetch_blue(ivec2(gl_FragCoord.xy + ChannelShuffleOffset));
|
||||
#elif PS_CHANNEL_FETCH == 4
|
||||
vec4 T = fetch_alpha(ivec2(gl_FragCoord.xy));
|
||||
vec4 T = fetch_alpha(ivec2(gl_FragCoord.xy + ChannelShuffleOffset));
|
||||
#elif PS_CHANNEL_FETCH == 5
|
||||
vec4 T = fetch_rgb(ivec2(gl_FragCoord.xy));
|
||||
vec4 T = fetch_rgb(ivec2(gl_FragCoord.xy + ChannelShuffleOffset));
|
||||
#elif PS_CHANNEL_FETCH == 6
|
||||
vec4 T = fetch_gXbY(ivec2(gl_FragCoord.xy));
|
||||
vec4 T = fetch_gXbY(ivec2(gl_FragCoord.xy + ChannelShuffleOffset));
|
||||
#elif PS_DEPTH_FMT > 0
|
||||
vec4 T = sample_depth(st_int, ivec2(gl_FragCoord.xy));
|
||||
#else
|
||||
|
||||
@@ -110,8 +110,10 @@ disable_compiler_warnings_for_target(speex)
|
||||
if(ENABLE_QT_UI)
|
||||
find_package(Qt6 6.10.0 COMPONENTS CoreTools Core GuiTools Gui WidgetsTools Widgets LinguistTools REQUIRED)
|
||||
|
||||
if (Qt6_VERSION VERSION_GREATER_EQUAL 6.10.0)
|
||||
find_package(Qt6 COMPONENTS CorePrivate GuiPrivate WidgetsPrivate REQUIRED)
|
||||
if(NOT WIN32 AND NOT APPLE)
|
||||
if (Qt6_VERSION VERSION_GREATER_EQUAL 6.10.0)
|
||||
find_package(Qt6 COMPONENTS CorePrivate GuiPrivate WidgetsPrivate REQUIRED)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# The docking system for the debugger.
|
||||
|
||||
@@ -28,6 +28,10 @@
|
||||
<DepsDLLs Include="$(DepsBinDir)plutovg.dll" />
|
||||
<DepsDLLs Include="$(DepsBinDir)plutosvg.dll" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AgilityDLLs Condition="Exists('$(DepsBinDir)D3D12\D3D12Core.dll')" Include="$(DepsBinDir)D3D12\D3D12Core.dll" />
|
||||
<AgilityDLLs Condition="Exists('$(DepsBinDir)D3D12\d3d12SDKLayers.dll')" Include="$(DepsBinDir)D3D12\d3d12SDKLayers.dll" />
|
||||
</ItemGroup>
|
||||
<Target Name="DepsListPDBs"
|
||||
AfterTargets="Build">
|
||||
<ItemGroup>
|
||||
@@ -56,4 +60,15 @@
|
||||
SkipUnchangedFiles="true"
|
||||
/>
|
||||
</Target>
|
||||
<Target Name="DepsCopyAgilityDLLs"
|
||||
AfterTargets="Build"
|
||||
Inputs="@(AgilityDLLs)"
|
||||
Outputs="@(AgilityDLLs -> '$(OutDir)D3D12\%(RecursiveDir)%(Filename)%(Extension)')">
|
||||
<Message Text="Copying Agility SDK DLLs" Importance="High" />
|
||||
<Copy
|
||||
SourceFiles="@(AgilityDLLs)"
|
||||
DestinationFolder="$(OutDir)D3D12"
|
||||
SkipUnchangedFiles="true"
|
||||
/>
|
||||
</Target>
|
||||
</Project>
|
||||
|
||||
@@ -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)):
|
||||
|
||||
@@ -289,11 +289,14 @@ target_link_libraries(pcsx2-qt PRIVATE
|
||||
PCSX2
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::GuiPrivate
|
||||
Qt6::Widgets
|
||||
KDAB::kddockwidgets
|
||||
)
|
||||
|
||||
if(NOT WIN32 AND NOT APPLE)
|
||||
target_link_libraries(pcsx2-qt PRIVATE Qt6::GuiPrivate)
|
||||
endif()
|
||||
|
||||
# Our Qt builds may have exceptions on, so force them off.
|
||||
target_compile_definitions(pcsx2-qt PRIVATE QT_NO_EXCEPTIONS)
|
||||
|
||||
|
||||
@@ -123,6 +123,11 @@ void BreakpointDialog::accept()
|
||||
bp->cond.expression = expr;
|
||||
bp->cond.expressionString = m_ui.txtCondition->text().toStdString();
|
||||
}
|
||||
else
|
||||
{
|
||||
bp->hasCond = false;
|
||||
bp->cond = {};
|
||||
}
|
||||
}
|
||||
if (auto* mc = std::get_if<MemCheck>(&m_bp_mc))
|
||||
{
|
||||
@@ -159,6 +164,11 @@ void BreakpointDialog::accept()
|
||||
mc->cond.expression = expr;
|
||||
mc->cond.expressionString = m_ui.txtCondition->text().toStdString();
|
||||
}
|
||||
else
|
||||
{
|
||||
mc->hasCond = false;
|
||||
mc->cond = {};
|
||||
}
|
||||
|
||||
int condition = 0;
|
||||
if (m_ui.chkRead->isChecked())
|
||||
|
||||
@@ -26,8 +26,6 @@
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include "common/RedtapeWindows.h"
|
||||
#elif !defined(APPLE)
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#endif
|
||||
|
||||
DisplaySurface::DisplaySurface()
|
||||
@@ -477,6 +475,13 @@ bool DisplaySurface::eventFilter(QObject* object, QEvent* event)
|
||||
}
|
||||
return false;
|
||||
|
||||
case QEvent::FocusIn:
|
||||
// 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))
|
||||
w->window()->activateWindow();
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -67,17 +67,6 @@ struct KeyCodeName
|
||||
const char* icon_name;
|
||||
};
|
||||
|
||||
// Extended keys for left/right versions, Non conflicting with Qt::Key enum
|
||||
enum ExtendedKeys
|
||||
{
|
||||
Key_RightShift = 0x01100022,
|
||||
Key_LeftShift = 0x01100023,
|
||||
Key_RightControl = 0x01100024,
|
||||
Key_LeftControl = 0x01100025,
|
||||
Key_RightAlt = 0x01100026,
|
||||
Key_LeftAlt = 0x01100027,
|
||||
};
|
||||
|
||||
static constexpr const KeyCodeName s_qt_key_names[] = {
|
||||
{Qt::Key_Escape, "Escape", ICON_PF_ESC},
|
||||
{Qt::Key_Tab, "Tab", ICON_PF_TAB},
|
||||
@@ -103,13 +92,6 @@ static constexpr const KeyCodeName s_qt_key_names[] = {
|
||||
{Qt::Key_Control, "Control", ICON_PF_CTRL},
|
||||
{Qt::Key_Meta, "Meta", ICON_PF_SUPER},
|
||||
{Qt::Key_Alt, "Alt", ICON_PF_ALT},
|
||||
// patch for left and right versions of the keys
|
||||
{ExtendedKeys::Key_LeftShift, "LShift", ICON_PF_SHIFT},
|
||||
{ExtendedKeys::Key_RightShift, "RShift", ICON_PF_SHIFT},
|
||||
{ExtendedKeys::Key_LeftControl, "LCtrl", ICON_PF_CTRL},
|
||||
{ExtendedKeys::Key_RightControl, "RCtrl", ICON_PF_CTRL},
|
||||
{ExtendedKeys::Key_LeftAlt, "LAlt", ICON_PF_ALT},
|
||||
{ExtendedKeys::Key_RightAlt, "RAlt", ICON_PF_ALT},
|
||||
{Qt::Key_CapsLock, "CapsLock", ICON_PF_CAPS},
|
||||
{Qt::Key_NumLock, "NumLock", ICON_PF_NUMLOCK},
|
||||
{Qt::Key_ScrollLock, "ScrollLock", ICON_PF_SCRLK},
|
||||
@@ -593,41 +575,6 @@ u32 QtUtils::KeyEventToCode(const QKeyEvent* ev)
|
||||
const u8 keycode = set_keycode ? map_text_to_keycode(text) : 0;
|
||||
int key = ev->key();
|
||||
|
||||
if (key == Qt::Key_Shift || key == Qt::Key_Alt || key == Qt::Key_Control)
|
||||
{
|
||||
#if defined(Q_OS_WIN) or defined(Q_OS_LINUX)
|
||||
// Scan codes (Tested it)
|
||||
// 0x2A : Left shift
|
||||
// 0x36 : Right shift
|
||||
// 0x1D : Left ctrl
|
||||
// 0xE01D : Right ctrl
|
||||
// 0x38 : Left alt
|
||||
// right alt can become ctrl + right alt in some keyboard layouts (windows)
|
||||
// but thats fine for our use case
|
||||
switch (ev->nativeScanCode())
|
||||
{
|
||||
case 0x2A:
|
||||
key = ExtendedKeys::Key_LeftShift;
|
||||
break;
|
||||
case 0x36:
|
||||
key = ExtendedKeys::Key_RightShift;
|
||||
break;
|
||||
case 0x1D:
|
||||
key = ExtendedKeys::Key_LeftControl;
|
||||
break;
|
||||
case 0xE01D:
|
||||
key = ExtendedKeys::Key_RightControl;
|
||||
break;
|
||||
case 0x38:
|
||||
key = ExtendedKeys::Key_LeftAlt;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
if (keycode != 0)
|
||||
key = keycode; // Override key if mapped
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>Options and Patches</string>
|
||||
<string>Fast Boot Options</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
|
||||
@@ -40,7 +40,6 @@ MemoryCardSettingsWidget::MemoryCardSettingsWidget(SettingsWindow* settings_dial
|
||||
|
||||
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.directory, m_ui.browse, m_ui.open, m_ui.reset, "Folders",
|
||||
"MemoryCards", Path::Combine(EmuFolders::DataRoot, "memcards"));
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.automaticManagement, "EmuCore", "McdFolderAutoManage", true);
|
||||
|
||||
setupAdditionalUi();
|
||||
|
||||
@@ -58,11 +57,6 @@ MemoryCardSettingsWidget::MemoryCardSettingsWidget(SettingsWindow* settings_dial
|
||||
connect(m_ui.deleteCard, &QPushButton::clicked, this, &MemoryCardSettingsWidget::deleteCard);
|
||||
|
||||
refresh();
|
||||
|
||||
dialog()->registerWidgetHelp(m_ui.automaticManagement, tr("Automatically manage saves based on running game"),
|
||||
tr("Checked"),
|
||||
tr("(Folder type only / Card size: Auto) Loads only the relevant booted game saves, ignoring others. Avoids "
|
||||
"running out of space for saves."));
|
||||
}
|
||||
|
||||
MemoryCardSettingsWidget::~MemoryCardSettingsWidget() = default;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <QtWidgets/QTreeWidget>
|
||||
#include <QtWidgets/QToolButton>
|
||||
#include <QtWidgets/QListWidget>
|
||||
#include <QtWidgets/QCheckBox>
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
|
||||
@@ -175,22 +175,6 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="settingsGroupBox">
|
||||
<property name="title">
|
||||
<string>Settings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="automaticManagement">
|
||||
<property name="text">
|
||||
<string>Automatically manage saves based on running game</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
@@ -211,7 +195,6 @@
|
||||
<tabstop>renameCard</tabstop>
|
||||
<tabstop>convertCard</tabstop>
|
||||
<tabstop>deleteCard</tabstop>
|
||||
<tabstop>automaticManagement</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
||||
@@ -140,27 +140,34 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
|
||||
return;
|
||||
}
|
||||
|
||||
// Locate home directory
|
||||
// Get path to Desktop or per-user Start Menu\Programs directory
|
||||
// https://superuser.com/questions/1489874/how-can-i-get-the-real-path-of-desktop-in-windows-explorer/1789849#1789849
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath
|
||||
// https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid
|
||||
std::string link_file;
|
||||
if (const char* home = std::getenv("USERPROFILE"))
|
||||
if (PWSTR directory; SUCCEEDED(SHGetKnownFolderPath(is_desktop ? FOLDERID_Desktop : FOLDERID_Programs, 0, NULL, &directory)))
|
||||
{
|
||||
std::string directory_utf8 = StringUtil::WideStringToUTF8String(directory);
|
||||
CoTaskMemFree(directory);
|
||||
|
||||
if (is_desktop)
|
||||
link_file = Path::ToNativePath(fmt::format("{}/Desktop/{}.lnk", home, clean_name));
|
||||
link_file = Path::ToNativePath(fmt::format("{}/{}.lnk", directory_utf8, clean_name));
|
||||
else
|
||||
{
|
||||
const std::string start_menu_dir = Path::ToNativePath(fmt::format("{}/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/PCSX2", home));
|
||||
if (!FileSystem::EnsureDirectoryExists(start_menu_dir.c_str(), false))
|
||||
const std::string pcsx2_start_menu_dir = Path::ToNativePath(fmt::format("{}/PCSX2", directory_utf8));
|
||||
if (!FileSystem::EnsureDirectoryExists(pcsx2_start_menu_dir.c_str(), false))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Failed to create shortcut"), tr("Could not create start menu directory."), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
link_file = Path::ToNativePath(fmt::format("{}/{}.lnk", start_menu_dir, clean_name));
|
||||
link_file = Path::ToNativePath(fmt::format("{}/{}.lnk", pcsx2_start_menu_dir, clean_name));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::critical(this, tr("Failed to create shortcut"), tr("Home path is empty."), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
|
||||
CoTaskMemFree(directory);
|
||||
QMessageBox::critical(this, tr("Failed to create shortcut"), is_desktop ? tr("'Desktop' directory not found") : tr("User's 'Start Menu\\Programs' directory not found"), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -295,6 +295,9 @@
|
||||
<property name="text">
|
||||
<string>Desktop</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -1308,6 +1310,12 @@ function(setup_main_executable target)
|
||||
install(FILES "${DEPS_BINDIR}/${SYMBOL_TO_COPY}" DESTINATION "${CMAKE_SOURCE_DIR}/bin" OPTIONAL)
|
||||
endforeach()
|
||||
|
||||
set(AGILITY_DIR "${CMAKE_SOURCE_DIR}/deps/bin/D3D12")
|
||||
set(AGILITY_DEPS_TO_COPY D3D12Core.dll d3d12SDKLayers.dll)
|
||||
foreach(AGILITY_DEP_TO_COPY ${AGILITY_DEPS_TO_COPY})
|
||||
install(FILES "${DEPS_BINDIR}/D3D12/${AGILITY_DEP_TO_COPY}" DESTINATION "${CMAKE_SOURCE_DIR}/bin/D3D12" OPTIONAL)
|
||||
endforeach()
|
||||
|
||||
get_target_property(WINDEPLOYQT_EXE Qt6::windeployqt IMPORTED_LOCATION)
|
||||
install(CODE "execute_process(COMMAND \"${WINDEPLOYQT_EXE}\" \"${CMAKE_SOURCE_DIR}/bin/$<TARGET_FILE_NAME:${target}>\" --plugindir \"${CMAKE_SOURCE_DIR}/bin/QtPlugins\" --pdb --no-compiler-runtime --no-system-d3d-compiler --no-system-dxc-compiler --no-translations COMMAND_ERROR_IS_FATAL ANY)")
|
||||
install(CODE "file(WRITE \"${CMAKE_SOURCE_DIR}/bin/qt.conf\" \"[Paths]\\nPlugins = ./QtPlugins\")")
|
||||
|
||||
@@ -1326,7 +1326,6 @@ struct Pcsx2Config
|
||||
UseSavestateSelector : 1,
|
||||
InhibitScreensaver : 1,
|
||||
BackupSavestate : 1,
|
||||
McdFolderAutoManage : 1,
|
||||
ManuallySetRealTimeClock : 1, // passes user-set real-time clock information to cdvd at startup
|
||||
UseSystemLocaleFormat : 1, // presents OS time format instead of yyyy-MM-dd HH:mm:ss for manual RTC
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1202,7 +1202,7 @@ void GSState::ApplyTEX0(GIFRegTEX0& TEX0)
|
||||
// Urban Chaos writes to the memory backing the CLUT in the middle of a shuffle, and
|
||||
// it's unclear whether the CLUT would actually get reloaded in that case.
|
||||
if (TEX0.CBP != m_mem.m_clut.GetCLUTCBP())
|
||||
m_channel_shuffle_abort = true;
|
||||
m_channel_shuffle_finish = true;
|
||||
}
|
||||
|
||||
TEX0.CPSM &= 0xa; // 1010b
|
||||
@@ -1899,6 +1899,30 @@ void GSState::FlushWrite()
|
||||
|
||||
r = m_tr.rect;
|
||||
|
||||
// If the end isn't where it said it would be, we need to calculate the end point.
|
||||
// Star Wars - The Clone Wars just sets the rect to 16x4095 then YOLO's about half a page, then kills the transfer.
|
||||
// If we just nuke the whole lot, even though nothing has been transferred, we risk killing data we don't mean to.
|
||||
if (m_tr.end < m_tr.total && GSIsHardwareRenderer())
|
||||
{
|
||||
const GSLocalMemory::psm_t& psm_s = GSLocalMemory::m_psm[m_tr.m_blit.DPSM];
|
||||
// Convert to nibbles then back to bytes after, in case trbpp is 4.
|
||||
const u32 in_data_pixel_count = (((len * 2) + ((psm_s.trbpp / 4) - 1)) / (psm_s.trbpp / 4));
|
||||
const u32 rect_pixel_count = r.width() * r.height();
|
||||
|
||||
if (rect_pixel_count > in_data_pixel_count)
|
||||
{
|
||||
const int calculated_height = ((in_data_pixel_count + (r.width() - 1)) / r.width());
|
||||
|
||||
// Just setting the height should be okay...
|
||||
r.w = std::max(r.y + calculated_height, psm_s.bs.y);
|
||||
|
||||
if (m_draw_transfers.size() > 0 && m_tr.m_blit.DBP == m_draw_transfers.back().blit.DBP)
|
||||
{
|
||||
m_draw_transfers.back().rect = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InvalidateVideoMem(m_env.BITBLTBUF, r);
|
||||
|
||||
const GSLocalMemory::writeImage wi = GSLocalMemory::m_psm[m_env.BITBLTBUF.DPSM].wi;
|
||||
@@ -3755,7 +3779,7 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
|
||||
}
|
||||
|
||||
PRIM_OVERLAP overlap = PRIM_OVERLAP_NO;
|
||||
bool check_quads = primclass == GS_TRIANGLE_CLASS;
|
||||
bool check_quads = (primclass == GS_TRIANGLE_CLASS);
|
||||
|
||||
u32 i = 0;
|
||||
u32 skip = 0; // Number of indices to skip if we have the bbox from the previous iteration.
|
||||
@@ -3766,11 +3790,85 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
|
||||
{
|
||||
u32 j = i + skip;
|
||||
|
||||
skip = 0;
|
||||
|
||||
GSVector4i bbox(INT_MAX, INT_MAX, -INT_MAX, -INT_MAX);
|
||||
|
||||
while (j < count)
|
||||
{
|
||||
if (check_quads && j + 3 < 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 } },
|
||||
|
||||
// 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]])
|
||||
{
|
||||
// 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());
|
||||
|
||||
while (true)
|
||||
{
|
||||
// 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;
|
||||
|
||||
// Cross product signs comparison.
|
||||
if ((unshared0.x * shared1.y - unshared0.y * shared1.x >= 0) ==
|
||||
(unshared1.x * shared1.y - unshared1.y * shared1.x >= 0))
|
||||
{
|
||||
break; // Triangles are on same side of the shared edge so there's overlap.
|
||||
}
|
||||
|
||||
// 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());
|
||||
|
||||
skip += 3;
|
||||
|
||||
got_bbox = true;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 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.
|
||||
}
|
||||
|
||||
// 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];
|
||||
@@ -3780,31 +3878,30 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
|
||||
if (AreTrianglesQuad<0, 0>(v, idx0, idx1, &tri0, &tri1))
|
||||
{
|
||||
// tri.b is right angle corner
|
||||
GSVector4i corner0 = GSVector4i(v[idx0[tri0.b]].m[1]).upl16().xyxy();
|
||||
GSVector4i corner1 = GSVector4i(v[idx1[tri1.b]].m[1]).upl16().xyxy();
|
||||
bbox = corner0.runion(corner1);
|
||||
bbox = GSVector4i(v[idx0[tri0.b]].m[1]).upl16().xyxy();
|
||||
bbox = bbox.runion(GSVector4i(v[idx1[tri1.b]].m[1]).upl16().xyxy());
|
||||
|
||||
skip = 6;
|
||||
|
||||
got_bbox = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
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());
|
||||
|
||||
skip = 3;
|
||||
|
||||
// If we fail a quad check assume the rest are not quads.
|
||||
// If we fail a quad check assume the rest are not quads since the check is relatively expensive.
|
||||
check_quads = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Avoid degenerate bbox.
|
||||
@@ -5496,6 +5593,11 @@ bool GSState::IsOpaque()
|
||||
return true;
|
||||
|
||||
const GSDrawingContext* context = m_context;
|
||||
const u32 fmsk = GSLocalMemory::m_psm[context->FRAME.PSM].fmsk;
|
||||
|
||||
// If we aren't drawing color, it's equivilant to opaque.
|
||||
if ((context->FRAME.FBMSK & fmsk) == (fmsk & 0x00FFFFFF))
|
||||
return true;
|
||||
|
||||
int amin = 0;
|
||||
int amax = 0xff;
|
||||
|
||||
@@ -258,7 +258,7 @@ public:
|
||||
bool m_using_temp_z = false;
|
||||
bool m_temp_z_full_copy = false;
|
||||
bool m_in_target_draw = false;
|
||||
bool m_channel_shuffle_abort = false;
|
||||
bool m_channel_shuffle_finish = false;
|
||||
|
||||
u32 m_target_offset = 0;
|
||||
u8 m_scanmask_used = 0;
|
||||
|
||||
@@ -244,7 +244,7 @@ const char* GSDevice::RenderAPIToString(RenderAPI api)
|
||||
|
||||
bool GSDevice::GetRequestedExclusiveFullscreenMode(u32* width, u32* height, float* refresh_rate)
|
||||
{
|
||||
const std::string mode = Host::GetBaseStringSettingValue("EmuCore/GS", "FullscreenMode", "");
|
||||
const std::string mode = Host::GetStringSettingValue("EmuCore/GS", "FullscreenMode", "");
|
||||
if (!mode.empty())
|
||||
{
|
||||
const std::string_view mode_view = mode;
|
||||
@@ -899,14 +899,13 @@ void GSDevice::ShadeBoost()
|
||||
if (ResizeRenderTarget(&m_target_tmp, m_current->GetWidth(), m_current->GetHeight(), false, false))
|
||||
{
|
||||
// predivide to avoid the divide (multiply) in the shader
|
||||
const float params[4] = {
|
||||
const GSVector4 params(
|
||||
static_cast<float>(GSConfig.ShadeBoost_Brightness) * (1.0f / 50.0f),
|
||||
static_cast<float>(GSConfig.ShadeBoost_Contrast) * (1.0f / 50.0f),
|
||||
static_cast<float>(GSConfig.ShadeBoost_Saturation) * (1.0f / 50.0f),
|
||||
static_cast<float>(GSConfig.ShadeBoost_Gamma) * (1.0f / 50.0f),
|
||||
};
|
||||
static_cast<float>(GSConfig.ShadeBoost_Gamma) * (1.0f / 50.0f));
|
||||
|
||||
DoShadeBoost(m_current, m_target_tmp, params);
|
||||
DoShadeBoost(m_current, m_target_tmp, params.v);
|
||||
|
||||
m_current = m_target_tmp;
|
||||
}
|
||||
|
||||
@@ -622,6 +622,7 @@ struct alignas(16) GSHWDrawConfig
|
||||
GSVector4 LODParams;
|
||||
GSVector4 STRange;
|
||||
GSVector4i ChannelShuffle;
|
||||
GSVector2 ChannelShuffleOffset;
|
||||
GSVector2 TCOffsetHack;
|
||||
GSVector2 STScale;
|
||||
|
||||
|
||||
@@ -426,7 +426,8 @@ GSRendererType D3D::GetPreferredRenderer()
|
||||
if (!feature_level.has_value())
|
||||
return GSRendererType::DX11;
|
||||
else if (feature_level == D3D_FEATURE_LEVEL_12_0)
|
||||
return check_vulkan_supported() ? GSRendererType::VK : GSRendererType::OGL;
|
||||
//return check_vulkan_supported() ? GSRendererType::VK : GSRendererType::OGL;
|
||||
return GSRendererType::DX12;
|
||||
else if (feature_level == D3D_FEATURE_LEVEL_11_0)
|
||||
return GSRendererType::OGL;
|
||||
else
|
||||
@@ -439,7 +440,8 @@ GSRendererType D3D::GetPreferredRenderer()
|
||||
if (!feature_level.has_value())
|
||||
return GSRendererType::DX11;
|
||||
else if (feature_level == D3D_FEATURE_LEVEL_12_0)
|
||||
return check_vulkan_supported() ? GSRendererType::VK : GSRendererType::DX12;
|
||||
//return check_vulkan_supported() ? GSRendererType::VK : GSRendererType::DX12;
|
||||
return GSRendererType::DX12;
|
||||
else if (feature_level == D3D_FEATURE_LEVEL_11_1)
|
||||
return GSRendererType::DX12;
|
||||
else
|
||||
|
||||
@@ -1237,7 +1237,17 @@ void GSDevice11::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r,
|
||||
return;
|
||||
}
|
||||
|
||||
const GSVector4i src_rect(0, 0, sTex->GetWidth(), sTex->GetHeight());
|
||||
const GSVector4i dst_rect(0, 0, dTex->GetWidth(), dTex->GetHeight());
|
||||
const bool src_dst_rect_match = src_rect.eq(dst_rect);
|
||||
|
||||
// Sizes must match for full depth copies when no partial copies are supported.
|
||||
if (sTex->IsDepthStencil() && !src_dst_rect_match)
|
||||
{
|
||||
GL_INS("D3D11: CopyRect rect mismatch for full depth copy.");
|
||||
return;
|
||||
}
|
||||
|
||||
const bool full_draw_copy = sTex->IsDepthStencil() || dst_rect.eq(r);
|
||||
|
||||
// Source is cleared, if destination is a render target, we can carry the clear forward.
|
||||
@@ -1259,8 +1269,7 @@ void GSDevice11::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r,
|
||||
// DX11 doesn't support partial depth copy so we need to
|
||||
// either pass a nullptr D3D11_BOX for a full depth copy or use CopyResource instead.
|
||||
// Optimization: Use CopyResource for depth copies or full rect color copies, it's faster than CopySubresourceRegion.
|
||||
const GSVector4i src_rect(0, 0, sTex->GetWidth(), sTex->GetHeight());
|
||||
const bool full_rt_copy = sTex->IsDepthStencil() || (destX == 0 && destY == 0 && r.eq(src_rect) && src_rect.eq(dst_rect));
|
||||
const bool full_rt_copy = src_dst_rect_match && (sTex->IsDepthStencil() || (destX == 0 && destY == 0 && r.eq(src_rect)));
|
||||
if (full_rt_copy)
|
||||
{
|
||||
m_ctx->CopyResource(*static_cast<GSTexture11*>(dTex), *static_cast<GSTexture11*>(sTex));
|
||||
@@ -1388,7 +1397,7 @@ void GSDevice11::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
|
||||
const float right = dRect.z * 2 / ds.x - 1.0f;
|
||||
const float bottom = 1.0f - dRect.w * 2 / ds.y;
|
||||
|
||||
GSVertexPT1 vertices[] =
|
||||
const GSVertexPT1 vertices[] =
|
||||
{
|
||||
{GSVector4(left, top, 0.5f, 1.0f), GSVector2(sRect.x, sRect.y)},
|
||||
{GSVector4(right, top, 0.5f, 1.0f), GSVector2(sRect.z, sRect.y)},
|
||||
@@ -1418,13 +1427,14 @@ void GSDevice11::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
|
||||
void GSDevice11::UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize)
|
||||
{
|
||||
// match merge cb
|
||||
struct Uniforms
|
||||
struct alignas(16) Uniforms
|
||||
{
|
||||
float scale;
|
||||
float pad1[3];
|
||||
u32 offsetX, offsetY, dOffset;
|
||||
u32 pad2;
|
||||
};
|
||||
const Uniforms cb = {sScale, {}, offsetX, offsetY, dOffset};
|
||||
const Uniforms cb = {sScale, {}, offsetX, offsetY, dOffset, 0};
|
||||
m_ctx->UpdateSubresource(m_merge.cb.get(), 0, nullptr, &cb, 0, 0);
|
||||
|
||||
const GSVector4 dRect(0, 0, dSize, 1);
|
||||
@@ -1435,14 +1445,15 @@ void GSDevice11::UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u
|
||||
void GSDevice11::ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM)
|
||||
{
|
||||
// match merge cb
|
||||
struct Uniforms
|
||||
struct alignas(16) Uniforms
|
||||
{
|
||||
float scale;
|
||||
float pad1[3];
|
||||
u32 SBW, DBW, SPSM;
|
||||
u32 pad2;
|
||||
};
|
||||
|
||||
const Uniforms cb = {sScale, {}, SBW, DBW, SPSM};
|
||||
const Uniforms cb = {sScale, {}, SBW, DBW, SPSM, 0};
|
||||
m_ctx->UpdateSubresource(m_merge.cb.get(), 0, nullptr, &cb, 0, 0);
|
||||
|
||||
const GSVector4 dRect(0, 0, dTex->GetWidth(), dTex->GetHeight());
|
||||
@@ -1452,7 +1463,7 @@ void GSDevice11::ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offs
|
||||
|
||||
void GSDevice11::FilteredDownsampleTexture(GSTexture* sTex, GSTexture* dTex, u32 downsample_factor, const GSVector2i& clamp_min, const GSVector4& dRect)
|
||||
{
|
||||
struct Uniforms
|
||||
struct alignas(16) Uniforms
|
||||
{
|
||||
float weight;
|
||||
float step_multiplier;
|
||||
@@ -2054,13 +2065,13 @@ void GSDevice11::RenderImGui()
|
||||
|
||||
UpdateImGuiTextures();
|
||||
|
||||
const float L = 0.0f;
|
||||
constexpr float L = 0.0f;
|
||||
const float R = static_cast<float>(m_window_info.surface_width);
|
||||
const float T = 0.0f;
|
||||
constexpr float T = 0.0f;
|
||||
const float B = static_cast<float>(m_window_info.surface_height);
|
||||
|
||||
// clang-format off
|
||||
const float ortho_projection[4][4] =
|
||||
const GSVector4 ortho_projection[4] =
|
||||
{
|
||||
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
|
||||
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
|
||||
|
||||
@@ -22,8 +22,8 @@ D3D12StreamBuffer::~D3D12StreamBuffer()
|
||||
|
||||
bool D3D12StreamBuffer::Create(u32 size)
|
||||
{
|
||||
const D3D12_RESOURCE_DESC resource_desc = {D3D12_RESOURCE_DIMENSION_BUFFER, 0, size, 1, 1, 1, DXGI_FORMAT_UNKNOWN,
|
||||
{1, 0}, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, D3D12_RESOURCE_FLAG_NONE};
|
||||
const GSDevice12::D3D12_RESOURCE_DESCU resource_desc = {{D3D12_RESOURCE_DIMENSION_BUFFER, 0, size, 1, 1, 1, DXGI_FORMAT_UNKNOWN,
|
||||
{1, 0}, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, D3D12_RESOURCE_FLAG_NONE}};
|
||||
|
||||
D3D12MA::ALLOCATION_DESC allocationDesc = {};
|
||||
allocationDesc.Flags = D3D12MA::ALLOCATION_FLAG_COMMITTED;
|
||||
@@ -31,8 +31,13 @@ bool D3D12StreamBuffer::Create(u32 size)
|
||||
|
||||
wil::com_ptr_nothrow<ID3D12Resource> buffer;
|
||||
wil::com_ptr_nothrow<D3D12MA::Allocation> allocation;
|
||||
HRESULT hr = GSDevice12::GetInstance()->GetAllocator()->CreateResource(&allocationDesc, &resource_desc,
|
||||
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, allocation.put(), IID_PPV_ARGS(buffer.put()));
|
||||
HRESULT hr;
|
||||
if (GSDevice12::GetInstance()->UseEnhancedBarriers())
|
||||
hr = GSDevice12::GetInstance()->GetAllocator()->CreateResource3(&allocationDesc, &resource_desc.desc1,
|
||||
D3D12_BARRIER_LAYOUT_UNDEFINED, nullptr, 0, nullptr, allocation.put(), IID_PPV_ARGS(buffer.put()));
|
||||
else
|
||||
hr = GSDevice12::GetInstance()->GetAllocator()->CreateResource(&allocationDesc, &resource_desc.desc,
|
||||
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, allocation.put(), IID_PPV_ARGS(buffer.put()));
|
||||
pxAssertMsg(SUCCEEDED(hr), "Allocate buffer");
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,14 @@ namespace D3D12MA
|
||||
class Allocator;
|
||||
}
|
||||
|
||||
struct D3D12CommandList
|
||||
{
|
||||
// Main command list
|
||||
wil::com_ptr_nothrow<ID3D12GraphicsCommandList4> list4;
|
||||
// Enhanced barriers command list
|
||||
wil::com_ptr_nothrow<ID3D12GraphicsCommandList7> list7;
|
||||
};
|
||||
|
||||
class GSDevice12 final : public GSDevice
|
||||
{
|
||||
public:
|
||||
@@ -42,6 +50,12 @@ public:
|
||||
NUM_TIMESTAMP_QUERIES_PER_CMDLIST = 2,
|
||||
};
|
||||
|
||||
union D3D12_RESOURCE_DESCU
|
||||
{
|
||||
D3D12_RESOURCE_DESC1 desc1;
|
||||
D3D12_RESOURCE_DESC desc;
|
||||
};
|
||||
|
||||
__fi IDXGIAdapter1* GetAdapter() const { return m_adapter.get(); }
|
||||
__fi ID3D12Device* GetDevice() const { return m_device.get(); }
|
||||
__fi ID3D12CommandQueue* GetCommandQueue() const { return m_command_queue.get(); }
|
||||
@@ -50,14 +64,16 @@ public:
|
||||
/// Returns the PCI vendor ID of the device, if known.
|
||||
u32 GetAdapterVendorID() const;
|
||||
|
||||
bool UseEnhancedBarriers() const { return m_enhanced_barriers; }
|
||||
|
||||
/// Returns the current command list, commands can be recorded directly.
|
||||
ID3D12GraphicsCommandList4* GetCommandList() const
|
||||
const D3D12CommandList& GetCommandList() const
|
||||
{
|
||||
return m_command_lists[m_current_command_list].command_lists[1].get();
|
||||
return m_command_lists[m_current_command_list].command_lists[1];
|
||||
}
|
||||
|
||||
/// Returns the init command list for uploading.
|
||||
ID3D12GraphicsCommandList4* GetInitCommandList();
|
||||
const D3D12CommandList& GetInitCommandList();
|
||||
|
||||
/// Returns the per-frame SRV/CBV/UAV allocator.
|
||||
D3D12DescriptorAllocator& GetDescriptorAllocator()
|
||||
@@ -99,6 +115,9 @@ public:
|
||||
/// Test for support for the specified texture format.
|
||||
bool SupportsTextureFormat(DXGI_FORMAT format);
|
||||
|
||||
// Partial depth copies require ProgrammableSamplePositions tier 1.
|
||||
bool SupportsProgrammableSamplePositions();
|
||||
|
||||
enum class WaitType
|
||||
{
|
||||
None, ///< Don't wait (async)
|
||||
@@ -134,7 +153,7 @@ private:
|
||||
struct CommandListResources
|
||||
{
|
||||
std::array<ComPtr<ID3D12CommandAllocator>, 2> command_allocators;
|
||||
std::array<ComPtr<ID3D12GraphicsCommandList4>, 2> command_lists;
|
||||
std::array<D3D12CommandList, 2> command_lists;
|
||||
D3D12DescriptorAllocator descriptor_allocator;
|
||||
D3D12GroupedSamplerAllocator<SAMPLER_GROUP_SIZE> sampler_allocator;
|
||||
std::vector<std::pair<D3D12MA::Allocation*, ID3D12DeviceChild*>> pending_resources;
|
||||
@@ -144,6 +163,8 @@ private:
|
||||
bool has_timestamp_query = false;
|
||||
};
|
||||
|
||||
void LoadAgilitySDK();
|
||||
|
||||
bool CreateDevice(u32& vendor_id);
|
||||
bool CreateDescriptorHeaps();
|
||||
bool CreateCommandLists();
|
||||
@@ -170,6 +191,7 @@ private:
|
||||
double m_timestamp_frequency = 0.0;
|
||||
float m_accumulated_gpu_time = 0.0f;
|
||||
bool m_gpu_timing_enabled = false;
|
||||
bool m_programmable_sample_positions = false;
|
||||
|
||||
D3D12DescriptorHeapManager m_descriptor_heap_manager;
|
||||
D3D12DescriptorHeapManager m_rtv_heap_manager;
|
||||
@@ -292,6 +314,7 @@ 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_device_lost = false;
|
||||
|
||||
ComPtr<ID3D12RootSignature> m_tfx_root_signature;
|
||||
@@ -373,6 +396,8 @@ private:
|
||||
ComPtr<ID3DBlob> GetUtilityVertexShader(const std::string& source, const char* entry_point);
|
||||
ComPtr<ID3DBlob> GetUtilityPixelShader(const std::string& source, const char* entry_point);
|
||||
|
||||
void FeedbackBarrier(const GSTexture12* texture);
|
||||
|
||||
bool CheckFeatures(const u32& vendor_id);
|
||||
bool CreateNullTexture();
|
||||
bool CreateBuffers();
|
||||
|
||||
@@ -19,7 +19,7 @@ GSTexture12::GSTexture12(Type type, Format format, int width, int height, int le
|
||||
wil::com_ptr_nothrow<D3D12MA::Allocation> allocation, const D3D12DescriptorHandle& srv_descriptor,
|
||||
const D3D12DescriptorHandle& write_descriptor, const D3D12DescriptorHandle& ro_dsv_descriptor,
|
||||
const D3D12DescriptorHandle& uav_descriptor, const D3D12DescriptorHandle& fbl_descriptor,
|
||||
WriteDescriptorType wdtype, D3D12_RESOURCE_STATES resource_state)
|
||||
WriteDescriptorType wdtype, bool simultaneous_texture, ResourceState resource_state)
|
||||
: m_resource(std::move(resource))
|
||||
, m_resource_fbl(std::move(resource_fbl))
|
||||
, m_allocation(std::move(allocation))
|
||||
@@ -31,6 +31,7 @@ GSTexture12::GSTexture12(Type type, Format format, int width, int height, int le
|
||||
, m_write_descriptor_type(wdtype)
|
||||
, m_dxgi_format(dxgi_format)
|
||||
, m_resource_state(resource_state)
|
||||
, m_simultaneous_tex(simultaneous_texture)
|
||||
{
|
||||
m_type = type;
|
||||
m_format = format;
|
||||
@@ -111,28 +112,87 @@ void GSTexture12::Destroy(bool defer)
|
||||
m_write_descriptor_type = WriteDescriptorType::None;
|
||||
}
|
||||
|
||||
// For use with non-simultaneous textures only.
|
||||
// Simultaneous testures are always D3D12_BARRIER_LAYOUT_COMMON.
|
||||
static D3D12_BARRIER_LAYOUT GetD3D12BarrierLayout(GSTexture12::ResourceState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case GSTexture12::ResourceState::Undefined:
|
||||
return D3D12_BARRIER_LAYOUT_UNDEFINED;
|
||||
case GSTexture12::ResourceState::Present:
|
||||
return D3D12_BARRIER_LAYOUT_COMMON;
|
||||
case GSTexture12::ResourceState::RenderTarget:
|
||||
return D3D12_BARRIER_LAYOUT_RENDER_TARGET;
|
||||
case GSTexture12::ResourceState::DepthWriteStencil:
|
||||
return D3D12_BARRIER_LAYOUT_DEPTH_STENCIL_WRITE;
|
||||
case GSTexture12::ResourceState::PixelShaderResource:
|
||||
case GSTexture12::ResourceState::ComputeShaderResource:
|
||||
return D3D12_BARRIER_LAYOUT_SHADER_RESOURCE;
|
||||
case GSTexture12::ResourceState::CopySrc:
|
||||
return D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_SOURCE;
|
||||
case GSTexture12::ResourceState::CopyDst:
|
||||
return D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_DEST;
|
||||
case GSTexture12::ResourceState::CASShaderUAV:
|
||||
case GSTexture12::ResourceState::PixelShaderUAV:
|
||||
return D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_UNORDERED_ACCESS;
|
||||
default:
|
||||
pxAssert(false);
|
||||
return D3D12_BARRIER_LAYOUT_UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
static D3D12_RESOURCE_STATES GetD3D12ResourceState(GSTexture12::ResourceState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case GSTexture12::ResourceState::Undefined:
|
||||
return D3D12_RESOURCE_STATE_COMMON;
|
||||
case GSTexture12::ResourceState::Present:
|
||||
return D3D12_RESOURCE_STATE_COMMON;
|
||||
case GSTexture12::ResourceState::RenderTarget:
|
||||
return D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
case GSTexture12::ResourceState::DepthWriteStencil:
|
||||
return D3D12_RESOURCE_STATE_DEPTH_WRITE;
|
||||
case GSTexture12::ResourceState::PixelShaderResource:
|
||||
return D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||||
case GSTexture12::ResourceState::ComputeShaderResource:
|
||||
return D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
|
||||
case GSTexture12::ResourceState::CopySrc:
|
||||
return D3D12_RESOURCE_STATE_COPY_SOURCE;
|
||||
case GSTexture12::ResourceState::CopyDst:
|
||||
return D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
case GSTexture12::ResourceState::CASShaderUAV:
|
||||
case GSTexture12::ResourceState::PixelShaderUAV:
|
||||
return D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
|
||||
default:
|
||||
pxAssert(false);
|
||||
return D3D12_RESOURCE_STATE_COMMON;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int width, int height, int levels,
|
||||
DXGI_FORMAT dxgi_format, DXGI_FORMAT srv_format, DXGI_FORMAT rtv_format, DXGI_FORMAT dsv_format,
|
||||
DXGI_FORMAT uav_format)
|
||||
{
|
||||
GSDevice12* const dev = GSDevice12::GetInstance();
|
||||
|
||||
D3D12_RESOURCE_DESC desc = {};
|
||||
desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
||||
desc.Width = width;
|
||||
desc.Height = height;
|
||||
desc.DepthOrArraySize = 1;
|
||||
desc.MipLevels = levels;
|
||||
desc.Format = dxgi_format;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
||||
GSDevice12::D3D12_RESOURCE_DESCU desc = {};
|
||||
desc.desc1.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
||||
desc.desc1.Width = width;
|
||||
desc.desc1.Height = height;
|
||||
desc.desc1.DepthOrArraySize = 1;
|
||||
desc.desc1.MipLevels = levels;
|
||||
desc.desc1.Format = dxgi_format;
|
||||
desc.desc1.SampleDesc.Count = 1;
|
||||
desc.desc1.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
||||
|
||||
D3D12MA::ALLOCATION_DESC allocationDesc = {};
|
||||
allocationDesc.Flags = D3D12MA::ALLOCATION_FLAG_WITHIN_BUDGET;
|
||||
allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
|
||||
|
||||
D3D12_CLEAR_VALUE optimized_clear_value = {};
|
||||
D3D12_RESOURCE_STATES state;
|
||||
ResourceState state;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
@@ -140,9 +200,9 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
{
|
||||
// This is a little annoying. basically, to do mipmap generation, we need to be a render target.
|
||||
// If it's a compressed texture, we won't be generating mips anyway, so this should be fine.
|
||||
desc.Flags = (levels > 1 && !IsCompressedFormat(format)) ? D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET :
|
||||
D3D12_RESOURCE_FLAG_NONE;
|
||||
state = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
desc.desc1.Flags = (levels > 1 && !IsCompressedFormat(format)) ? D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET :
|
||||
D3D12_RESOURCE_FLAG_NONE;
|
||||
state = ResourceState::CopyDst;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -152,10 +212,11 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
pxAssert(levels == 1);
|
||||
allocationDesc.Flags |= D3D12MA::ALLOCATION_FLAG_COMMITTED;
|
||||
allocationDesc.ExtraHeapFlags = D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES;
|
||||
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS;
|
||||
desc.Layout = D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE;
|
||||
desc.desc1.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS;
|
||||
if (!dev->UseEnhancedBarriers())
|
||||
desc.desc1.Layout = D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE;
|
||||
optimized_clear_value.Format = rtv_format;
|
||||
state = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
state = ResourceState::RenderTarget;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -163,9 +224,9 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
{
|
||||
pxAssert(levels == 1);
|
||||
allocationDesc.Flags |= D3D12MA::ALLOCATION_FLAG_COMMITTED;
|
||||
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
|
||||
desc.desc1.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
|
||||
optimized_clear_value.Format = dsv_format;
|
||||
state = D3D12_RESOURCE_STATE_DEPTH_WRITE;
|
||||
state = ResourceState::DepthWriteStencil;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -173,7 +234,7 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
{
|
||||
pxAssert(levels == 1);
|
||||
allocationDesc.Flags |= D3D12MA::ALLOCATION_FLAG_COMMITTED;
|
||||
state = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||||
state = ResourceState::PixelShaderResource;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -182,15 +243,16 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
}
|
||||
|
||||
if (uav_format != DXGI_FORMAT_UNKNOWN)
|
||||
desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
||||
desc.desc1.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
||||
|
||||
wil::com_ptr_nothrow<ID3D12Resource> resource;
|
||||
wil::com_ptr_nothrow<ID3D12Resource> resource_fbl;
|
||||
wil::com_ptr_nothrow<D3D12MA::Allocation> allocation;
|
||||
|
||||
if (type == Type::RenderTarget)
|
||||
if (type == Type::RenderTarget && !dev->UseEnhancedBarriers())
|
||||
{
|
||||
const D3D12_RESOURCE_ALLOCATION_INFO allocInfo = dev->GetDevice()->GetResourceAllocationInfo(0, 1, &desc);
|
||||
// We need to use an aliased resource for feedback with legacy barriers.
|
||||
const D3D12_RESOURCE_ALLOCATION_INFO allocInfo = dev->GetDevice()->GetResourceAllocationInfo(0, 1, &desc.desc);
|
||||
|
||||
HRESULT hr = dev->GetAllocator()->AllocateMemory(&allocationDesc, &allocInfo, allocation.put());
|
||||
if (FAILED(hr))
|
||||
@@ -202,7 +264,7 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
return {};
|
||||
}
|
||||
|
||||
hr = dev->GetAllocator()->CreateAliasingResource(allocation.get(), 0, &desc, state,
|
||||
hr = dev->GetAllocator()->CreateAliasingResource(allocation.get(), 0, &desc.desc, GetD3D12ResourceState(state),
|
||||
(type == Type::RenderTarget || type == Type::DepthStencil) ? &optimized_clear_value : nullptr,
|
||||
IID_PPV_ARGS(resource.put()));
|
||||
if (FAILED(hr))
|
||||
@@ -214,7 +276,7 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
return {};
|
||||
}
|
||||
|
||||
hr = dev->GetAllocator()->CreateAliasingResource(allocation.get(), 0, &desc, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
|
||||
hr = dev->GetAllocator()->CreateAliasingResource(allocation.get(), 0, &desc.desc, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
|
||||
(type == Type::RenderTarget || type == Type::DepthStencil) ? &optimized_clear_value : nullptr,
|
||||
IID_PPV_ARGS(resource_fbl.put()));
|
||||
if (FAILED(hr))
|
||||
@@ -228,9 +290,20 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
}
|
||||
else
|
||||
{
|
||||
HRESULT hr = dev->GetAllocator()->CreateResource(&allocationDesc, &desc, state,
|
||||
(type == Type::RenderTarget || type == Type::DepthStencil) ? &optimized_clear_value : nullptr, allocation.put(),
|
||||
IID_PPV_ARGS(resource.put()));
|
||||
HRESULT hr;
|
||||
if (dev->UseEnhancedBarriers())
|
||||
{
|
||||
hr = dev->GetAllocator()->CreateResource3(&allocationDesc, &desc.desc1,
|
||||
type == Type::RenderTarget ? D3D12_BARRIER_LAYOUT_COMMON : GetD3D12BarrierLayout(state),
|
||||
(type == Type::RenderTarget || type == Type::DepthStencil) ? &optimized_clear_value : nullptr,
|
||||
0, nullptr, allocation.put(), IID_PPV_ARGS(resource.put()));
|
||||
}
|
||||
else
|
||||
{
|
||||
hr = dev->GetAllocator()->CreateResource(&allocationDesc, &desc.desc, GetD3D12ResourceState(state),
|
||||
(type == Type::RenderTarget || type == Type::DepthStencil) ? &optimized_clear_value : nullptr, allocation.put(),
|
||||
IID_PPV_ARGS(resource.put()));
|
||||
}
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// OOM isn't fatal.
|
||||
@@ -301,8 +374,10 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
return {};
|
||||
}
|
||||
|
||||
// Feedback descriptor used with legacy barriers
|
||||
if (resource_fbl)
|
||||
{
|
||||
pxAssert(!dev->UseEnhancedBarriers());
|
||||
if (!CreateSRVDescriptor(resource_fbl.get(), levels, srv_format, &fbl_descriptor))
|
||||
{
|
||||
dev->GetDescriptorHeapManager().Free(&uav_descriptor);
|
||||
@@ -325,12 +400,12 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
|
||||
return std::unique_ptr<GSTexture12>(
|
||||
new GSTexture12(type, format, width, height, levels, dxgi_format, std::move(resource), std::move(resource_fbl), std::move(allocation),
|
||||
srv_descriptor, write_descriptor, ro_dsv_descriptor, uav_descriptor, fbl_descriptor, write_descriptor_type, state));
|
||||
srv_descriptor, write_descriptor, ro_dsv_descriptor, uav_descriptor, fbl_descriptor, write_descriptor_type, type == Type::RenderTarget, state));
|
||||
}
|
||||
|
||||
std::unique_ptr<GSTexture12> GSTexture12::Adopt(wil::com_ptr_nothrow<ID3D12Resource> resource, Type type, Format format,
|
||||
int width, int height, int levels, DXGI_FORMAT dxgi_format, DXGI_FORMAT srv_format, DXGI_FORMAT rtv_format,
|
||||
DXGI_FORMAT dsv_format, DXGI_FORMAT uav_format, D3D12_RESOURCE_STATES resource_state)
|
||||
DXGI_FORMAT dsv_format, DXGI_FORMAT uav_format, ResourceState resource_state)
|
||||
{
|
||||
const D3D12_RESOURCE_DESC desc = resource->GetDesc();
|
||||
|
||||
@@ -391,7 +466,7 @@ std::unique_ptr<GSTexture12> GSTexture12::Adopt(wil::com_ptr_nothrow<ID3D12Resou
|
||||
|
||||
return std::unique_ptr<GSTexture12>(new GSTexture12(type, format, static_cast<u32>(desc.Width), desc.Height,
|
||||
desc.MipLevels, desc.Format, std::move(resource), {}, {}, srv_descriptor, write_descriptor, {}, uav_descriptor,
|
||||
{}, write_descriptor_type, resource_state));
|
||||
{}, write_descriptor_type, false, resource_state));
|
||||
}
|
||||
|
||||
bool GSTexture12::CreateSRVDescriptor(
|
||||
@@ -432,7 +507,7 @@ bool GSTexture12::CreateDSVDescriptor(ID3D12Resource* resource, DXGI_FORMAT form
|
||||
return false;
|
||||
}
|
||||
|
||||
const D3D12_DEPTH_STENCIL_VIEW_DESC desc = {format, D3D12_DSV_DIMENSION_TEXTURE2D, read_only ? D3D12_DSV_FLAG_READ_ONLY_DEPTH : D3D12_DSV_FLAG_NONE };
|
||||
const D3D12_DEPTH_STENCIL_VIEW_DESC desc = {format, D3D12_DSV_DIMENSION_TEXTURE2D, read_only ? D3D12_DSV_FLAG_READ_ONLY_DEPTH : D3D12_DSV_FLAG_NONE};
|
||||
GSDevice12::GetInstance()->GetDevice()->CreateDepthStencilView(resource, &desc, dh->cpu_handle);
|
||||
return true;
|
||||
}
|
||||
@@ -455,7 +530,7 @@ void* GSTexture12::GetNativeHandle() const
|
||||
return const_cast<GSTexture12*>(this);
|
||||
}
|
||||
|
||||
ID3D12GraphicsCommandList* GSTexture12::GetCommandBufferForUpdate()
|
||||
const D3D12CommandList& GSTexture12::GetCommandBufferForUpdate()
|
||||
{
|
||||
GSDevice12* const dev = GSDevice12::GetInstance();
|
||||
if (m_type != Type::Texture || m_use_fence_counter == dev->GetCurrentFenceValue())
|
||||
@@ -476,10 +551,20 @@ ID3D12Resource* GSTexture12::AllocateUploadStagingBuffer(
|
||||
wil::com_ptr_nothrow<D3D12MA::Allocation> allocation;
|
||||
|
||||
const D3D12MA::ALLOCATION_DESC allocation_desc = {D3D12MA::ALLOCATION_FLAG_NONE, D3D12_HEAP_TYPE_UPLOAD};
|
||||
const D3D12_RESOURCE_DESC resource_desc = {D3D12_RESOURCE_DIMENSION_BUFFER, 0, buffer_size, 1, 1, 1,
|
||||
DXGI_FORMAT_UNKNOWN, {1, 0}, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, D3D12_RESOURCE_FLAG_NONE};
|
||||
HRESULT hr = GSDevice12::GetInstance()->GetAllocator()->CreateResource(&allocation_desc, &resource_desc,
|
||||
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, allocation.put(), IID_PPV_ARGS(resource.put()));
|
||||
|
||||
const GSDevice12::D3D12_RESOURCE_DESCU resource_desc = {{D3D12_RESOURCE_DIMENSION_BUFFER, 0, buffer_size, 1, 1, 1,
|
||||
DXGI_FORMAT_UNKNOWN, {1, 0}, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, D3D12_RESOURCE_FLAG_NONE}};
|
||||
HRESULT hr;
|
||||
if (GSDevice12::GetInstance()->UseEnhancedBarriers())
|
||||
{
|
||||
hr = GSDevice12::GetInstance()->GetAllocator()->CreateResource3(&allocation_desc, &resource_desc.desc1,
|
||||
D3D12_BARRIER_LAYOUT_UNDEFINED, nullptr, 0, nullptr, allocation.put(), IID_PPV_ARGS(resource.put()));
|
||||
}
|
||||
else
|
||||
{
|
||||
hr = GSDevice12::GetInstance()->GetAllocator()->CreateResource(&allocation_desc, &resource_desc.desc,
|
||||
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, allocation.put(), IID_PPV_ARGS(resource.put()));
|
||||
}
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.WriteLn("(AllocateUploadStagingBuffer) CreateCommittedResource() failed with %08X", hr);
|
||||
@@ -564,14 +649,14 @@ bool GSTexture12::Update(const GSVector4i& r, const void* data, int pitch, int l
|
||||
sbuffer.CommitMemory(required_size);
|
||||
}
|
||||
|
||||
ID3D12GraphicsCommandList* cmdlist = GetCommandBufferForUpdate();
|
||||
const D3D12CommandList& cmdlist = GetCommandBufferForUpdate();
|
||||
GL_PUSH("GSTexture12::Update({%d,%d} %dx%d Lvl:%u", r.x, r.y, r.width(), r.height(), layer);
|
||||
|
||||
// first time the texture is used? don't leave it undefined
|
||||
if (m_resource_state == D3D12_RESOURCE_STATE_COMMON)
|
||||
TransitionToState(cmdlist, D3D12_RESOURCE_STATE_COPY_DEST);
|
||||
else if (m_resource_state != D3D12_RESOURCE_STATE_COPY_DEST)
|
||||
TransitionSubresourceToState(cmdlist, layer, m_resource_state, D3D12_RESOURCE_STATE_COPY_DEST);
|
||||
if (m_resource_state == GSTexture12::ResourceState::Undefined)
|
||||
TransitionToState(cmdlist, GSTexture12::ResourceState::CopyDst);
|
||||
else if (m_resource_state != GSTexture12::ResourceState::CopyDst)
|
||||
TransitionSubresourceToState(cmdlist, layer, m_resource_state, GSTexture12::ResourceState::CopyDst);
|
||||
|
||||
// if we're an rt and have been cleared, and the full rect isn't being uploaded, do the clear
|
||||
if (m_type == Type::RenderTarget)
|
||||
@@ -588,11 +673,11 @@ bool GSTexture12::Update(const GSVector4i& r, const void* data, int pitch, int l
|
||||
dstloc.SubresourceIndex = layer;
|
||||
|
||||
const D3D12_BOX srcbox{0u, 0u, 0u, width, height, 1u};
|
||||
cmdlist->CopyTextureRegion(&dstloc, Common::AlignDownPow2((u32)r.x, block_size),
|
||||
cmdlist.list4->CopyTextureRegion(&dstloc, Common::AlignDownPow2((u32)r.x, block_size),
|
||||
Common::AlignDownPow2((u32)r.y, block_size), 0, &srcloc, &srcbox);
|
||||
|
||||
if (m_resource_state != D3D12_RESOURCE_STATE_COPY_DEST)
|
||||
TransitionSubresourceToState(cmdlist, layer, D3D12_RESOURCE_STATE_COPY_DEST, m_resource_state);
|
||||
if (m_resource_state != GSTexture12::ResourceState::CopyDst)
|
||||
TransitionSubresourceToState(cmdlist, layer, GSTexture12::ResourceState::CopyDst, m_resource_state);
|
||||
|
||||
if (m_type == Type::Texture)
|
||||
m_needs_mipmaps_generated |= (layer == 0);
|
||||
@@ -642,15 +727,15 @@ void GSTexture12::Unmap()
|
||||
const u32 buffer_offset = buffer.GetCurrentOffset();
|
||||
buffer.CommitMemory(required_size);
|
||||
|
||||
ID3D12GraphicsCommandList* cmdlist = GetCommandBufferForUpdate();
|
||||
const D3D12CommandList& cmdlist = GetCommandBufferForUpdate();
|
||||
GL_PUSH("GSTexture12::Update({%d,%d} %dx%d Lvl:%u", m_map_area.x, m_map_area.y, m_map_area.width(),
|
||||
m_map_area.height(), m_map_level);
|
||||
|
||||
// first time the texture is used? don't leave it undefined
|
||||
if (m_resource_state == D3D12_RESOURCE_STATE_COMMON)
|
||||
TransitionToState(cmdlist, D3D12_RESOURCE_STATE_COPY_DEST);
|
||||
else if (m_resource_state != D3D12_RESOURCE_STATE_COPY_DEST)
|
||||
TransitionSubresourceToState(cmdlist, m_map_level, m_resource_state, D3D12_RESOURCE_STATE_COPY_DEST);
|
||||
if (m_resource_state == ResourceState::Undefined)
|
||||
TransitionToState(cmdlist, ResourceState::CopyDst);
|
||||
else if (m_resource_state != ResourceState::CopyDst)
|
||||
TransitionSubresourceToState(cmdlist, m_map_level, m_resource_state, ResourceState::CopyDst);
|
||||
|
||||
// if we're an rt and have been cleared, and the full rect isn't being uploaded, do the clear
|
||||
if (m_type == Type::RenderTarget)
|
||||
@@ -677,10 +762,10 @@ void GSTexture12::Unmap()
|
||||
dstloc.SubresourceIndex = m_map_level;
|
||||
|
||||
const D3D12_BOX srcbox{0u, 0u, 0u, width, height, 1};
|
||||
cmdlist->CopyTextureRegion(&dstloc, m_map_area.x, m_map_area.y, 0, &srcloc, &srcbox);
|
||||
cmdlist.list4->CopyTextureRegion(&dstloc, m_map_area.x, m_map_area.y, 0, &srcloc, &srcbox);
|
||||
|
||||
if (m_resource_state != D3D12_RESOURCE_STATE_COPY_DEST)
|
||||
TransitionSubresourceToState(cmdlist, m_map_level, D3D12_RESOURCE_STATE_COPY_DEST, m_resource_state);
|
||||
if (m_resource_state != ResourceState::CopyDst)
|
||||
TransitionSubresourceToState(cmdlist, m_map_level, ResourceState::CopyDst, m_resource_state);
|
||||
|
||||
if (m_type == Type::Texture)
|
||||
m_needs_mipmaps_generated |= (m_map_level == 0);
|
||||
@@ -717,55 +802,343 @@ void GSTexture12::SetDebugName(std::string_view name)
|
||||
|
||||
#endif
|
||||
|
||||
void GSTexture12::TransitionToState(D3D12_RESOURCE_STATES state)
|
||||
void GSTexture12::TransitionToState(ResourceState state)
|
||||
{
|
||||
TransitionToState(GSDevice12::GetInstance()->GetCommandList(), state);
|
||||
}
|
||||
|
||||
void GSTexture12::TransitionToState(ID3D12GraphicsCommandList* cmdlist, D3D12_RESOURCE_STATES state)
|
||||
void GSTexture12::TransitionToState(const D3D12CommandList& cmdlist, ResourceState state)
|
||||
{
|
||||
if (m_resource_state == state)
|
||||
return;
|
||||
|
||||
// Read only depth requires special handling as we might want to write stencil.
|
||||
// Also batch the transition barriers as per recommendation from docs.
|
||||
if (state == (D3D12_RESOURCE_STATE_DEPTH_READ | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE))
|
||||
{
|
||||
// Transition to read depth/write stencil
|
||||
const D3D12_RESOURCE_BARRIER barriers[2] = {
|
||||
{D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_BARRIER_FLAG_NONE,
|
||||
{{m_resource.get(), 0, m_resource_state, (D3D12_RESOURCE_STATE_DEPTH_READ | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)}}},
|
||||
{D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_BARRIER_FLAG_NONE,
|
||||
{{m_resource.get(), 1, m_resource_state, D3D12_RESOURCE_STATE_DEPTH_WRITE}}},
|
||||
};
|
||||
cmdlist->ResourceBarrier(m_resource_state == D3D12_RESOURCE_STATE_DEPTH_WRITE ? 1 : 2, barriers);
|
||||
}
|
||||
else if (m_resource_state == (D3D12_RESOURCE_STATE_DEPTH_READ | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE))
|
||||
{
|
||||
// Transition from read depth/write stencil
|
||||
const D3D12_RESOURCE_BARRIER barriers[2] = {
|
||||
{D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_BARRIER_FLAG_NONE,
|
||||
{{m_resource.get(), 0, (D3D12_RESOURCE_STATE_DEPTH_READ | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE), state}}},
|
||||
{D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_BARRIER_FLAG_NONE,
|
||||
{{m_resource.get(), 1, D3D12_RESOURCE_STATE_DEPTH_WRITE, state}}},
|
||||
};
|
||||
cmdlist->ResourceBarrier(state == D3D12_RESOURCE_STATE_DEPTH_WRITE ? 1 : 2, barriers);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal transition
|
||||
TransitionSubresourceToState(cmdlist, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, m_resource_state, state);
|
||||
}
|
||||
TransitionSubresourceToState(cmdlist, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, m_resource_state, state);
|
||||
|
||||
m_resource_state = state;
|
||||
}
|
||||
|
||||
void GSTexture12::TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, int level,
|
||||
D3D12_RESOURCE_STATES before_state, D3D12_RESOURCE_STATES after_state) const
|
||||
void GSTexture12::TransitionSubresourceToState(const D3D12CommandList& cmdlist, int level,
|
||||
ResourceState before_state, ResourceState after_state) const
|
||||
{
|
||||
const D3D12_RESOURCE_BARRIER barrier = {D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_BARRIER_FLAG_NONE,
|
||||
{{m_resource.get(), static_cast<u32>(level), before_state, after_state}}};
|
||||
cmdlist->ResourceBarrier(1, &barrier);
|
||||
if (GSDevice12::GetInstance()->UseEnhancedBarriers())
|
||||
{
|
||||
// Read only depth requires special handling as we might want to write stencil.
|
||||
// We need to transition subresources separately, requiring 2 barriers
|
||||
// Handling it here allows us to batch those barriers.
|
||||
// Other transitions only need the one barrier.
|
||||
D3D12_TEXTURE_BARRIER barriers[2] = {{D3D12_BARRIER_SYNC_NONE, D3D12_BARRIER_SYNC_NONE,
|
||||
D3D12_BARRIER_ACCESS_COMMON, D3D12_BARRIER_ACCESS_COMMON,
|
||||
D3D12_BARRIER_LAYOUT_COMMON, D3D12_BARRIER_LAYOUT_COMMON,
|
||||
m_resource.get(), {static_cast<u32>(level), 0, 0, 0, 0, 0}, D3D12_TEXTURE_BARRIER_FLAG_NONE}};
|
||||
|
||||
uint num_barriers = 1;
|
||||
D3D12_TEXTURE_BARRIER& barrier = barriers[0];
|
||||
switch (before_state)
|
||||
{
|
||||
case ResourceState::Undefined:
|
||||
case ResourceState::Present:
|
||||
barrier.LayoutBefore = D3D12_BARRIER_LAYOUT_COMMON;
|
||||
barrier.AccessBefore = D3D12_BARRIER_ACCESS_NO_ACCESS;
|
||||
barrier.SyncBefore = D3D12_BARRIER_SYNC_NONE;
|
||||
break;
|
||||
case ResourceState::RenderTarget:
|
||||
barrier.LayoutBefore = m_simultaneous_tex ? D3D12_BARRIER_LAYOUT_COMMON : D3D12_BARRIER_LAYOUT_RENDER_TARGET;
|
||||
barrier.AccessBefore = m_simultaneous_tex ?
|
||||
D3D12_BARRIER_ACCESS_RENDER_TARGET | D3D12_BARRIER_ACCESS_SHADER_RESOURCE :
|
||||
D3D12_BARRIER_ACCESS_RENDER_TARGET;
|
||||
barrier.SyncBefore = m_simultaneous_tex ?
|
||||
D3D12_BARRIER_SYNC_RENDER_TARGET | D3D12_BARRIER_SYNC_PIXEL_SHADING :
|
||||
D3D12_BARRIER_SYNC_RENDER_TARGET;
|
||||
break;
|
||||
case ResourceState::DepthWriteStencil:
|
||||
pxAssert(!m_simultaneous_tex);
|
||||
barrier.LayoutBefore = D3D12_BARRIER_LAYOUT_DEPTH_STENCIL_WRITE;
|
||||
barrier.AccessBefore = D3D12_BARRIER_ACCESS_DEPTH_STENCIL_WRITE;
|
||||
barrier.SyncBefore = D3D12_BARRIER_SYNC_DEPTH_STENCIL;
|
||||
break;
|
||||
case ResourceState::DepthReadStencil:
|
||||
pxAssert(!m_simultaneous_tex);
|
||||
pxAssert(level == D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES);
|
||||
|
||||
barriers[0].Subresources = {0, static_cast<uint>(m_mipmap_levels), 0, 1, 0, 1};
|
||||
barriers[0].LayoutBefore = D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_GENERIC_READ;
|
||||
barriers[0].AccessBefore = D3D12_BARRIER_ACCESS_SHADER_RESOURCE | D3D12_BARRIER_ACCESS_DEPTH_STENCIL_READ;
|
||||
barriers[0].SyncBefore = D3D12_BARRIER_SYNC_DEPTH_STENCIL | D3D12_BARRIER_SYNC_PIXEL_SHADING;
|
||||
if (after_state != ResourceState::DepthWriteStencil)
|
||||
{
|
||||
num_barriers = 2;
|
||||
barriers[1].Subresources = {0, static_cast<uint>(m_mipmap_levels), 0, 1, 1, 1};
|
||||
barriers[1].LayoutBefore = D3D12_BARRIER_LAYOUT_DEPTH_STENCIL_WRITE;
|
||||
barriers[1].AccessBefore = D3D12_BARRIER_ACCESS_DEPTH_STENCIL_READ;
|
||||
barriers[1].SyncBefore = D3D12_BARRIER_SYNC_DEPTH_STENCIL;
|
||||
}
|
||||
break;
|
||||
case ResourceState::PixelShaderResource:
|
||||
barrier.LayoutBefore = m_simultaneous_tex ? D3D12_BARRIER_LAYOUT_COMMON : D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_SHADER_RESOURCE;
|
||||
barrier.AccessBefore = D3D12_BARRIER_ACCESS_SHADER_RESOURCE;
|
||||
barrier.SyncBefore = D3D12_BARRIER_SYNC_PIXEL_SHADING;
|
||||
break;
|
||||
case ResourceState::ComputeShaderResource:
|
||||
barrier.LayoutBefore = m_simultaneous_tex ? D3D12_BARRIER_LAYOUT_COMMON : D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_SHADER_RESOURCE;
|
||||
barrier.AccessBefore = D3D12_BARRIER_ACCESS_SHADER_RESOURCE;
|
||||
barrier.SyncBefore = D3D12_BARRIER_SYNC_COMPUTE_SHADING;
|
||||
break;
|
||||
case ResourceState::CopySrc:
|
||||
barrier.LayoutBefore = m_simultaneous_tex ? D3D12_BARRIER_LAYOUT_COMMON : D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_SOURCE;
|
||||
barrier.AccessBefore = D3D12_BARRIER_ACCESS_COPY_SOURCE;
|
||||
barrier.SyncBefore = D3D12_BARRIER_SYNC_COPY;
|
||||
break;
|
||||
case ResourceState::CopyDst:
|
||||
barrier.LayoutBefore = m_simultaneous_tex ? D3D12_BARRIER_LAYOUT_COMMON : D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_DEST;
|
||||
barrier.AccessBefore = D3D12_BARRIER_ACCESS_COPY_DEST;
|
||||
barrier.SyncBefore = D3D12_BARRIER_SYNC_COPY;
|
||||
break;
|
||||
case ResourceState::CASShaderUAV:
|
||||
barrier.LayoutBefore = m_simultaneous_tex ? D3D12_BARRIER_LAYOUT_COMMON : D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_UNORDERED_ACCESS;
|
||||
barrier.AccessBefore = D3D12_BARRIER_ACCESS_UNORDERED_ACCESS;
|
||||
barrier.SyncBefore = D3D12_BARRIER_SYNC_COMPUTE_SHADING;
|
||||
break;
|
||||
case ResourceState::PixelShaderUAV:
|
||||
barrier.LayoutBefore = m_simultaneous_tex ? D3D12_BARRIER_LAYOUT_COMMON : D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_UNORDERED_ACCESS;
|
||||
barrier.AccessBefore = D3D12_BARRIER_ACCESS_UNORDERED_ACCESS;
|
||||
barrier.SyncBefore = D3D12_BARRIER_SYNC_PIXEL_SHADING | D3D12_BARRIER_SYNC_CLEAR_UNORDERED_ACCESS_VIEW;
|
||||
break;
|
||||
default:
|
||||
pxAssert(false);
|
||||
barrier.LayoutBefore = D3D12_BARRIER_LAYOUT_UNDEFINED;
|
||||
barrier.AccessBefore = D3D12_BARRIER_ACCESS_NO_ACCESS;
|
||||
barrier.SyncBefore = D3D12_BARRIER_SYNC_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (after_state)
|
||||
{
|
||||
case ResourceState::Undefined:
|
||||
case ResourceState::Present:
|
||||
barrier.LayoutAfter = D3D12_BARRIER_LAYOUT_COMMON;
|
||||
barrier.AccessAfter = D3D12_BARRIER_ACCESS_NO_ACCESS;
|
||||
barrier.SyncAfter = D3D12_BARRIER_SYNC_NONE;
|
||||
break;
|
||||
case ResourceState::RenderTarget:
|
||||
barrier.LayoutAfter = m_simultaneous_tex ? D3D12_BARRIER_LAYOUT_COMMON : D3D12_BARRIER_LAYOUT_RENDER_TARGET;
|
||||
barrier.AccessAfter = m_simultaneous_tex ?
|
||||
D3D12_BARRIER_ACCESS_RENDER_TARGET | D3D12_BARRIER_ACCESS_SHADER_RESOURCE :
|
||||
D3D12_BARRIER_ACCESS_RENDER_TARGET;
|
||||
barrier.SyncAfter = m_simultaneous_tex ?
|
||||
D3D12_BARRIER_SYNC_RENDER_TARGET | D3D12_BARRIER_SYNC_PIXEL_SHADING :
|
||||
D3D12_BARRIER_SYNC_RENDER_TARGET;
|
||||
break;
|
||||
case ResourceState::DepthWriteStencil:
|
||||
pxAssert(!m_simultaneous_tex);
|
||||
barrier.LayoutAfter = D3D12_BARRIER_LAYOUT_DEPTH_STENCIL_WRITE;
|
||||
barrier.AccessAfter = D3D12_BARRIER_ACCESS_DEPTH_STENCIL_WRITE;
|
||||
barrier.SyncAfter = D3D12_BARRIER_SYNC_DEPTH_STENCIL;
|
||||
break;
|
||||
case ResourceState::DepthReadStencil:
|
||||
pxAssert(!m_simultaneous_tex);
|
||||
pxAssert(level == D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES);
|
||||
|
||||
barriers[0].Subresources = {0, static_cast<uint>(m_mipmap_levels), 0, 1, 0, 1};
|
||||
barriers[0].LayoutAfter = D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_GENERIC_READ;
|
||||
barriers[0].AccessAfter = D3D12_BARRIER_ACCESS_SHADER_RESOURCE | D3D12_BARRIER_ACCESS_DEPTH_STENCIL_READ;
|
||||
barriers[0].SyncAfter = D3D12_BARRIER_SYNC_DEPTH_STENCIL | D3D12_BARRIER_SYNC_PIXEL_SHADING;
|
||||
if (before_state != ResourceState::DepthWriteStencil)
|
||||
{
|
||||
num_barriers = 2;
|
||||
barriers[1].Subresources = {0, static_cast<uint>(m_mipmap_levels), 0, 1, 1, 1};
|
||||
barriers[1].LayoutAfter = D3D12_BARRIER_LAYOUT_DEPTH_STENCIL_WRITE;
|
||||
barriers[1].AccessAfter = D3D12_BARRIER_ACCESS_DEPTH_STENCIL_READ;
|
||||
barriers[1].SyncAfter = D3D12_BARRIER_SYNC_DEPTH_STENCIL;
|
||||
}
|
||||
break;
|
||||
case ResourceState::PixelShaderResource:
|
||||
barrier.LayoutAfter = m_simultaneous_tex ? D3D12_BARRIER_LAYOUT_COMMON : D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_SHADER_RESOURCE;
|
||||
barrier.AccessAfter = D3D12_BARRIER_ACCESS_SHADER_RESOURCE;
|
||||
barrier.SyncAfter = D3D12_BARRIER_SYNC_PIXEL_SHADING;
|
||||
break;
|
||||
case ResourceState::ComputeShaderResource:
|
||||
barrier.LayoutAfter = m_simultaneous_tex ? D3D12_BARRIER_LAYOUT_COMMON : D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_SHADER_RESOURCE;
|
||||
barrier.AccessAfter = D3D12_BARRIER_ACCESS_SHADER_RESOURCE;
|
||||
barrier.SyncAfter = D3D12_BARRIER_SYNC_COMPUTE_SHADING;
|
||||
break;
|
||||
case ResourceState::CopySrc:
|
||||
barrier.LayoutAfter = m_simultaneous_tex ? D3D12_BARRIER_LAYOUT_COMMON : D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_SOURCE;
|
||||
barrier.AccessAfter = D3D12_BARRIER_ACCESS_COPY_SOURCE;
|
||||
barrier.SyncAfter = D3D12_BARRIER_SYNC_COPY;
|
||||
break;
|
||||
case ResourceState::CopyDst:
|
||||
barrier.LayoutAfter = m_simultaneous_tex ? D3D12_BARRIER_LAYOUT_COMMON : D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_DEST;
|
||||
barrier.AccessAfter = D3D12_BARRIER_ACCESS_COPY_DEST;
|
||||
barrier.SyncAfter = D3D12_BARRIER_SYNC_COPY;
|
||||
break;
|
||||
case ResourceState::CASShaderUAV:
|
||||
barrier.LayoutAfter = m_simultaneous_tex ? D3D12_BARRIER_LAYOUT_COMMON : D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_UNORDERED_ACCESS;
|
||||
barrier.AccessAfter = D3D12_BARRIER_ACCESS_UNORDERED_ACCESS;
|
||||
barrier.SyncAfter = D3D12_BARRIER_SYNC_COMPUTE_SHADING;
|
||||
break;
|
||||
case ResourceState::PixelShaderUAV:
|
||||
barrier.LayoutAfter = m_simultaneous_tex ? D3D12_BARRIER_LAYOUT_COMMON : D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_UNORDERED_ACCESS;
|
||||
barrier.AccessAfter = D3D12_BARRIER_ACCESS_UNORDERED_ACCESS;
|
||||
barrier.SyncAfter = D3D12_BARRIER_SYNC_PIXEL_SHADING | D3D12_BARRIER_SYNC_CLEAR_UNORDERED_ACCESS_VIEW;
|
||||
break;
|
||||
default:
|
||||
pxAssert(false);
|
||||
barrier.LayoutAfter = D3D12_BARRIER_LAYOUT_UNDEFINED;
|
||||
barrier.AccessAfter = D3D12_BARRIER_ACCESS_NO_ACCESS;
|
||||
barrier.SyncAfter = D3D12_BARRIER_SYNC_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (num_barriers == 2)
|
||||
{
|
||||
barriers[1].pResource = m_resource.get();
|
||||
barriers[1].Flags = barriers[0].Flags;
|
||||
if (before_state == ResourceState::DepthReadStencil)
|
||||
{
|
||||
barriers[1].LayoutAfter = barriers[0].LayoutAfter;
|
||||
barriers[1].AccessAfter = barriers[0].AccessAfter;
|
||||
barriers[1].SyncAfter = barriers[0].SyncAfter;
|
||||
}
|
||||
else // after_state == ResourceState::DepthReadStencil
|
||||
{
|
||||
barriers[1].LayoutBefore = barriers[0].LayoutBefore;
|
||||
barriers[1].AccessBefore = barriers[0].AccessBefore;
|
||||
barriers[1].SyncBefore = barriers[0].SyncBefore;
|
||||
}
|
||||
}
|
||||
|
||||
const D3D12_BARRIER_GROUP group = {.Type = D3D12_BARRIER_TYPE_TEXTURE, .NumBarriers = num_barriers, .pTextureBarriers = barriers};
|
||||
cmdlist.list7->Barrier(1, &group);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read only depth requires special handling as we might want to write stencil.
|
||||
// We need to transition subresources separately, requiring 2 barriers.
|
||||
// Handling it here allows us to batch those barriers.
|
||||
// Other transitions only need the one barrier.
|
||||
D3D12_RESOURCE_BARRIER barriers[2] = {{D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_BARRIER_FLAG_NONE,
|
||||
{{m_resource.get(), static_cast<u32>(level), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COMMON}}}};
|
||||
|
||||
int num_barriers = 1;
|
||||
D3D12_RESOURCE_BARRIER& barrier = barriers[0];
|
||||
switch (before_state)
|
||||
{
|
||||
case ResourceState::Undefined:
|
||||
case ResourceState::Present:
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON;
|
||||
break;
|
||||
case ResourceState::RenderTarget:
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
break;
|
||||
case ResourceState::DepthWriteStencil:
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_DEPTH_WRITE;
|
||||
break;
|
||||
case ResourceState::DepthReadStencil:
|
||||
pxAssert(!m_simultaneous_tex);
|
||||
pxAssert(m_mipmap_levels == 1);
|
||||
pxAssert(level == D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES);
|
||||
|
||||
barriers[0].Transition.Subresource = 0;
|
||||
barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_DEPTH_READ;
|
||||
if (after_state != ResourceState::DepthWriteStencil)
|
||||
{
|
||||
num_barriers = 2;
|
||||
barriers[1].Transition.Subresource = 1;
|
||||
barriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_DEPTH_WRITE;
|
||||
}
|
||||
break;
|
||||
case ResourceState::PixelShaderResource:
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||||
break;
|
||||
case ResourceState::ComputeShaderResource:
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
|
||||
break;
|
||||
case ResourceState::CopySrc:
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
|
||||
break;
|
||||
case ResourceState::CopyDst:
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
break;
|
||||
case ResourceState::CASShaderUAV:
|
||||
case ResourceState::PixelShaderUAV:
|
||||
// Handled in after_state cases.
|
||||
if (after_state == ResourceState::CASShaderUAV || after_state == ResourceState::PixelShaderUAV)
|
||||
break;
|
||||
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
|
||||
break;
|
||||
default:
|
||||
pxAssert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (after_state)
|
||||
{
|
||||
case ResourceState::Undefined:
|
||||
case ResourceState::Present:
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COMMON;
|
||||
break;
|
||||
case ResourceState::RenderTarget:
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
break;
|
||||
case ResourceState::DepthWriteStencil:
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_DEPTH_WRITE;
|
||||
break;
|
||||
case ResourceState::DepthReadStencil:
|
||||
pxAssert(!m_simultaneous_tex);
|
||||
pxAssert(m_mipmap_levels == 1);
|
||||
pxAssert(level == D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES);
|
||||
|
||||
barriers[0].Transition.Subresource = 0;
|
||||
barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_DEPTH_READ;
|
||||
if (before_state != ResourceState::DepthWriteStencil)
|
||||
{
|
||||
num_barriers = 2;
|
||||
barriers[1].Transition.Subresource = 1;
|
||||
barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_DEPTH_WRITE;
|
||||
}
|
||||
break;
|
||||
case ResourceState::PixelShaderResource:
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||||
break;
|
||||
case ResourceState::ComputeShaderResource:
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
|
||||
break;
|
||||
case ResourceState::CopySrc:
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
|
||||
break;
|
||||
case ResourceState::CopyDst:
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
break;
|
||||
case ResourceState::CASShaderUAV:
|
||||
case ResourceState::PixelShaderUAV:
|
||||
if (before_state == ResourceState::CASShaderUAV || before_state == ResourceState::PixelShaderUAV)
|
||||
{
|
||||
// No state transition, but probably want a barrier instead.
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
|
||||
// pResource is a common initial member, so no need to set again.
|
||||
}
|
||||
else
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
|
||||
break;
|
||||
default:
|
||||
pxAssert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if (num_barriers == 2)
|
||||
{
|
||||
barriers[1].Transition.pResource = m_resource.get();
|
||||
barriers[1].Type = barriers[0].Type;
|
||||
barriers[1].Flags = barriers[0].Flags;
|
||||
if (before_state == ResourceState::DepthReadStencil)
|
||||
barriers[1].Transition.StateAfter = barriers[0].Transition.StateAfter;
|
||||
else // after_state == ResourceState::DepthReadStencil
|
||||
barriers[1].Transition.StateBefore = barriers[0].Transition.StateBefore;
|
||||
}
|
||||
|
||||
cmdlist.list4->ResourceBarrier(num_barriers, barriers);
|
||||
}
|
||||
}
|
||||
|
||||
void GSTexture12::CommitClear()
|
||||
@@ -774,22 +1147,21 @@ void GSTexture12::CommitClear()
|
||||
return;
|
||||
|
||||
GSDevice12::GetInstance()->EndRenderPass();
|
||||
|
||||
CommitClear(GSDevice12::GetInstance()->GetCommandList());
|
||||
}
|
||||
|
||||
void GSTexture12::CommitClear(ID3D12GraphicsCommandList* cmdlist)
|
||||
void GSTexture12::CommitClear(const D3D12CommandList& cmdlist)
|
||||
{
|
||||
if (IsDepthStencil())
|
||||
{
|
||||
TransitionToState(cmdlist, D3D12_RESOURCE_STATE_DEPTH_WRITE);
|
||||
cmdlist->ClearDepthStencilView(
|
||||
TransitionToState(cmdlist, ResourceState::DepthWriteStencil);
|
||||
cmdlist.list4->ClearDepthStencilView(
|
||||
GetWriteDescriptor(), D3D12_CLEAR_FLAG_DEPTH, m_clear_value.depth, 0, 0, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||
cmdlist->ClearRenderTargetView(GetWriteDescriptor(), GSVector4::unorm8(m_clear_value.color).v, 0, nullptr);
|
||||
TransitionToState(cmdlist, ResourceState::RenderTarget);
|
||||
cmdlist.list4->ClearRenderTargetView(GetWriteDescriptor(), GSVector4::unorm8(m_clear_value.color).v, 0, nullptr);
|
||||
}
|
||||
|
||||
SetState(GSTexture::State::Dirty);
|
||||
@@ -816,14 +1188,23 @@ std::unique_ptr<GSDownloadTexture12> GSDownloadTexture12::Create(u32 width, u32
|
||||
D3D12MA::ALLOCATION_DESC allocation_desc = {};
|
||||
allocation_desc.HeapType = D3D12_HEAP_TYPE_READBACK;
|
||||
|
||||
const D3D12_RESOURCE_DESC resource_desc = {D3D12_RESOURCE_DIMENSION_BUFFER, 0, buffer_size, 1, 1, 1,
|
||||
DXGI_FORMAT_UNKNOWN, {1, 0}, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, D3D12_RESOURCE_FLAG_NONE};
|
||||
const GSDevice12::D3D12_RESOURCE_DESCU resource_desc = {{D3D12_RESOURCE_DIMENSION_BUFFER, 0, buffer_size, 1, 1, 1,
|
||||
DXGI_FORMAT_UNKNOWN, {1, 0}, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, D3D12_RESOURCE_FLAG_NONE}};
|
||||
|
||||
wil::com_ptr_nothrow<D3D12MA::Allocation> allocation;
|
||||
wil::com_ptr_nothrow<ID3D12Resource> buffer;
|
||||
|
||||
HRESULT hr = GSDevice12::GetInstance()->GetAllocator()->CreateResource(&allocation_desc, &resource_desc,
|
||||
D3D12_RESOURCE_STATE_COPY_DEST, nullptr, allocation.put(), IID_PPV_ARGS(buffer.put()));
|
||||
HRESULT hr;
|
||||
if (GSDevice12::GetInstance()->UseEnhancedBarriers())
|
||||
{
|
||||
hr = GSDevice12::GetInstance()->GetAllocator()->CreateResource3(&allocation_desc, &resource_desc.desc1,
|
||||
D3D12_BARRIER_LAYOUT_UNDEFINED, nullptr, 0, nullptr, allocation.put(), IID_PPV_ARGS(buffer.put()));
|
||||
}
|
||||
else
|
||||
{
|
||||
hr = GSDevice12::GetInstance()->GetAllocator()->CreateResource(&allocation_desc, &resource_desc.desc,
|
||||
D3D12_RESOURCE_STATE_COPY_DEST, nullptr, allocation.put(), IID_PPV_ARGS(buffer.put()));
|
||||
}
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.Error("(GSDownloadTexture12::Create) CreateResource() failed with HRESULT %08X", hr);
|
||||
@@ -861,7 +1242,7 @@ void GSDownloadTexture12::CopyFromTexture(
|
||||
if (IsMapped())
|
||||
Unmap();
|
||||
|
||||
ID3D12GraphicsCommandList* cmdlist = GSDevice12::GetInstance()->GetCommandList();
|
||||
const D3D12CommandList& cmdlist = GSDevice12::GetInstance()->GetCommandList();
|
||||
GL_INS("ReadbackTexture: {%d,%d} %ux%u", src.left, src.top, src.width(), src.height());
|
||||
|
||||
D3D12_TEXTURE_COPY_LOCATION srcloc;
|
||||
@@ -879,17 +1260,17 @@ void GSDownloadTexture12::CopyFromTexture(
|
||||
dstloc.PlacedFootprint.Footprint.Depth = 1;
|
||||
dstloc.PlacedFootprint.Footprint.RowPitch = m_current_pitch;
|
||||
|
||||
const D3D12_RESOURCE_STATES old_layout = tex12->GetResourceState();
|
||||
if (old_layout != D3D12_RESOURCE_STATE_COPY_SOURCE)
|
||||
tex12->TransitionSubresourceToState(cmdlist, src_level, old_layout, D3D12_RESOURCE_STATE_COPY_SOURCE);
|
||||
const GSTexture12::ResourceState old_layout = tex12->GetResourceState();
|
||||
if (old_layout != GSTexture12::ResourceState::CopySrc)
|
||||
tex12->TransitionSubresourceToState(cmdlist, src_level, old_layout, GSTexture12::ResourceState::CopySrc);
|
||||
|
||||
// TODO: Rules for depth buffers here?
|
||||
const D3D12_BOX srcbox{static_cast<UINT>(src.left), static_cast<UINT>(src.top), 0u, static_cast<UINT>(src.right),
|
||||
static_cast<UINT>(src.bottom), 1u};
|
||||
cmdlist->CopyTextureRegion(&dstloc, 0, 0, 0, &srcloc, &srcbox);
|
||||
cmdlist.list4->CopyTextureRegion(&dstloc, 0, 0, 0, &srcloc, &srcbox);
|
||||
|
||||
if (old_layout != D3D12_RESOURCE_STATE_COPY_SOURCE)
|
||||
tex12->TransitionSubresourceToState(cmdlist, src_level, D3D12_RESOURCE_STATE_COPY_SOURCE, old_layout);
|
||||
if (old_layout != GSTexture12::ResourceState::CopySrc)
|
||||
tex12->TransitionSubresourceToState(cmdlist, src_level, GSTexture12::ResourceState::CopySrc, old_layout);
|
||||
|
||||
m_copy_fence_value = GSDevice12::GetInstance()->GetCurrentFenceValue();
|
||||
m_needs_flush = true;
|
||||
|
||||
@@ -16,9 +16,27 @@ namespace D3D12MA
|
||||
class Allocation;
|
||||
}
|
||||
|
||||
struct D3D12CommandList;
|
||||
|
||||
class GSTexture12 final : public GSTexture
|
||||
{
|
||||
public:
|
||||
enum class ResourceState : u32
|
||||
{
|
||||
Undefined,
|
||||
Present,
|
||||
RenderTarget,
|
||||
DepthWriteStencil,
|
||||
DepthReadStencil,
|
||||
PixelShaderResource,
|
||||
ComputeShaderResource,
|
||||
CopySrc,
|
||||
CopyDst,
|
||||
CASShaderUAV, // No Clear UAV Sync
|
||||
PixelShaderUAV,
|
||||
Count
|
||||
};
|
||||
|
||||
~GSTexture12() override;
|
||||
|
||||
static std::unique_ptr<GSTexture12> Create(Type type, Format format, int width, int height, int levels,
|
||||
@@ -26,14 +44,14 @@ public:
|
||||
DXGI_FORMAT uav_format);
|
||||
static std::unique_ptr<GSTexture12> Adopt(wil::com_ptr_nothrow<ID3D12Resource> resource, Type type, Format format,
|
||||
int width, int height, int levels, DXGI_FORMAT dxgi_format, DXGI_FORMAT srv_format, DXGI_FORMAT rtv_format,
|
||||
DXGI_FORMAT dsv_format, DXGI_FORMAT uav_format, D3D12_RESOURCE_STATES resource_state);
|
||||
DXGI_FORMAT dsv_format, DXGI_FORMAT uav_format, ResourceState resource_state);
|
||||
|
||||
__fi const D3D12DescriptorHandle& GetSRVDescriptor() const { return m_srv_descriptor; }
|
||||
__fi const D3D12DescriptorHandle& GetWriteDescriptor() const { return m_write_descriptor; }
|
||||
__fi const D3D12DescriptorHandle& GetReadDepthViewDescriptor() const { return m_read_dsv_descriptor; }
|
||||
__fi const D3D12DescriptorHandle& GetUAVDescriptor() const { return m_uav_descriptor; }
|
||||
__fi const D3D12DescriptorHandle& GetFBLDescriptor() const { return m_fbl_descriptor; }
|
||||
__fi D3D12_RESOURCE_STATES GetResourceState() const { return m_resource_state; }
|
||||
__fi ResourceState GetResourceState() const { return m_resource_state; }
|
||||
__fi DXGI_FORMAT GetDXGIFormat() const { return m_dxgi_format; }
|
||||
__fi ID3D12Resource* GetResource() const { return m_resource.get(); }
|
||||
__fi ID3D12Resource* GetFBLResource() const { return m_resource_fbl.get(); }
|
||||
@@ -49,15 +67,15 @@ public:
|
||||
void SetDebugName(std::string_view name) override;
|
||||
#endif
|
||||
|
||||
void TransitionToState(D3D12_RESOURCE_STATES state);
|
||||
void TransitionToState(ResourceState state);
|
||||
void CommitClear();
|
||||
void CommitClear(ID3D12GraphicsCommandList* cmdlist);
|
||||
void CommitClear(const D3D12CommandList& cmdlist);
|
||||
|
||||
void Destroy(bool defer = true);
|
||||
|
||||
void TransitionToState(ID3D12GraphicsCommandList* cmdlist, D3D12_RESOURCE_STATES state);
|
||||
void TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, int level, D3D12_RESOURCE_STATES before_state,
|
||||
D3D12_RESOURCE_STATES after_state) const;
|
||||
void TransitionToState(const D3D12CommandList&, ResourceState state);
|
||||
void TransitionSubresourceToState(const D3D12CommandList& cmdlist, int level, ResourceState before_state,
|
||||
ResourceState after_state) const;
|
||||
|
||||
// Call when the texture is bound to the pipeline, or read from in a copy.
|
||||
__fi void SetUseFenceCounter(u64 val) { m_use_fence_counter = val; }
|
||||
@@ -75,7 +93,7 @@ private:
|
||||
wil::com_ptr_nothrow<D3D12MA::Allocation> allocation, const D3D12DescriptorHandle& srv_descriptor,
|
||||
const D3D12DescriptorHandle& write_descriptor, const D3D12DescriptorHandle& ro_dsv_descriptor,
|
||||
const D3D12DescriptorHandle& uav_descriptor, const D3D12DescriptorHandle& fbl_descriptor,
|
||||
WriteDescriptorType wdtype, D3D12_RESOURCE_STATES resource_state);
|
||||
WriteDescriptorType wdtype, bool simultaneous_texture, ResourceState resource_state);
|
||||
|
||||
static bool CreateSRVDescriptor(
|
||||
ID3D12Resource* resource, u32 levels, DXGI_FORMAT format, D3D12DescriptorHandle* dh);
|
||||
@@ -83,7 +101,7 @@ private:
|
||||
static bool CreateDSVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh, bool read_only);
|
||||
static bool CreateUAVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh);
|
||||
|
||||
ID3D12GraphicsCommandList* GetCommandBufferForUpdate();
|
||||
const D3D12CommandList& GetCommandBufferForUpdate();
|
||||
ID3D12Resource* AllocateUploadStagingBuffer(const void* data, u32 pitch, u32 upload_pitch, u32 height) const;
|
||||
void CopyTextureDataForUpload(void* dst, const void* src, u32 pitch, u32 upload_pitch, u32 height) const;
|
||||
|
||||
@@ -99,7 +117,11 @@ private:
|
||||
WriteDescriptorType m_write_descriptor_type = WriteDescriptorType::None;
|
||||
|
||||
DXGI_FORMAT m_dxgi_format = DXGI_FORMAT_UNKNOWN;
|
||||
D3D12_RESOURCE_STATES m_resource_state = D3D12_RESOURCE_STATE_COMMON;
|
||||
ResourceState m_resource_state = ResourceState::Undefined;
|
||||
|
||||
// With legacy barriers, an aliased resource is used as the feedback shader resource.
|
||||
// With enhanced barriers, the layout is always COMMON, but can use the main resource for feedback.
|
||||
bool m_simultaneous_tex;
|
||||
|
||||
// Contains the fence counter when the texture was last used.
|
||||
// When this matches the current fence counter, the texture was used this command buffer.
|
||||
|
||||
@@ -39,15 +39,77 @@ static bool s_nativeres;
|
||||
|
||||
bool GSHwHack::GSC_IRem(GSRendererHW& r, int& skip)
|
||||
{
|
||||
static bool first_shuffle = false;
|
||||
|
||||
if (skip > 0)
|
||||
{
|
||||
if (skip == 1 && first_shuffle)
|
||||
{
|
||||
first_shuffle = false;
|
||||
|
||||
GIFRegTEX0 RTLookup = GIFRegTEX0::Create(RTBP0, RFBW, RFPSM);
|
||||
GSTextureCache::Source* src = g_texture_cache->LookupSource(true, RTLookup, r.m_cached_ctx.TEXA, r.m_cached_ctx.CLAMP, GSVector4i(0, 0, 1, 1), nullptr, true, false, r.m_cached_ctx.FRAME, true, true);
|
||||
|
||||
GSTextureCache::Target* rt = g_texture_cache->LookupTarget(GIFRegTEX0::Create(RTBP0, RFBW, RFPSM),
|
||||
GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget, true, 0, false, false, true, true, GSVector4i(0, 0, 1, 1), true, false, true, src);
|
||||
|
||||
if (!rt)
|
||||
return false;
|
||||
|
||||
GSLocalMemory::psm_t rt_psm = GSLocalMemory::m_psm[RFPSM];
|
||||
int page_offset = (RTBP0 - rt->m_TEX0.TBP0) >> 5;
|
||||
int vertical_offset = page_offset / std::max(rt->m_TEX0.TBW, 1U) * rt_psm.pgs.y;
|
||||
int horizontal_offset = page_offset % std::max(rt->m_TEX0.TBW, 1U) * rt_psm.pgs.x;
|
||||
|
||||
GSVector4i draw_size = GSVector4i(0, 0, 64, 32) + GSVector4i(horizontal_offset, vertical_offset, horizontal_offset, vertical_offset);
|
||||
rt->UnscaleRTAlpha();
|
||||
|
||||
// We need the original red back now for the next channel shuffle.
|
||||
GSHWDrawConfig& config = r.BeginHLEHardwareDraw(
|
||||
rt->GetTexture(), nullptr, rt->GetScale(), rt->GetTexture(), rt->GetScale(), draw_size);
|
||||
config.ps.shuffle = 1;
|
||||
config.ps.dst_fmt = GSLocalMemory::PSM_FMT_32;
|
||||
config.ps.write_rg = 0;
|
||||
config.ps.shuffle_same = 0;
|
||||
config.ps.real16src = 0;
|
||||
config.ps.shuffle_across = 1;
|
||||
config.ps.process_rg = r.SHUFFLE_READWRITE;
|
||||
config.ps.process_ba = r.SHUFFLE_READWRITE;
|
||||
config.colormask.wrgba = 0;
|
||||
config.colormask.wr = 1;
|
||||
config.colormask.wb = 1;
|
||||
config.ps.rta_correction = 0;
|
||||
config.ps.rta_source_correction = 0;
|
||||
config.ps.tfx = TFX_DECAL;
|
||||
config.ps.tcc = true;
|
||||
r.EndHLEHardwareDraw(true);
|
||||
|
||||
rt->m_alpha_min = 0;
|
||||
rt->m_alpha_max = 255;
|
||||
|
||||
rt = nullptr;
|
||||
src = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
skip--;
|
||||
return !first_shuffle;
|
||||
}
|
||||
}
|
||||
|
||||
if (skip == 0)
|
||||
{
|
||||
const int get_next_ctx = r.m_env.PRIM.CTXT;
|
||||
const GSDrawingContext& next_ctx = r.m_env.CTXT[get_next_ctx];
|
||||
|
||||
// Uses these to do some shuffle tricks, it breaks things for us
|
||||
r.m_env.SCANMSK.MSK = 0;
|
||||
r.m_prev_env.SCANMSK.MSK = 0;
|
||||
|
||||
// Game does alternate line channel shuffles with blending, we can't handle this and the first one does it, so skip the second.
|
||||
if (RTME && RTPSM == PSMT8 && (RTBP0 + 0x20) == next_ctx.TEX0.TBP0 && RFBP == next_ctx.FRAME.Block())
|
||||
{
|
||||
skip = 1;
|
||||
skip = 2;
|
||||
return false;
|
||||
}
|
||||
// Detect the deswizzling shuffle from depth, copying the RG and BA separately on each half of the page (ignore the split).
|
||||
@@ -90,7 +152,7 @@ bool GSHwHack::GSC_IRem(GSRendererHW& r, int& skip)
|
||||
draw_size = draw_size + GSVector4i(horizontal_offset, vertical_offset, horizontal_offset, vertical_offset);
|
||||
rt->UnscaleRTAlpha();
|
||||
|
||||
// Shuffle the blue channel in to red, leave green as-is.
|
||||
// Shuffle the blue channel in to red, but swap them, we'll need the original red later.
|
||||
GSHWDrawConfig& config = r.BeginHLEHardwareDraw(
|
||||
rt->GetTexture(), nullptr, rt->GetScale(), rt->GetTexture(), rt->GetScale(), draw_size);
|
||||
config.ps.shuffle = 1;
|
||||
@@ -99,10 +161,11 @@ bool GSHwHack::GSC_IRem(GSRendererHW& r, int& skip)
|
||||
config.ps.shuffle_same = 0;
|
||||
config.ps.real16src = 0;
|
||||
config.ps.shuffle_across = 1;
|
||||
config.ps.process_rg = r.SHUFFLE_WRITE;
|
||||
config.ps.process_ba = r.SHUFFLE_READ;
|
||||
config.ps.process_rg = r.SHUFFLE_READWRITE;
|
||||
config.ps.process_ba = r.SHUFFLE_READWRITE;
|
||||
config.colormask.wrgba = 0;
|
||||
config.colormask.wr = 1;
|
||||
config.colormask.wb = 1;
|
||||
config.ps.rta_correction = 0;
|
||||
config.ps.rta_source_correction = 0;
|
||||
config.ps.tfx = TFX_DECAL;
|
||||
@@ -114,6 +177,7 @@ bool GSHwHack::GSC_IRem(GSRendererHW& r, int& skip)
|
||||
|
||||
rt = nullptr;
|
||||
src = nullptr;
|
||||
first_shuffle = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -692,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
|
||||
@@ -750,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;
|
||||
|
||||
@@ -165,11 +165,12 @@ GSTexture* GSRendererHW::GetOutput(int i, float& scale, int& y_offset)
|
||||
|
||||
if (GSTextureCache::Target* rt = g_texture_cache->LookupDisplayTarget(TEX0, framebufferSize, GetTextureScaleFactor(), false))
|
||||
{
|
||||
const u32 bp_adj = (TEX0.TBP0 < rt->m_TEX0.TBP0 && rt->UnwrappedEndBlock() > GS_MAX_BLOCKS) ? (TEX0.TBP0 + GS_MAX_BLOCKS) : TEX0.TBP0;
|
||||
rt->Update();
|
||||
t = rt->m_texture;
|
||||
scale = rt->m_scale;
|
||||
|
||||
const int delta = TEX0.TBP0 - rt->m_TEX0.TBP0;
|
||||
const int delta = bp_adj - rt->m_TEX0.TBP0;
|
||||
if (delta > 0 && curFramebuffer.FBW != 0)
|
||||
{
|
||||
const int pages = delta >> 5u;
|
||||
@@ -2338,6 +2339,12 @@ void GSRendererHW::Draw()
|
||||
return;
|
||||
}
|
||||
|
||||
// Sometimes everything will get reset and it will draw a single black point in the top left corner,
|
||||
// which can cause invalid targets to be created, so might as well skip it.
|
||||
if (GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)).eq(GSVector4i::zero()) && m_vt.m_eq.rgba == 0xffff &&
|
||||
m_vt.m_max.c.rgba32() == 0 && m_draw_env->PRIM.PRIM == GS_POINTLIST && m_env.PRIM.PRIM != GS_POINTLIST)
|
||||
return;
|
||||
|
||||
// Channel shuffles repeat lots of draws. Get out early if we can.
|
||||
if (m_channel_shuffle)
|
||||
{
|
||||
@@ -2345,9 +2352,18 @@ void GSRendererHW::Draw()
|
||||
// Fortunately, it seems to change the FBMSK along the way, so this check alone is sufficient.
|
||||
// Tomb Raider: Underworld does similar, except with R, G, B in separate palettes, therefore
|
||||
// we need to split on those too.
|
||||
m_channel_shuffle = !m_channel_shuffle_abort && IsPossibleChannelShuffle() && m_last_channel_shuffle_fbmsk == m_context->FRAME.FBMSK &&
|
||||
m_last_channel_shuffle_fbp <= m_context->FRAME.Block() && m_last_channel_shuffle_end_block > m_context->FRAME.Block() &&
|
||||
m_last_channel_shuffle_tbp <= m_context->TEX0.TBP0;
|
||||
const bool is_hle_skip = m_conf.ps.urban_chaos_hle || m_conf.ps.tales_of_abyss_hle;
|
||||
const u32 max_skip = ((m_channel_shuffle_finish || !m_channel_shuffle_width) ? std::max(m_context->FRAME.FBW, 1U) : m_channel_shuffle_width) << 5;
|
||||
const bool shuffle_detect = IsPossibleChannelShuffle() && m_last_channel_shuffle_fbmsk == m_context->FRAME.FBMSK &&
|
||||
m_last_channel_shuffle_fbp <= m_context->FRAME.Block() && (m_last_channel_shuffle_fbp + max_skip) >= m_context->FRAME.Block() &&
|
||||
m_last_channel_shuffle_end_block > m_context->FRAME.Block() && m_last_channel_shuffle_tbp <= m_context->TEX0.TBP0
|
||||
&& (m_last_channel_shuffle_tbp + max_skip) >= m_context->TEX0.TBP0;
|
||||
|
||||
const bool shuffle_detect_loose = IsPossibleChannelShuffle() && m_last_channel_shuffle_fbmsk == m_context->FRAME.FBMSK &&
|
||||
m_last_channel_shuffle_fbp <= m_context->FRAME.Block() &&
|
||||
m_last_channel_shuffle_end_block > m_context->FRAME.Block() && m_last_channel_shuffle_tbp <= m_context->TEX0.TBP0;
|
||||
|
||||
m_channel_shuffle = !m_channel_shuffle_finish && ((!is_hle_skip && shuffle_detect) || (is_hle_skip && shuffle_detect_loose));
|
||||
|
||||
if (m_channel_shuffle)
|
||||
{
|
||||
@@ -2415,12 +2431,21 @@ void GSRendererHW::Draw()
|
||||
CleanupDraw(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!shuffle_detect)
|
||||
{
|
||||
m_last_channel_shuffle_fbp = 0xffff;
|
||||
m_last_channel_shuffle_tbp = 0xffff;
|
||||
m_last_channel_shuffle_end_block = 0xffff;
|
||||
}
|
||||
#ifdef ENABLE_OGL_DEBUG
|
||||
if (num_skipped_channel_shuffle_draws > 0)
|
||||
GL_CACHE("HW: Skipped %d channel shuffle draws ending at %d", num_skipped_channel_shuffle_draws, s_n);
|
||||
#endif
|
||||
num_skipped_channel_shuffle_draws = 0;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
m_last_channel_shuffle_fbp = 0xffff;
|
||||
m_last_channel_shuffle_tbp = 0xffff;
|
||||
m_last_channel_shuffle_end_block = 0xffff;
|
||||
@@ -2429,7 +2454,7 @@ void GSRendererHW::Draw()
|
||||
m_last_rt = nullptr;
|
||||
m_channel_shuffle_width = 0;
|
||||
m_full_screen_shuffle = false;
|
||||
m_channel_shuffle_abort = false;
|
||||
m_channel_shuffle_finish = false;
|
||||
m_channel_shuffle_src_valid = GSVector4i::zero();
|
||||
|
||||
GL_PUSH("HW: Draw %d (Context %u)", s_n, PRIM->CTXT);
|
||||
@@ -2826,7 +2851,7 @@ void GSRendererHW::Draw()
|
||||
const bool page_aligned = (m_r.w % pgs.y) == (pgs.y - 1) || (m_r.w % pgs.y) == 0;
|
||||
const bool is_zero_color_clear = (GetConstantDirectWriteMemClearColor() == 0 && !preserve_rt_color && page_aligned);
|
||||
const bool is_zero_depth_clear = (GetConstantDirectWriteMemClearDepth() == 0 && !preserve_depth && page_aligned);
|
||||
|
||||
bool gs_mem_cleared = false;
|
||||
// If it's an invalid-sized draw, do the mem clear on the CPU, we don't want to create huge targets.
|
||||
// If clearing to zero, don't bother creating the target. Games tend to clear more than they use, wasting VRAM/bandwidth.
|
||||
if (is_zero_color_clear || is_zero_depth_clear || height_invalid)
|
||||
@@ -2858,7 +2883,7 @@ void GSRendererHW::Draw()
|
||||
{
|
||||
g_texture_cache->InvalidateTemporaryZ();
|
||||
}
|
||||
|
||||
gs_mem_cleared |= overwriting_whole_rt && overwriting_whole_ds && (!no_rt || !no_ds);
|
||||
if (overwriting_whole_rt && overwriting_whole_ds &&
|
||||
TryGSMemClear(no_rt, preserve_rt_color, is_zero_color_clear, rt_end_bp,
|
||||
no_ds, preserve_depth, is_zero_depth_clear, ds_end_bp))
|
||||
@@ -2888,6 +2913,27 @@ void GSRendererHW::Draw()
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If not a zero clear or the RT's aren't fully overwritten, we need to see if this is clearing for a future operation.
|
||||
// So if the FBP or Z being cleared isn't getting used next frame, clear the actual GS memory.
|
||||
if (!gs_mem_cleared)
|
||||
{
|
||||
const int get_next_ctx = m_env.PRIM.CTXT;
|
||||
const GSDrawingContext& next_ctx = m_env.CTXT[get_next_ctx];
|
||||
if ((!no_rt && next_ctx.FRAME.FBP != m_cached_ctx.FRAME.FBP) || (!no_ds && next_ctx.ZBUF.ZBP != m_cached_ctx.ZBUF.ZBP))
|
||||
{
|
||||
bool frame_masked = no_rt || (m_cached_ctx.FRAME.FBMSK & GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk) || !IsOpaque() || !IsRTWritten();
|
||||
const bool z_masked = no_ds || m_cached_ctx.ZBUF.ZMSK;
|
||||
|
||||
if (frame_masked && m_cached_ctx.FRAME.PSM == PSMCT32 && m_cached_ctx.FRAME.FBMSK == 0xFF000000u)
|
||||
{
|
||||
frame_masked = no_rt || !IsOpaque() || !IsRTWritten();
|
||||
}
|
||||
|
||||
// Force clear of memory but don't invalidate anything.
|
||||
TryGSMemClear(frame_masked, false, false, 0, z_masked, false, false, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIFRegTEX0 TEX0 = {};
|
||||
@@ -3085,15 +3131,16 @@ void GSRendererHW::Draw()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
possible_shuffle = !no_rt && (((shuffle_target /*&& GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16*/) /*|| (m_cached_ctx.FRAME.Block() == m_cached_ctx.TEX0.TBP0 && ((m_cached_ctx.TEX0.PSM & 0x6) || m_cached_ctx.FRAME.PSM != m_cached_ctx.TEX0.PSM))*/) || IsPossibleChannelShuffle());
|
||||
const bool is_possible_channel_shuffle = IsPossibleChannelShuffle();
|
||||
possible_shuffle = !no_rt && (((shuffle_target /*&& GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16*/) /*|| (m_cached_ctx.FRAME.Block() == m_cached_ctx.TEX0.TBP0 && ((m_cached_ctx.TEX0.PSM & 0x6) || m_cached_ctx.FRAME.PSM != m_cached_ctx.TEX0.PSM))*/) || is_possible_channel_shuffle);
|
||||
const u32 channel_shuffle_targets = is_possible_channel_shuffle ? EmulateChannelShuffle(nullptr, true) : ChannelFetch_NONE;
|
||||
const bool need_aem_color = GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].trbpp <= 24 && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].pal == 0 && ((NeedsBlending() && m_context->ALPHA.C == 0) || IsDiscardingDstAlpha()) && m_cached_ctx.TEXA.AEM;
|
||||
const u32 color_mask = (m_vt.m_max.c > GSVector4i::zero()).mask();
|
||||
const bool texture_function_color = m_cached_ctx.TEX0.TFX == TFX_DECAL || (color_mask & 0xFFF) || (m_cached_ctx.TEX0.TFX > TFX_DECAL && (color_mask & 0xF000));
|
||||
const bool texture_function_alpha = m_cached_ctx.TEX0.TFX != TFX_MODULATE || (color_mask & 0xF000);
|
||||
const bool req_color = (texture_function_color && (!PRIM->ABE || GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp < 16 || (NeedsBlending() && IsUsingCsInBlend())) && (possible_shuffle || (m_cached_ctx.FRAME.FBMSK & (fm_mask & 0x00FFFFFF)) != (fm_mask & 0x00FFFFFF))) || need_aem_color;
|
||||
const bool req_color = (is_possible_channel_shuffle && channel_shuffle_targets != ChannelFetch_ALPHA) || (!is_possible_channel_shuffle && ((texture_function_color && (!PRIM->ABE || GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp < 16 || (NeedsBlending() && IsUsingCsInBlend())) && (possible_shuffle || (m_cached_ctx.FRAME.FBMSK & (fm_mask & 0x00FFFFFF)) != (fm_mask & 0x00FFFFFF))) || need_aem_color));
|
||||
const bool alpha_used = (GSUtil::GetChannelMask(m_context->TEX0.PSM) == 0x8 || (m_context->TEX0.TCC && texture_function_alpha)) && ((NeedsBlending() && IsUsingAsInBlend()) || (m_cached_ctx.TEST.ATE && m_cached_ctx.TEST.ATST > ATST_ALWAYS) || (possible_shuffle || (m_cached_ctx.FRAME.FBMSK & (fm_mask & 0xFF000000)) != (fm_mask & 0xFF000000)));
|
||||
const bool req_alpha = (GSUtil::GetChannelMask(m_context->TEX0.PSM) & 0x8) && alpha_used;
|
||||
const bool req_alpha = (is_possible_channel_shuffle && channel_shuffle_targets == ChannelFetch_ALPHA) || (!is_possible_channel_shuffle && (GSUtil::GetChannelMask(m_context->TEX0.PSM) & 0x8) && alpha_used);
|
||||
|
||||
// TODO: Be able to send an alpha of 1.0 (blended with vertex alpha maybe?) so we can avoid sending the texture, since we don't always need it.
|
||||
// Example games: Evolution Snowboarding, Final Fantasy Dirge of Cerberus, Red Dead Revolver, Stuntman, Tony Hawk's Underground 2, Ultimate Spider-Man.
|
||||
@@ -3196,6 +3243,7 @@ void GSRendererHW::Draw()
|
||||
float target_scale = GetTextureScaleFactor();
|
||||
bool scaled_copy = false;
|
||||
int scale_draw = IsScalingDraw(src, m_primitive_covers_without_gaps != NoGapsType::GapsFound);
|
||||
m_downscale_source = false;
|
||||
|
||||
if (GSConfig.UserHacks_NativeScaling != GSNativeScaling::Off)
|
||||
{
|
||||
@@ -3229,8 +3277,6 @@ void GSRendererHW::Draw()
|
||||
scale_draw = 1;
|
||||
scaled_copy = true;
|
||||
}
|
||||
|
||||
m_downscale_source = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3277,7 +3323,7 @@ void GSRendererHW::Draw()
|
||||
ZBUF_TEX0.PSM = m_cached_ctx.ZBUF.PSM;
|
||||
|
||||
ds = g_texture_cache->LookupTarget(ZBUF_TEX0, t_size, target_scale, GSTextureCache::DepthStencil,
|
||||
m_cached_ctx.DepthWrite(), 0, false, force_preload, preserve_depth, preserve_depth, unclamped_draw_rect, IsPossibleChannelShuffle(), is_possible_mem_clear && ZBUF_TEX0.TBP0 != m_cached_ctx.FRAME.Block(), false,
|
||||
m_cached_ctx.DepthWrite(), 0, false, force_preload, preserve_depth, preserve_depth, unclamped_draw_rect, IsPossibleChannelShuffle(), is_possible_mem_clear && ZBUF_TEX0.TBP0 != m_cached_ctx.FRAME.Block(), !no_rt,
|
||||
src, nullptr, -1);
|
||||
|
||||
ZBUF_TEX0.TBW = m_channel_shuffle ? src->m_from_target_TEX0.TBW : m_cached_ctx.FRAME.FBW;
|
||||
@@ -3892,7 +3938,7 @@ void GSRendererHW::Draw()
|
||||
{
|
||||
const GSVector2i unscaled_size(ds->m_unscaled_size.x, ds->m_unscaled_size.y);
|
||||
ds->m_scale = 1;
|
||||
ds->ResizeTexture(ds->m_unscaled_size.x * target_scale, ds->m_unscaled_size.y * target_scale, true);
|
||||
ds->ResizeTexture(ds->m_unscaled_size.x * target_scale, ds->m_unscaled_size.y * target_scale, true, true, GSVector4i::loadh(ds->m_unscaled_size * target_scale));
|
||||
// Slightly abusing the texture resize.
|
||||
ds->m_scale = target_scale;
|
||||
ds->m_unscaled_size = unscaled_size;
|
||||
@@ -3904,7 +3950,6 @@ void GSRendererHW::Draw()
|
||||
|
||||
if (m_channel_shuffle)
|
||||
{
|
||||
m_last_channel_shuffle_fbp = rt->m_TEX0.TBP0;
|
||||
m_last_channel_shuffle_tbp = src->m_TEX0.TBP0;
|
||||
|
||||
// If it's a new target, we don't know where the end is as it's starting on a shuffle, so just do every shuffle following.
|
||||
@@ -4041,7 +4086,6 @@ void GSRendererHW::Draw()
|
||||
m_last_channel_shuffle_fbmsk = m_context->FRAME.FBMSK;
|
||||
if (rt)
|
||||
{
|
||||
m_last_channel_shuffle_fbp = rt->m_TEX0.TBP0;
|
||||
m_last_channel_shuffle_tbp = src->m_TEX0.TBP0;
|
||||
// Urban Chaos goes from Z16 to C32, so let's just use the rt's original end block.
|
||||
if (!src->m_from_target || GSLocalMemory::m_psm[src->m_from_target_TEX0.PSM].bpp != GSLocalMemory::m_psm[rt->m_TEX0.PSM].bpp)
|
||||
@@ -4268,7 +4312,7 @@ void GSRendererHW::Draw()
|
||||
if (rt)
|
||||
{
|
||||
const bool update_fbw = (FRAME_TEX0.TBW != rt->m_TEX0.TBW || rt->m_TEX0.TBW == 1) && !m_in_target_draw && (m_channel_shuffle && src->m_target) && (!NeedsBlending() || IsOpaque() || m_context->ALPHA.IsBlack());
|
||||
rt->m_TEX0.TBW = update_fbw ? ((src && src->m_from_target && src->m_32_bits_fmt) ? src->m_from_target->m_TEX0.TBW : FRAME_TEX0.TBW) : std::max(rt->m_TEX0.TBW, FRAME_TEX0.TBW);
|
||||
rt->m_TEX0.TBW = update_fbw ? ((src && src->m_from_target && src->m_from_target->m_32_bits_fmt) ? src->m_from_target->m_TEX0.TBW : FRAME_TEX0.TBW) : std::max(rt->m_TEX0.TBW, FRAME_TEX0.TBW);
|
||||
rt->m_TEX0.PSM = FRAME_TEX0.PSM;
|
||||
}
|
||||
if (ds)
|
||||
@@ -4740,6 +4784,10 @@ void GSRendererHW::Draw()
|
||||
// Limit to 2x the vertical height of the resolution (for double buffering)
|
||||
rt->UpdateValidity(real_rect, !frame_masked && (can_update_size || (real_rect.w <= (resolution.y * 2) && !m_texture_shuffle)));
|
||||
|
||||
if (m_channel_shuffle)
|
||||
{
|
||||
m_last_channel_shuffle_fbp = rt->m_TEX0.TBP0;
|
||||
}
|
||||
}
|
||||
|
||||
if (ds)
|
||||
@@ -5147,7 +5195,9 @@ void GSRendererHW::EmulateZbuffer(const GSTextureCache::Target* ds)
|
||||
m_conf.cb_vs.max_depth = GSVector2i(0xFFFFFFFF);
|
||||
//ps_cb.MaxDepth = GSVector4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
m_conf.ps.zclamp = 0;
|
||||
m_conf.ps.zfloor = !m_cached_ctx.ZBUF.ZMSK;
|
||||
m_conf.ps.zfloor = !m_vt.m_eq.z &&
|
||||
(m_vt.m_primclass == GS_TRIANGLE_CLASS || m_vt.m_primclass == GS_LINE_CLASS) &&
|
||||
(m_cached_ctx.DepthWrite() || (m_cached_ctx.DepthRead() && m_cached_ctx.TEST.ZTST == ZTST_GREATER));
|
||||
|
||||
if (clamp_z)
|
||||
{
|
||||
@@ -5341,13 +5391,13 @@ bool GSRendererHW::TestChannelShuffle(GSTextureCache::Target* src)
|
||||
const bool shuffle = m_channel_shuffle || IsPossibleChannelShuffle();
|
||||
|
||||
// This is a little redundant since it'll get called twice, but the only way to stop us wasting time on copies.
|
||||
m_channel_shuffle = (shuffle && EmulateChannelShuffle(src, true));
|
||||
m_channel_shuffle = (shuffle && EmulateChannelShuffle(src, true)) != 0;
|
||||
return m_channel_shuffle;
|
||||
}
|
||||
|
||||
__ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool test_only, GSTextureCache::Target* rt)
|
||||
__ri u32 GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool test_only, GSTextureCache::Target* rt)
|
||||
{
|
||||
if ((src->m_texture->GetType() == GSTexture::Type::DepthStencil) && !src->m_32_bits_fmt)
|
||||
if (src && (src->m_texture->GetType() == GSTexture::Type::DepthStencil) && !src->m_32_bits_fmt)
|
||||
{
|
||||
// So far 2 games hit this code path. Urban Chaos and Tales of Abyss
|
||||
// UC: will copy depth to green channel
|
||||
@@ -5357,7 +5407,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
|
||||
// Green channel is masked
|
||||
GL_INS("HW: HLE Shuffle Tales Of Abyss");
|
||||
if (test_only)
|
||||
return true;
|
||||
return ChannelFetch_RGB;
|
||||
|
||||
m_conf.ps.tales_of_abyss_hle = 1;
|
||||
}
|
||||
@@ -5365,7 +5415,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
|
||||
{
|
||||
GL_INS("HW: HLE Shuffle Urban Chaos");
|
||||
if (test_only)
|
||||
return true;
|
||||
return ChannelFetch_RGB;
|
||||
|
||||
m_conf.ps.urban_chaos_hle = 1;
|
||||
}
|
||||
@@ -5380,18 +5430,18 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
|
||||
// handled above.
|
||||
GL_INS("HW: Might not be channel shuffle");
|
||||
if (test_only)
|
||||
return false;
|
||||
return ChannelFetch_NONE;
|
||||
|
||||
m_channel_shuffle = false;
|
||||
return false;
|
||||
}
|
||||
else if (m_cached_ctx.CLAMP.WMS == 3 && ((m_cached_ctx.CLAMP.MAXU & 0x8) == 8))
|
||||
{
|
||||
const ChannelFetch channel_select = ((m_cached_ctx.CLAMP.WMT != 3 && (m_vertex.buff[m_index.buff[0]].V & 0x20) == 0) || (m_cached_ctx.CLAMP.WMT == 3 && ((m_cached_ctx.CLAMP.MAXV & 0x2) == 0))) ? ChannelFetch_BLUE : ChannelFetch_ALPHA;
|
||||
|
||||
// MGS3/Kill Zone
|
||||
if (test_only)
|
||||
return true;
|
||||
|
||||
const ChannelFetch channel_select = ((m_cached_ctx.CLAMP.WMT != 3 && (m_vertex.buff[m_index.buff[0]].V & 0x20) == 0) || (m_cached_ctx.CLAMP.WMT == 3 && ((m_cached_ctx.CLAMP.MAXV & 0x2) == 0))) ? ChannelFetch_BLUE : ChannelFetch_ALPHA;
|
||||
return channel_select;
|
||||
|
||||
GL_INS("HW: %s channel", (channel_select == ChannelFetch_BLUE) ? "blue" : "alpha");
|
||||
|
||||
@@ -5401,7 +5451,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
|
||||
{
|
||||
// Read either Red or Green. Let's check the V coordinate. 0-1 is likely top so
|
||||
// red. 2-3 is likely bottom so green (actually depends on texture base pointer offset)
|
||||
const bool green = PRIM->FST && (m_vertex.buff[0].V & 32);
|
||||
const bool green = (m_cached_ctx.CLAMP.WMT == 3 && ((m_cached_ctx.CLAMP.MAXV & 0x2) == 2)) || (PRIM->FST && (m_vertex.buff[0].V & 32));
|
||||
if (green && (m_cached_ctx.FRAME.FBMSK & 0x00FFFFFF) == 0x00FFFFFF)
|
||||
{
|
||||
// Typically used in Terminator 3
|
||||
@@ -5429,7 +5479,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
|
||||
|
||||
GL_INS("HW: Green/Blue channel (%d, %d)", blue_shift, green_shift);
|
||||
if (test_only)
|
||||
return true;
|
||||
return ChannelFetch_GXBY;
|
||||
|
||||
m_conf.cb_ps.ChannelShuffle = GSVector4i(blue_mask, blue_shift, green_mask, green_shift);
|
||||
m_conf.ps.channel = ChannelFetch_GXBY;
|
||||
@@ -5439,7 +5489,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
|
||||
{
|
||||
GL_INS("HW: Green channel (wrong mask) (fbmask %x)", blue_mask);
|
||||
if (test_only)
|
||||
return true;
|
||||
return ChannelFetch_GREEN;
|
||||
|
||||
m_conf.ps.channel = ChannelFetch_GREEN;
|
||||
}
|
||||
@@ -5448,7 +5498,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
|
||||
{
|
||||
GL_INS("HW: Green channel");
|
||||
if (test_only)
|
||||
return true;
|
||||
return ChannelFetch_GREEN;
|
||||
|
||||
m_conf.ps.channel = ChannelFetch_GREEN;
|
||||
}
|
||||
@@ -5457,7 +5507,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
|
||||
// Pop
|
||||
GL_INS("HW: Red channel");
|
||||
if (test_only)
|
||||
return true;
|
||||
return ChannelFetch_RED;
|
||||
|
||||
m_conf.ps.channel = ChannelFetch_RED;
|
||||
}
|
||||
@@ -5502,7 +5552,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
|
||||
#endif
|
||||
|
||||
if (test_only)
|
||||
return true;
|
||||
return channel;
|
||||
|
||||
m_conf.ps.channel = channel;
|
||||
}
|
||||
@@ -5512,7 +5562,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
|
||||
m_r.x, m_r.y, m_r.z, m_r.w, min_uv.x, min_uv.y);
|
||||
|
||||
if (test_only)
|
||||
return false;
|
||||
return ChannelFetch_NONE;
|
||||
|
||||
m_channel_shuffle = false;
|
||||
return false;
|
||||
@@ -5546,7 +5596,6 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
|
||||
s[1].V = 16384;
|
||||
|
||||
m_r = GSVector4i(0, 0, 1024, 1024);
|
||||
|
||||
// We need to count the pages that get shuffled to, some games (like Hitman Blood Money dialogue blur effects) only do half the screen.
|
||||
if (!m_full_screen_shuffle && !m_conf.ps.urban_chaos_hle && !m_conf.ps.tales_of_abyss_hle && src)
|
||||
{
|
||||
@@ -5556,21 +5605,14 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
|
||||
|
||||
m_channel_shuffle_width = src->m_TEX0.TBW;
|
||||
}
|
||||
|
||||
m_channel_shuffle_finish = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
const u32 frame_page_offset = std::max(static_cast<int>(((m_r.x / frame_psm.pgs.x) + (m_r.y / frame_psm.pgs.y) * rt->m_TEX0.TBW)), 0);
|
||||
m_r = GSVector4i(m_r.x & ~(frame_psm.pgs.x - 1), m_r.y & ~(frame_psm.pgs.y - 1), (m_r.z + (frame_psm.pgs.x - 1)) & ~(frame_psm.pgs.x - 1), (m_r.w + (frame_psm.pgs.y - 1)) & ~(frame_psm.pgs.y - 1));
|
||||
|
||||
// Hitman suffers from this, not sure on the exact scenario at the moment, but we need the barrier.
|
||||
if (NeedsBlending() && m_context->ALPHA.IsCdInBlend())
|
||||
{
|
||||
// Needed to enable IsFeedbackLoop.
|
||||
m_conf.ps.channel_fb = 1;
|
||||
// Assume no overlap when it's a channel shuffle, no need for full barriers.
|
||||
m_conf.require_one_barrier = true;
|
||||
}
|
||||
|
||||
// This is for offsetting the texture, however if the texture has a region clamp, we don't want to move it.
|
||||
// A good two test games for this is Ghost in the Shell (no region clamp) and Tekken 5 (offset clamp on shadows)
|
||||
if (rt && rt->m_TEX0.TBP0 == m_cached_ctx.FRAME.Block())
|
||||
@@ -5594,7 +5636,6 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
|
||||
s[1].U = m_r.z << 4;
|
||||
s[0].V = m_r.y << 4;
|
||||
s[1].V = m_r.w << 4;
|
||||
m_last_channel_shuffle_fbmsk = 0xFFFFFFFF;
|
||||
|
||||
// If we're doing per page copying, then set the valid 1 frame ahead if we're continuing, as this will save the target lookup making a new target for the new row.
|
||||
const u32 frame_offset = m_cached_ctx.FRAME.Block() + (IsPageCopy() ? 0x20 : 0);
|
||||
@@ -5613,13 +5654,15 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
|
||||
|
||||
new_valid.w = std::max(new_valid.w, offset_height);
|
||||
rt->UpdateValidity(new_valid, true);
|
||||
|
||||
m_channel_shuffle_finish = true;
|
||||
}
|
||||
|
||||
m_vertex.head = m_vertex.tail = m_vertex.next = 2;
|
||||
m_index.tail = 2;
|
||||
|
||||
m_primitive_covers_without_gaps = NoGapsType::FullCover;
|
||||
m_channel_shuffle_abort = false;
|
||||
m_conf.cb_ps.ChannelShuffleOffset = GSVector2(0, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -5867,6 +5910,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;
|
||||
@@ -6425,7 +6469,10 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,
|
||||
{
|
||||
// don't overwrite the texture when using channel shuffle, but keep the palette
|
||||
if (!m_channel_shuffle)
|
||||
{
|
||||
m_conf.cb_ps.ChannelShuffleOffset = GSVector2(0, 0);
|
||||
m_conf.tex = tex->m_texture;
|
||||
}
|
||||
m_conf.pal = tex->m_palette;
|
||||
|
||||
// Hazard handling (i.e. reading from the current RT/DS).
|
||||
@@ -6817,7 +6864,7 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
|
||||
const GSTextureCache::Target* src_target = nullptr;
|
||||
if (!m_downscale_source || !tex->m_from_target)
|
||||
{
|
||||
if (rt && m_conf.tex == m_conf.rt && !(m_channel_shuffle && tex && (tex_diff != frame_diff || target_region)))
|
||||
if (rt && m_conf.tex == m_conf.rt && !(m_channel_shuffle && tex && tex_diff != frame_diff))
|
||||
{
|
||||
// Can we read the framebuffer directly? (i.e. sample location matches up).
|
||||
if (CanUseTexIsFB(rt, tex, tmm))
|
||||
@@ -6900,37 +6947,63 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
|
||||
GSVector4i::storel(©_dst_offset, copy_range);
|
||||
if (m_channel_shuffle && (tex_diff || frame_diff))
|
||||
{
|
||||
const int page_offset = (m_cached_ctx.TEX0.TBP0 - src_target->m_TEX0.TBP0) >> 5;
|
||||
const int horizontal_offset = ((page_offset % src_target->m_TEX0.TBW) * GSLocalMemory::m_psm[src_target->m_TEX0.PSM].pgs.x);
|
||||
const int vertical_offset = ((page_offset / src_target->m_TEX0.TBW) * GSLocalMemory::m_psm[src_target->m_TEX0.PSM].pgs.y);
|
||||
|
||||
const u32 page_offset = (m_cached_ctx.TEX0.TBP0 - src_target->m_TEX0.TBP0) >> 5;
|
||||
const u32 horizontal_offset = (page_offset % src_target->m_TEX0.TBW) * GSLocalMemory::m_psm[src_target->m_TEX0.PSM].pgs.x;
|
||||
const u32 vertical_offset = (page_offset / src_target->m_TEX0.TBW) * GSLocalMemory::m_psm[src_target->m_TEX0.PSM].pgs.y;
|
||||
|
||||
copy_range.x += horizontal_offset;
|
||||
copy_range.y += vertical_offset;
|
||||
copy_range.z += horizontal_offset;
|
||||
copy_range.w += vertical_offset;
|
||||
|
||||
if (!m_channel_shuffle)
|
||||
if (g_gs_device->Features().texture_barrier || g_gs_device->Features().multidraw_fb_copy)
|
||||
{
|
||||
copy_size.y -= vertical_offset;
|
||||
copy_size.x -= horizontal_offset;
|
||||
}
|
||||
target_region = false;
|
||||
source_region.bits = 0;
|
||||
//copied_rt = tex->m_from_target != nullptr;
|
||||
if (m_in_target_draw && (page_offset || frame_diff))
|
||||
{
|
||||
copy_range.z = copy_range.x + m_r.width();
|
||||
copy_range.w = copy_range.y + m_r.height();
|
||||
const u32 max_skip = ((m_channel_shuffle_finish || !m_channel_shuffle_width) ? 1 : m_channel_shuffle_width) << 5;
|
||||
const bool new_shuffle = !(m_last_channel_shuffle_fbmsk == m_context->FRAME.FBMSK &&
|
||||
m_last_channel_shuffle_fbp <= m_context->FRAME.Block() && (m_last_channel_shuffle_fbp + max_skip) >= m_context->FRAME.Block() &&
|
||||
m_last_channel_shuffle_end_block > m_context->FRAME.Block() && m_last_channel_shuffle_tbp <= m_context->TEX0.TBP0 && (m_last_channel_shuffle_tbp + max_skip) >= m_context->TEX0.TBP0);
|
||||
|
||||
if (tex_diff != frame_diff)
|
||||
if (rt == tex->m_from_target && new_shuffle)
|
||||
{
|
||||
GSVector4i::storel(©_dst_offset, m_r);
|
||||
if (m_prim_overlap == PRIM_OVERLAP_NO)
|
||||
m_conf.require_one_barrier = true;
|
||||
else
|
||||
m_conf.require_full_barrier = true;
|
||||
}
|
||||
}
|
||||
|
||||
copy_range.z = std::min(copy_range.z, src_target->m_unscaled_size.x);
|
||||
copy_range.w = std::min(copy_range.w, src_target->m_unscaled_size.y);
|
||||
m_conf.cb_ps.ChannelShuffleOffset = GSVector2((horizontal_offset - m_r.x) * tex->GetScale(), (vertical_offset - m_r.y) * tex->GetScale());
|
||||
m_conf.ps.channel_fb = 1;
|
||||
target_region = false;
|
||||
source_region.bits = 0;
|
||||
|
||||
unscaled_size = src_target->GetUnscaledSize();
|
||||
scale = src_target->GetScale();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
copy_range.x += horizontal_offset;
|
||||
copy_range.y += vertical_offset;
|
||||
copy_range.z += horizontal_offset;
|
||||
copy_range.w += vertical_offset;
|
||||
|
||||
if (!m_channel_shuffle)
|
||||
{
|
||||
copy_size.y -= vertical_offset;
|
||||
copy_size.x -= horizontal_offset;
|
||||
}
|
||||
target_region = false;
|
||||
source_region.bits = 0;
|
||||
//copied_rt = tex->m_from_target != nullptr;
|
||||
if (m_in_target_draw && (page_offset || frame_diff))
|
||||
{
|
||||
copy_range.z = copy_range.x + m_r.width();
|
||||
copy_range.w = copy_range.y + m_r.height();
|
||||
|
||||
if (tex_diff != frame_diff)
|
||||
{
|
||||
GSVector4i::storel(©_dst_offset, m_r);
|
||||
}
|
||||
}
|
||||
|
||||
copy_range.z = std::min(copy_range.z, src_target->m_unscaled_size.x);
|
||||
copy_range.w = std::min(copy_range.w, src_target->m_unscaled_size.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -8892,17 +8965,17 @@ bool GSRendererHW::TryGSMemClear(bool no_rt, bool preserve_rt, bool invalidate_r
|
||||
if (m_r.width() < ((static_cast<int>(m_cached_ctx.FRAME.FBW) - 1) * 64))
|
||||
return false;
|
||||
|
||||
if (!no_rt && !preserve_rt)
|
||||
if (!no_rt && (!preserve_rt || (IsOpaque() && m_cached_ctx.FRAME.FBMSK)))
|
||||
{
|
||||
ClearGSLocalMemory(m_context->offset.fb, m_r, GetConstantDirectWriteMemClearColor());
|
||||
|
||||
if (invalidate_rt)
|
||||
if (invalidate_rt && !preserve_rt)
|
||||
{
|
||||
g_texture_cache->InvalidateVideoMem(m_context->offset.fb, m_r, false);
|
||||
g_texture_cache->InvalidateContainedTargets(
|
||||
GSLocalMemory::GetStartBlockAddress(
|
||||
m_cached_ctx.FRAME.Block(), m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.PSM, m_r),
|
||||
rt_end_bp, m_cached_ctx.FRAME.PSM, m_cached_ctx.FRAME.FBW);
|
||||
rt_end_bp, m_cached_ctx.FRAME.PSM, m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.FBMSK);
|
||||
|
||||
GSUploadQueue clear_queue;
|
||||
clear_queue.draw = s_n;
|
||||
@@ -8913,6 +8986,13 @@ bool GSRendererHW::TryGSMemClear(bool no_rt, bool preserve_rt, bool invalidate_r
|
||||
clear_queue.zero_clear = true;
|
||||
m_draw_transfers.push_back(clear_queue);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_texture_cache->InvalidateContainedTargets(
|
||||
GSLocalMemory::GetStartBlockAddress(
|
||||
m_cached_ctx.FRAME.Block(), m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.PSM, m_r),
|
||||
rt_end_bp, m_cached_ctx.FRAME.PSM, m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.FBMSK, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!no_ds && !preserve_z)
|
||||
@@ -8926,6 +9006,15 @@ bool GSRendererHW::TryGSMemClear(bool no_rt, bool preserve_rt, bool invalidate_r
|
||||
GSLocalMemory::GetStartBlockAddress(
|
||||
m_cached_ctx.ZBUF.Block(), m_cached_ctx.FRAME.FBW, m_cached_ctx.ZBUF.PSM, m_r),
|
||||
ds_end_bp, m_cached_ctx.ZBUF.PSM, m_cached_ctx.FRAME.FBW);
|
||||
|
||||
GSUploadQueue clear_queue;
|
||||
clear_queue.draw = s_n;
|
||||
clear_queue.rect = m_r;
|
||||
clear_queue.blit.DBP = m_cached_ctx.ZBUF.Block();
|
||||
clear_queue.blit.DBW = m_cached_ctx.FRAME.FBW;
|
||||
clear_queue.blit.DPSM = m_cached_ctx.ZBUF.PSM;
|
||||
clear_queue.zero_clear = true;
|
||||
m_draw_transfers.push_back(clear_queue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8944,6 +9033,7 @@ void GSRendererHW::ClearGSLocalMemory(const GSOffset& off, const GSVector4i& r,
|
||||
const int right = r.right;
|
||||
const int bottom = r.bottom;
|
||||
int top = r.top;
|
||||
u32 drawing_mask = GSLocalMemory::m_psm[psm].depth ? 0x0 : m_cached_ctx.FRAME.FBMSK;
|
||||
|
||||
// Process the page aligned region first, then fall back to anything which is not.
|
||||
// Since pages are linear in memory, we can do it basically with a vector memset.
|
||||
@@ -8959,22 +9049,34 @@ void GSRendererHW::ClearGSLocalMemory(const GSOffset& off, const GSVector4i& r,
|
||||
|
||||
if (format == GSLocalMemory::PSM_FMT_32)
|
||||
{
|
||||
const GSVector4i vcolor = GSVector4i(vert_color);
|
||||
const GSVector4i vcolor = GSVector4i(vert_color & ~drawing_mask);
|
||||
const u32 iterations_per_page = (pages_wide * pixels_per_page) / 4;
|
||||
const GSVector4i mask = GSVector4i(drawing_mask);
|
||||
pxAssert((off.bp() & (GS_BLOCKS_PER_PAGE - 1)) == 0);
|
||||
for (u32 current_page = off.bp() >> 5; top < page_aligned_bottom; top += pgs.y, current_page += fbw)
|
||||
{
|
||||
current_page &= (GS_MAX_PAGES - 1);
|
||||
GSVector4i* ptr = reinterpret_cast<GSVector4i*>(m_mem.vm8() + current_page * GS_PAGE_SIZE);
|
||||
GSVector4i* const ptr_end = ptr + iterations_per_page;
|
||||
while (ptr != ptr_end)
|
||||
*(ptr++) = vcolor;
|
||||
if (drawing_mask)
|
||||
{
|
||||
while (ptr != ptr_end)
|
||||
{
|
||||
*ptr = (*ptr & mask) | vcolor;
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (ptr != ptr_end)
|
||||
*(ptr++) = vcolor;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (format == GSLocalMemory::PSM_FMT_24)
|
||||
{
|
||||
const GSVector4i mask = GSVector4i::xff000000();
|
||||
const GSVector4i vcolor = GSVector4i(vert_color & 0x00ffffffu);
|
||||
const GSVector4i mask = GSVector4i::xff000000() | GSVector4i(drawing_mask);
|
||||
const GSVector4i vcolor = GSVector4i((vert_color & 0x00ffffffu) & ~drawing_mask);
|
||||
const u32 iterations_per_page = (pages_wide * pixels_per_page) / 4;
|
||||
pxAssert((off.bp() & (GS_BLOCKS_PER_PAGE - 1)) == 0);
|
||||
for (u32 current_page = off.bp() >> 5; top < page_aligned_bottom; top += pgs.y, current_page += fbw)
|
||||
@@ -8993,7 +9095,10 @@ void GSRendererHW::ClearGSLocalMemory(const GSOffset& off, const GSVector4i& r,
|
||||
{
|
||||
const u16 converted_color = ((vert_color >> 16) & 0x8000) | ((vert_color >> 9) & 0x7C00) |
|
||||
((vert_color >> 6) & 0x7E0) | ((vert_color >> 3) & 0x1F);
|
||||
const u16 converted_mask = ((drawing_mask >> 16) & 0x8000) | ((drawing_mask >> 9) & 0x7C00) |
|
||||
((drawing_mask >> 6) & 0x7E0) | ((drawing_mask >> 3) & 0x1F);
|
||||
const GSVector4i vcolor = GSVector4i::broadcast16(converted_color);
|
||||
const GSVector4i mask = GSVector4i::broadcast16(converted_mask);
|
||||
const u32 iterations_per_page = (pages_wide * pixels_per_page) / 8;
|
||||
pxAssert((off.bp() & (GS_BLOCKS_PER_PAGE - 1)) == 0);
|
||||
for (u32 current_page = off.bp() >> 5; top < page_aligned_bottom; top += pgs.y, current_page += fbw)
|
||||
@@ -9001,14 +9106,27 @@ void GSRendererHW::ClearGSLocalMemory(const GSOffset& off, const GSVector4i& r,
|
||||
current_page &= (GS_MAX_PAGES - 1);
|
||||
GSVector4i* ptr = reinterpret_cast<GSVector4i*>(m_mem.vm8() + current_page * GS_PAGE_SIZE);
|
||||
GSVector4i* const ptr_end = ptr + iterations_per_page;
|
||||
while (ptr != ptr_end)
|
||||
*(ptr++) = vcolor;
|
||||
if (converted_mask)
|
||||
{
|
||||
while (ptr != ptr_end)
|
||||
{
|
||||
*ptr = (*ptr & mask) | vcolor;
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (ptr != ptr_end)
|
||||
*(ptr++) = vcolor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (format == GSLocalMemory::PSM_FMT_32)
|
||||
{
|
||||
const u32 mask = drawing_mask;
|
||||
const u32 vcolor = vert_color & ~mask;
|
||||
// Based on WritePixel32
|
||||
u32* vm = m_mem.vm32();
|
||||
for (int y = top; y < bottom; y++)
|
||||
@@ -9016,25 +9134,28 @@ void GSRendererHW::ClearGSLocalMemory(const GSOffset& off, const GSVector4i& r,
|
||||
GSOffset::PAHelper pa = off.assertSizesMatch(GSLocalMemory::swizzle32).paMulti(0, y);
|
||||
|
||||
for (int x = left; x < right; x++)
|
||||
vm[pa.value(x)] = vert_color;
|
||||
vm[pa.value(x)] = vcolor | (vm[pa.value(x)] & mask);
|
||||
}
|
||||
}
|
||||
else if (format == GSLocalMemory::PSM_FMT_24)
|
||||
{
|
||||
// Based on WritePixel24
|
||||
u32* vm = m_mem.vm32();
|
||||
const u32 write_color = vert_color & 0xffffffu;
|
||||
const u32 mask = drawing_mask | 0xff000000u;
|
||||
const u32 write_color = (vert_color & 0xffffffu) & ~mask;
|
||||
for (int y = top; y < bottom; y++)
|
||||
{
|
||||
GSOffset::PAHelper pa = off.assertSizesMatch(GSLocalMemory::swizzle32).paMulti(0, y);
|
||||
|
||||
for (int x = left; x < right; x++)
|
||||
vm[pa.value(x)] = (vm[pa.value(x)] & 0xff000000u) | write_color;
|
||||
vm[pa.value(x)] = (vm[pa.value(x)] & mask) | write_color;
|
||||
}
|
||||
}
|
||||
else if (format == GSLocalMemory::PSM_FMT_16)
|
||||
{
|
||||
const u16 converted_color = ((vert_color >> 16) & 0x8000) | ((vert_color >> 9) & 0x7C00) | ((vert_color >> 6) & 0x7E0) | ((vert_color >> 3) & 0x1F);
|
||||
const u16 converted_mask = ((drawing_mask >> 16) & 0x8000) | ((drawing_mask >> 9) & 0x7C00) |
|
||||
((drawing_mask >> 6) & 0x7E0) | ((drawing_mask >> 3) & 0x1F);
|
||||
const u16 converted_color = (((vert_color >> 16) & 0x8000) | ((vert_color >> 9) & 0x7C00) | ((vert_color >> 6) & 0x7E0) | ((vert_color >> 3) & 0x1F)) & ~converted_mask;
|
||||
|
||||
// Based on WritePixel16
|
||||
u16* vm = m_mem.vm16();
|
||||
@@ -9043,7 +9164,7 @@ void GSRendererHW::ClearGSLocalMemory(const GSOffset& off, const GSVector4i& r,
|
||||
GSOffset::PAHelper pa = off.assertSizesMatch(GSLocalMemory::swizzle16).paMulti(0, y);
|
||||
|
||||
for (int x = left; x < right; x++)
|
||||
vm[pa.value(x)] = converted_color;
|
||||
vm[pa.value(x)] = converted_color | (vm[pa.value(x)] & converted_mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9070,8 +9191,9 @@ bool GSRendererHW::OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Sourc
|
||||
// Bottom of Texture (half height frame, will be the copy of Top texture after the draw)
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
const int tw = static_cast<int>(1 << m_cached_ctx.TEX0.TW);
|
||||
const int th = static_cast<int>(1 << m_cached_ctx.TEX0.TH);
|
||||
// Do not use tw and th, if it has been optimized to not be the TEX0 size, it will crash.
|
||||
const int tw = tex->m_unscaled_size.x;
|
||||
int th = tex->m_unscaled_size.y;
|
||||
|
||||
// Compute the Bottom of texture rectangle
|
||||
pxAssert(m_cached_ctx.TEX0.TBP0 > m_cached_ctx.FRAME.Block());
|
||||
@@ -9079,30 +9201,48 @@ bool GSRendererHW::OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Sourc
|
||||
GSVector4i r_texture(r_draw);
|
||||
r_texture.y -= offset;
|
||||
r_texture.w -= offset;
|
||||
const int new_height = std::max(r_texture.w, th);
|
||||
|
||||
if (GSTexture* rt = g_gs_device->CreateRenderTarget(tw, th, GSTexture::Format::Color))
|
||||
GSTexture* temp_tex = g_gs_device->CreateTexture(tw, new_height, 1, tex->m_texture->GetFormat(), true);
|
||||
|
||||
if (temp_tex)
|
||||
{
|
||||
// sRect is the top of texture
|
||||
// Need to half pixel offset the dest tex coordinates as draw pixels are top left instead of centre for texel reads.
|
||||
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 GSVector4 dRect = GSVector4(r_texture) + GSVector4(0.5f);
|
||||
const GSVector4i r_full(0, 0, tw, th);
|
||||
if (GSTexture* rt = g_gs_device->CreateRenderTarget(tw, new_height, GSTexture::Format::Color))
|
||||
{
|
||||
// sRect is the top of texture
|
||||
// Need to half pixel offset the dest tex coordinates as draw pixels are top left instead of centre for texel reads.
|
||||
const GSVector4 dRect = GSVector4(r_texture) + GSVector4(0.5f);
|
||||
const GSVector4i r_full(0, 0, tw, th);
|
||||
|
||||
g_gs_device->CopyRect(tex->m_texture, rt, r_full, 0, 0);
|
||||
g_gs_device->CopyRect(tex->m_texture, rt, r_full, 0, 0);
|
||||
|
||||
g_gs_device->StretchRect(tex->m_texture, sRect, rt, dRect, ShaderConvert::COPY, m_vt.IsRealLinear());
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
g_texture_cache->ReplaceSourceTexture(tex, temp_tex, tex->m_scale, GSVector2i(tw, new_height), nullptr, false);
|
||||
|
||||
g_gs_device->CopyRect(rt, tex->m_texture, r_full, 0, 0);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
if (tex->m_region.HasY())
|
||||
{
|
||||
if (tex->m_region.GetMaxY() == th)
|
||||
{
|
||||
tex->m_region.bits &= ~(0xFFFF0000ULL << 32);
|
||||
tex->m_region.SetY(tex->m_region.GetMinY(), new_height);
|
||||
}
|
||||
}
|
||||
th = new_height;
|
||||
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);
|
||||
}
|
||||
|
||||
// Copy back the texture into the GS mem. I don't know why but it will be
|
||||
// reuploaded again later
|
||||
g_texture_cache->Read(tex, r_texture.rintersect(tex->m_texture->GetRect()));
|
||||
|
||||
g_gs_device->Recycle(rt);
|
||||
}
|
||||
|
||||
// Copy back the texture into the GS mem. I don't know why but it will be
|
||||
// reuploaded again later
|
||||
g_texture_cache->Read(tex, r_texture.rintersect(tex->m_texture->GetRect()));
|
||||
|
||||
g_texture_cache->InvalidateVideoMemSubTarget(_rt);
|
||||
|
||||
return false; // skip current draw
|
||||
|
||||
@@ -96,7 +96,7 @@ private:
|
||||
void HandleProvokingVertexFirst();
|
||||
void SetupIA(float target_scale, float sx, float sy, bool req_vert_backup);
|
||||
void EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt, GSTextureCache::Source* tex);
|
||||
bool EmulateChannelShuffle(GSTextureCache::Target* src, bool test_only, GSTextureCache::Target* rt = nullptr);
|
||||
u32 EmulateChannelShuffle(GSTextureCache::Target* src, bool test_only, GSTextureCache::Target* rt = nullptr);
|
||||
void EmulateBlending(int rt_alpha_min, int rt_alpha_max, const bool DATE, bool& DATE_PRIMID, bool& DATE_BARRIER, GSTextureCache::Target* rt,
|
||||
bool can_scale_rt_alpha, bool& new_rt_alpha_scale);
|
||||
void CleanupDraw(bool invalidate_temp_src);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -32,8 +32,18 @@ public:
|
||||
|
||||
constexpr static bool CheckOverlap(const u32 a_bp, const u32 a_bp_end, const u32 b_bp, const u32 b_bp_end) noexcept
|
||||
{
|
||||
const bool valid = a_bp <= a_bp_end && b_bp <= b_bp_end;
|
||||
const bool overlap = a_bp <= b_bp_end && a_bp_end >= b_bp;
|
||||
u32 b_bp_start_synced = b_bp;
|
||||
u32 b_bp_end_synced = b_bp_end;
|
||||
|
||||
// Check for wrapping
|
||||
if (a_bp_end > GS_MAX_BLOCKS && b_bp_end < a_bp)
|
||||
{
|
||||
b_bp_start_synced += GS_MAX_BLOCKS;
|
||||
b_bp_end_synced += GS_MAX_BLOCKS;
|
||||
}
|
||||
|
||||
const bool valid = a_bp <= a_bp_end && b_bp_start_synced <= b_bp_end_synced;
|
||||
const bool overlap = a_bp <= b_bp_end_synced && a_bp_end >= b_bp_start_synced;
|
||||
return valid && overlap;
|
||||
}
|
||||
|
||||
@@ -268,7 +278,7 @@ public:
|
||||
void UpdateValidChannels(u32 psm, u32 fbmsk);
|
||||
|
||||
/// Resizes target texture, DOES NOT RESCALE.
|
||||
bool ResizeTexture(int new_unscaled_width, int new_unscaled_height, bool recycle_old = true, bool require_offset = false, GSVector4i offset = GSVector4i::zero(), bool keep_old = false);
|
||||
bool ResizeTexture(int new_unscaled_width, int new_unscaled_height, bool recycle_old = true, bool require_new_rect = false, GSVector4i new_rect = GSVector4i::zero(), bool keep_old = false);
|
||||
|
||||
private:
|
||||
void UpdateTextureDebugName();
|
||||
@@ -445,7 +455,7 @@ protected:
|
||||
std::unique_ptr<GSDownloadTexture> m_uint16_download_texture;
|
||||
std::unique_ptr<GSDownloadTexture> m_uint32_download_texture;
|
||||
|
||||
Source* CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* t, int x_offset, int y_offset, const GSVector2i* lod, const GSVector4i* src_range, GSTexture* gpu_clut, SourceRegion region, bool force_temporary = false);
|
||||
Source* CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, Target* t, int x_offset, int y_offset, const GSVector2i* lod, const GSVector4i* src_range, GSTexture* gpu_clut, SourceRegion region, bool force_temporary = false);
|
||||
|
||||
bool PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size, bool is_frame,
|
||||
bool preload, bool preserve_target, const GSVector4i draw_rect, Target* dst, GSTextureCache::Source* src = nullptr);
|
||||
@@ -522,7 +532,7 @@ public:
|
||||
bool HasTargetInHeightCache(u32 bp, u32 fbw, u32 psm, u32 max_age = std::numeric_limits<u32>::max(), bool move_front = true);
|
||||
bool Has32BitTarget(u32 bp);
|
||||
|
||||
void InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 write_psm = PSMCT32, u32 write_bw = 1);
|
||||
void InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 write_psm = PSMCT32, u32 write_bw = 1, u32 fb_mask = 0x00000000, bool ignore_exact = false);
|
||||
void InvalidateVideoMemType(int type, u32 bp, u32 write_psm = PSMCT32, u32 write_fbmsk = 0, bool dirty_only = false);
|
||||
void InvalidateVideoMemSubTarget(GSTextureCache::Target* rt);
|
||||
void InvalidateVideoMem(const GSOffset& off, const GSVector4i& r, bool target = true);
|
||||
|
||||
@@ -2081,6 +2081,7 @@ static_assert(offsetof(GSHWDrawConfig::PSConstantBuffer, HalfTexel) == of
|
||||
static_assert(offsetof(GSHWDrawConfig::PSConstantBuffer, MinMax) == offsetof(GSMTLMainPSUniform, uv_min_max));
|
||||
static_assert(offsetof(GSHWDrawConfig::PSConstantBuffer, STRange) == offsetof(GSMTLMainPSUniform, st_range));
|
||||
static_assert(offsetof(GSHWDrawConfig::PSConstantBuffer, ChannelShuffle) == offsetof(GSMTLMainPSUniform, channel_shuffle));
|
||||
static_assert(offsetof(GSHWDrawConfig::PSConstantBuffer, ChannelShuffleOffset) == offsetof(GSMTLMainPSUniform, channel_shuffle_offset));
|
||||
static_assert(offsetof(GSHWDrawConfig::PSConstantBuffer, TCOffsetHack) == offsetof(GSMTLMainPSUniform, tc_offset));
|
||||
static_assert(offsetof(GSHWDrawConfig::PSConstantBuffer, STScale) == offsetof(GSMTLMainPSUniform, st_scale));
|
||||
static_assert(offsetof(GSHWDrawConfig::PSConstantBuffer, DitherMatrix) == offsetof(GSMTLMainPSUniform, dither_matrix));
|
||||
|
||||
@@ -129,6 +129,7 @@ struct GSMTLMainPSUniform
|
||||
unsigned int green_mask;
|
||||
unsigned int green_shift;
|
||||
} channel_shuffle;
|
||||
vector_float2 channel_shuffle_offset;
|
||||
vector_float2 tc_offset;
|
||||
vector_float2 st_scale;
|
||||
matrix_float4x4 dither_matrix;
|
||||
|
||||
@@ -509,7 +509,7 @@ struct PSMain
|
||||
|
||||
uint fetch_raw_depth()
|
||||
{
|
||||
return tex_depth.read(ushort2(in.p.xy)) * 0x1p32f;
|
||||
return tex_depth.read(ushort2(in.p.xy + cb.channel_shuffle_offset)) * 0x1p32f;
|
||||
}
|
||||
|
||||
float4 fetch_raw_color()
|
||||
@@ -517,7 +517,7 @@ struct PSMain
|
||||
if (PS_TEX_IS_FB)
|
||||
return current_color;
|
||||
else
|
||||
return tex.read(ushort2(in.p.xy));
|
||||
return tex.read(ushort2(in.p.xy + cb.channel_shuffle_offset));
|
||||
}
|
||||
|
||||
float4 fetch_c(ushort2 uv)
|
||||
@@ -790,7 +790,7 @@ struct PSMain
|
||||
void fog(thread float4& C, float f)
|
||||
{
|
||||
if (PS_FOG)
|
||||
C.rgb = trunc(mix(cb.fog_color, C.rgb, f));
|
||||
C.rgb = trunc(mix(cb.fog_color, C.rgb, (f * 255.0f) / 256.0f));
|
||||
}
|
||||
|
||||
float4 ps_color()
|
||||
|
||||
@@ -770,6 +770,11 @@ bool GSDeviceOGL::CheckFeatures()
|
||||
m_features.line_expand ? "hardware" : (m_features.vs_expand ? "vertex expanding" : "UNSUPPORTED"),
|
||||
m_features.vs_expand ? "vertex expanding" : "CPU");
|
||||
|
||||
if (!GLAD_GL_ARB_conservative_depth)
|
||||
{
|
||||
Console.Warning("GLAD_GL_ARB_conservative_depth is not supported. This will reduce performance.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1293,6 +1298,16 @@ std::string GSDeviceOGL::GenGlslHeader(const std::string_view entry, GLenum type
|
||||
else
|
||||
header += "#define HAS_FRAMEBUFFER_FETCH 0\n";
|
||||
|
||||
if (GLAD_GL_ARB_conservative_depth)
|
||||
{
|
||||
header += "#extension GL_ARB_conservative_depth : enable\n";
|
||||
header += "#define HAS_CONSERVATIVE_DEPTH 1\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
header += "#define HAS_CONSERVATIVE_DEPTH 0\n";
|
||||
}
|
||||
|
||||
// Allow to puts several shader in 1 files
|
||||
switch (type)
|
||||
{
|
||||
|
||||
@@ -481,72 +481,26 @@ bool GSDeviceVK::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(m_physical_device, &queue_family_count, queue_family_properties.data());
|
||||
DevCon.WriteLn("%u vulkan queue families", queue_family_count);
|
||||
|
||||
std::vector<uint32_t> queue_family_users(queue_family_count, 0);
|
||||
|
||||
// Find graphics and present queues.
|
||||
m_graphics_queue_family_index = queue_family_count;
|
||||
m_present_queue_family_index = queue_family_count;
|
||||
u32 present_queue_index = 0;
|
||||
m_spin_queue_family_index = queue_family_count;
|
||||
u32 spin_queue_index = 0;
|
||||
|
||||
// Graphics Queue
|
||||
for (uint32_t i = 0; i < queue_family_count; i++)
|
||||
{
|
||||
if (queue_family_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
|
||||
VkBool32 graphics_supported = queue_family_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT;
|
||||
if (graphics_supported)
|
||||
{
|
||||
m_graphics_queue_family_index = i;
|
||||
queue_family_users[i]++;
|
||||
break;
|
||||
// Quit now, no need for a present queue.
|
||||
if (!surface)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Spinwait Queue
|
||||
for (uint32_t i = 0; i < queue_family_count; i++)
|
||||
{
|
||||
if (queue_family_properties[i].queueCount == queue_family_users[i])
|
||||
continue;
|
||||
if (!(queue_family_properties[i].queueFlags & VK_QUEUE_COMPUTE_BIT))
|
||||
continue;
|
||||
if (queue_family_properties[i].timestampValidBits == 0)
|
||||
continue; // We need timing
|
||||
|
||||
if (!(queue_family_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT))
|
||||
if (surface)
|
||||
{
|
||||
m_spin_queue_family_index = i;
|
||||
break;
|
||||
}
|
||||
else if (m_spin_queue_family_index == queue_family_count)
|
||||
m_spin_queue_family_index = i;
|
||||
}
|
||||
|
||||
if (m_spin_queue_family_index != queue_family_count)
|
||||
{
|
||||
spin_queue_index = queue_family_users[m_spin_queue_family_index];
|
||||
queue_family_users[m_spin_queue_family_index]++;
|
||||
m_spin_queue_is_graphics_queue = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No spare queue? Try the graphics queue.
|
||||
if ((queue_family_properties[m_graphics_queue_family_index].queueFlags & VK_QUEUE_COMPUTE_BIT) &&
|
||||
(queue_family_properties[m_graphics_queue_family_index].timestampValidBits != 0))
|
||||
{
|
||||
m_spin_queue_family_index = m_graphics_queue_family_index;
|
||||
spin_queue_index = 0;
|
||||
m_spin_queue_is_graphics_queue = true;
|
||||
}
|
||||
else
|
||||
m_spin_queue_is_graphics_queue = false;
|
||||
}
|
||||
|
||||
// Present Queue
|
||||
if (surface)
|
||||
{
|
||||
for (uint32_t i = 0; i < queue_family_count; i++)
|
||||
{
|
||||
if (queue_family_properties[i].queueCount == queue_family_users[i])
|
||||
continue;
|
||||
|
||||
VkBool32 present_supported;
|
||||
VkResult res = vkGetPhysicalDeviceSurfaceSupportKHR(m_physical_device, i, surface, &present_supported);
|
||||
if (res != VK_SUCCESS)
|
||||
@@ -555,48 +509,35 @@ bool GSDeviceVK::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!present_supported)
|
||||
continue;
|
||||
|
||||
// Perfer aync compute queue
|
||||
if ((queue_family_properties[i].queueFlags & VK_QUEUE_COMPUTE_BIT) &&
|
||||
!(queue_family_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT))
|
||||
{
|
||||
m_present_queue_family_index = i;
|
||||
break;
|
||||
}
|
||||
else if (m_present_queue_family_index == queue_family_count)
|
||||
m_present_queue_family_index = i;
|
||||
}
|
||||
|
||||
if (m_present_queue_family_index != queue_family_count)
|
||||
{
|
||||
present_queue_index = queue_family_users[m_present_queue_family_index];
|
||||
queue_family_users[m_present_queue_family_index]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No spare queue? Try the graphics queue.
|
||||
VkBool32 present_supported;
|
||||
VkResult res = vkGetPhysicalDeviceSurfaceSupportKHR(m_physical_device, m_graphics_queue_family_index, surface, &present_supported);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceSupportKHR failed: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (present_supported)
|
||||
{
|
||||
m_present_queue_family_index = m_graphics_queue_family_index;
|
||||
present_queue_index = 0;
|
||||
m_present_queue_family_index = i;
|
||||
}
|
||||
|
||||
// Prefer one queue family index that does both graphics and present.
|
||||
if (graphics_supported && present_supported)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Swap spin and present to simplify queue priorities logic.
|
||||
if (!m_spin_queue_is_graphics_queue && m_present_queue_family_index == m_spin_queue_family_index)
|
||||
std::swap(spin_queue_index, present_queue_index);
|
||||
|
||||
for (uint32_t i = 0; i < queue_family_count; i++)
|
||||
{
|
||||
// Pick a queue for spinning
|
||||
if (!(queue_family_properties[i].queueFlags & VK_QUEUE_COMPUTE_BIT))
|
||||
continue; // We need compute
|
||||
if (queue_family_properties[i].timestampValidBits == 0)
|
||||
continue; // We need timing
|
||||
const bool queue_is_used = i == m_graphics_queue_family_index || i == m_present_queue_family_index;
|
||||
if (queue_is_used && m_spin_queue_family_index != queue_family_count)
|
||||
continue; // Found a non-graphics queue to use
|
||||
spin_queue_index = 0;
|
||||
m_spin_queue_family_index = i;
|
||||
if (queue_is_used && queue_family_properties[i].queueCount > 1)
|
||||
spin_queue_index = 1;
|
||||
if (!(queue_family_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT))
|
||||
break; // Async compute queue, definitely pick this one
|
||||
}
|
||||
if (m_graphics_queue_family_index == queue_family_count)
|
||||
{
|
||||
Console.Error("VK: Failed to find an acceptable graphics queue.");
|
||||
@@ -614,16 +555,14 @@ bool GSDeviceVK::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer
|
||||
device_info.flags = 0;
|
||||
device_info.queueCreateInfoCount = 0;
|
||||
|
||||
// Low priority for the spin queue
|
||||
static constexpr float queue_priorities[] = {1.0f, 1.0f, 0.0f};
|
||||
|
||||
static constexpr float queue_priorities[] = {1.0f, 0.0f}; // Low priority for the spin queue
|
||||
std::array<VkDeviceQueueCreateInfo, 3> queue_infos;
|
||||
VkDeviceQueueCreateInfo& graphics_queue_info = queue_infos[device_info.queueCreateInfoCount++];
|
||||
graphics_queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||
graphics_queue_info.pNext = nullptr;
|
||||
graphics_queue_info.flags = 0;
|
||||
graphics_queue_info.queueFamilyIndex = m_graphics_queue_family_index;
|
||||
graphics_queue_info.queueCount = queue_family_users[m_graphics_queue_family_index];
|
||||
graphics_queue_info.queueCount = 1;
|
||||
graphics_queue_info.pQueuePriorities = queue_priorities;
|
||||
|
||||
if (surface != VK_NULL_HANDLE && m_graphics_queue_family_index != m_present_queue_family_index)
|
||||
@@ -633,19 +572,19 @@ bool GSDeviceVK::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer
|
||||
present_queue_info.pNext = nullptr;
|
||||
present_queue_info.flags = 0;
|
||||
present_queue_info.queueFamilyIndex = m_present_queue_family_index;
|
||||
present_queue_info.queueCount = queue_family_users[m_present_queue_family_index];
|
||||
present_queue_info.queueCount = 1;
|
||||
present_queue_info.pQueuePriorities = queue_priorities;
|
||||
}
|
||||
|
||||
if (m_spin_queue_family_index == m_graphics_queue_family_index)
|
||||
{
|
||||
if (spin_queue_index == 1)
|
||||
graphics_queue_info.pQueuePriorities = queue_priorities + 1;
|
||||
if (spin_queue_index != 0)
|
||||
graphics_queue_info.queueCount = 2;
|
||||
}
|
||||
else if (m_spin_queue_family_index == m_present_queue_family_index)
|
||||
{
|
||||
if (spin_queue_index == 1)
|
||||
queue_infos[1].pQueuePriorities = queue_priorities + 1;
|
||||
if (spin_queue_index != 0)
|
||||
queue_infos[1].queueCount = 2; // present queue
|
||||
}
|
||||
else if (m_spin_queue_family_index != queue_family_count)
|
||||
{
|
||||
@@ -655,7 +594,7 @@ bool GSDeviceVK::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer
|
||||
spin_queue_info.flags = 0;
|
||||
spin_queue_info.queueFamilyIndex = m_spin_queue_family_index;
|
||||
spin_queue_info.queueCount = 1;
|
||||
spin_queue_info.pQueuePriorities = queue_priorities + 2;
|
||||
spin_queue_info.pQueuePriorities = queue_priorities + 1;
|
||||
}
|
||||
|
||||
device_info.pQueueCreateInfos = queue_infos.data();
|
||||
@@ -744,11 +683,13 @@ bool GSDeviceVK::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer
|
||||
vkGetDeviceQueue(m_device, m_graphics_queue_family_index, 0, &m_graphics_queue);
|
||||
if (surface)
|
||||
{
|
||||
vkGetDeviceQueue(m_device, m_present_queue_family_index, present_queue_index, &m_present_queue);
|
||||
vkGetDeviceQueue(m_device, m_present_queue_family_index, 0, &m_present_queue);
|
||||
}
|
||||
m_spinning_supported = m_spin_queue_family_index != queue_family_count &&
|
||||
queue_family_properties[m_graphics_queue_family_index].timestampValidBits > 0 &&
|
||||
m_device_properties.limits.timestampPeriod > 0;
|
||||
m_spin_queue_is_graphics_queue =
|
||||
m_spin_queue_family_index == m_graphics_queue_family_index && spin_queue_index == 0;
|
||||
|
||||
m_gpu_timing_supported = (m_device_properties.limits.timestampComputeAndGraphics != 0 &&
|
||||
queue_family_properties[m_graphics_queue_family_index].timestampValidBits > 0 &&
|
||||
@@ -1354,7 +1295,7 @@ void GSDeviceVK::SubmitCommandBuffer(VKSwapChain* present_swap_chain)
|
||||
|
||||
present_swap_chain->ResetImageAcquireResult();
|
||||
|
||||
const VkResult res = vkQueuePresentKHR(m_present_queue, &present_info);
|
||||
res = vkQueuePresentKHR(m_present_queue, &present_info);
|
||||
if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR)
|
||||
{
|
||||
// VK_ERROR_OUT_OF_DATE_KHR is not fatal, just means we need to recreate our swap chain.
|
||||
@@ -3190,7 +3131,7 @@ void GSDeviceVK::UpdateCLUTTexture(
|
||||
GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize)
|
||||
{
|
||||
// Super annoying, but apparently NVIDIA doesn't like floats/ints packed together in the same vec4?
|
||||
struct Uniforms
|
||||
struct alignas(16) Uniforms
|
||||
{
|
||||
u32 offsetX, offsetY, dOffset, pad1;
|
||||
float scale;
|
||||
@@ -3209,7 +3150,7 @@ void GSDeviceVK::UpdateCLUTTexture(
|
||||
void GSDeviceVK::ConvertToIndexedTexture(
|
||||
GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM)
|
||||
{
|
||||
struct Uniforms
|
||||
struct alignas(16) Uniforms
|
||||
{
|
||||
u32 SBW;
|
||||
u32 DBW;
|
||||
@@ -3230,7 +3171,7 @@ void GSDeviceVK::ConvertToIndexedTexture(
|
||||
|
||||
void GSDeviceVK::FilteredDownsampleTexture(GSTexture* sTex, GSTexture* dTex, u32 downsample_factor, const GSVector2i& clamp_min, const GSVector4& dRect)
|
||||
{
|
||||
struct Uniforms
|
||||
struct alignas(16) Uniforms
|
||||
{
|
||||
GSVector2i clamp_min;
|
||||
int downsample_factor;
|
||||
@@ -4493,16 +4434,13 @@ void GSDeviceVK::RenderImGui()
|
||||
|
||||
UpdateImGuiTextures();
|
||||
|
||||
const float uniforms[2][2] = {{
|
||||
2.0f / static_cast<float>(m_window_info.surface_width),
|
||||
2.0f / static_cast<float>(m_window_info.surface_height),
|
||||
},
|
||||
{
|
||||
-1.0f,
|
||||
-1.0f,
|
||||
}};
|
||||
const GSVector4 uniforms(
|
||||
2.0f / static_cast<float>(m_window_info.surface_width),
|
||||
2.0f / static_cast<float>(m_window_info.surface_height),
|
||||
-1.0f,
|
||||
-1.0f);
|
||||
|
||||
SetUtilityPushConstants(uniforms, sizeof(uniforms));
|
||||
SetUtilityPushConstants(&uniforms, sizeof(uniforms));
|
||||
SetPipeline(m_imgui_pipeline);
|
||||
|
||||
if (m_utility_sampler != m_linear_sampler)
|
||||
|
||||
@@ -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,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
496
pcsx2/ImGui/FullscreenUI_Internal.h
Normal file
496
pcsx2/ImGui/FullscreenUI_Internal.h
Normal file
@@ -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
|
||||
5770
pcsx2/ImGui/FullscreenUI_Settings.cpp
Normal file
5770
pcsx2/ImGui/FullscreenUI_Settings.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1923,7 +1923,6 @@ Pcsx2Config::Pcsx2Config()
|
||||
{
|
||||
bitset = 0;
|
||||
// Set defaults for fresh installs / reset settings
|
||||
McdFolderAutoManage = true;
|
||||
EnablePatches = true;
|
||||
EnableFastBoot = true;
|
||||
EnableRecordingTools = true;
|
||||
@@ -1979,7 +1978,6 @@ void Pcsx2Config::LoadSaveCore(SettingsWrapper& wrap)
|
||||
SettingsWrapBitBool(HostFs);
|
||||
|
||||
SettingsWrapBitBool(BackupSavestate);
|
||||
SettingsWrapBitBool(McdFolderAutoManage);
|
||||
|
||||
SettingsWrapBitBool(WarnAboutUnsafeSettings);
|
||||
|
||||
|
||||
@@ -611,7 +611,7 @@ void FileMcd_EmuOpen()
|
||||
|
||||
|
||||
Mcd::impl.Open();
|
||||
Mcd::implFolder.SetFiltering(EmuConfig.McdFolderAutoManage);
|
||||
Mcd::implFolder.SetFiltering(true);
|
||||
Mcd::implFolder.Open();
|
||||
}
|
||||
|
||||
@@ -823,7 +823,7 @@ int FileMcd_ReIndex(uint port, uint slot, const std::string& filter)
|
||||
// return Mcd::impl.ReIndex( combinedSlot, filter );
|
||||
// break;
|
||||
case MemoryCardType::Folder:
|
||||
if (!Mcd::implFolder.ReIndex(combinedSlot, EmuConfig.McdFolderAutoManage, filter))
|
||||
if (!Mcd::implFolder.ReIndex(combinedSlot, true, filter))
|
||||
return -1;
|
||||
break;
|
||||
default:
|
||||
@@ -930,7 +930,7 @@ std::vector<AvailableMcdInfo> FileMcd_GetAvailableCards(bool include_in_use_card
|
||||
Pcsx2Config::McdOptions config;
|
||||
config.Enabled = true;
|
||||
config.Type = MemoryCardType::Folder;
|
||||
sourceFolderMemoryCard.Open(fd.FileName, config, (8 * 1024 * 1024) / FolderMemoryCard::ClusterSize, EmuConfig.McdFolderAutoManage, "");
|
||||
sourceFolderMemoryCard.Open(fd.FileName, config, (8 * 1024 * 1024) / FolderMemoryCard::ClusterSize, true, "");
|
||||
|
||||
mcds.push_back({std::move(basename), std::move(fd.FileName), fd.ModificationTime,
|
||||
MemoryCardType::Folder, MemoryCardFileType::Unknown, 0u, sourceFolderMemoryCard.IsFormatted()});
|
||||
|
||||
@@ -218,7 +218,7 @@ void Pad::SetDefaultHotkeyConfig(SettingsInterface& si)
|
||||
// PCSX2 Controller Settings - Hotkeys
|
||||
|
||||
// PCSX2 Controller Settings - Hotkeys - General
|
||||
si.SetStringValue("Hotkeys", "ToggleFullscreen", "Keyboard/LAlt & Keyboard/Return");
|
||||
si.SetStringValue("Hotkeys", "ToggleFullscreen", "Keyboard/Alt & Keyboard/Return");
|
||||
|
||||
// PCSX2 Controller Settings - Hotkeys - Graphics
|
||||
si.SetStringValue("Hotkeys", "CycleAspectRatio", "Keyboard/F6");
|
||||
@@ -228,24 +228,24 @@ void Pad::SetDefaultHotkeyConfig(SettingsInterface& si)
|
||||
// si.SetStringValue("Hotkeys", "DecreaseUpscaleMultiplier", "Keyboard"); TBD
|
||||
// si.SetStringValue("Hotkeys", "IncreaseUpscaleMultiplier", "Keyboard"); TBD
|
||||
// si.SetStringValue("Hotkeys", "ReloadTextureReplacements", "Keyboard"); TBD
|
||||
si.SetStringValue("Hotkeys", "GSDumpMultiFrame", "Keyboard/RCtrl & Keyboard/RShift & Keyboard/F8");
|
||||
si.SetStringValue("Hotkeys", "GSDumpMultiFrame", "Keyboard/Control & Keyboard/Shift & Keyboard/F8");
|
||||
si.SetStringValue("Hotkeys", "Screenshot", "Keyboard/F8");
|
||||
si.SetStringValue("Hotkeys", "GSDumpSingleFrame", "Keyboard/RShift & Keyboard/F8");
|
||||
si.SetStringValue("Hotkeys", "GSDumpSingleFrame", "Keyboard/Shift & Keyboard/F8");
|
||||
si.SetStringValue("Hotkeys", "ToggleSoftwareRendering", "Keyboard/F9");
|
||||
// si.SetStringValue("Hotkeys", "ToggleTextureDumping", "Keyboard"); TBD
|
||||
// si.SetStringValue("Hotkeys", "ToggleTextureReplacements", "Keyboard"); TBD
|
||||
si.SetStringValue("Hotkeys", "ZoomIn", "Keyboard/LCtrl & Keyboard/Plus");
|
||||
si.SetStringValue("Hotkeys", "ZoomOut", "Keyboard/LCtrl & Keyboard/Minus");
|
||||
// Missing hotkey for resetting zoom back to 100 with Keyboard/LCtrl & Keyboard/Asterisk
|
||||
si.SetStringValue("Hotkeys", "ZoomIn", "Keyboard/Control & Keyboard/Plus");
|
||||
si.SetStringValue("Hotkeys", "ZoomOut", "Keyboard/Control & Keyboard/Minus");
|
||||
// Missing hotkey for resetting zoom back to 100 with Keyboard/Control & Keyboard/Asterisk
|
||||
|
||||
// PCSX2 Controller Settings - Hotkeys - Input Recording
|
||||
si.SetStringValue("Hotkeys", "InputRecToggleMode", "Keyboard/LShift & Keyboard/R");
|
||||
si.SetStringValue("Hotkeys", "InputRecToggleMode", "Keyboard/Shift & Keyboard/R");
|
||||
|
||||
// PCSX2 Controller Settings - Hotkeys - Save States
|
||||
si.SetStringValue("Hotkeys", "LoadStateFromSlot", "Keyboard/F3");
|
||||
si.SetStringValue("Hotkeys", "SaveStateToSlot", "Keyboard/F1");
|
||||
si.SetStringValue("Hotkeys", "NextSaveStateSlot", "Keyboard/F2");
|
||||
si.SetStringValue("Hotkeys", "PreviousSaveStateSlot", "Keyboard/LShift & Keyboard/F2");
|
||||
si.SetStringValue("Hotkeys", "PreviousSaveStateSlot", "Keyboard/Shift & Keyboard/F2");
|
||||
|
||||
// PCSX2 Controller Settings - Hotkeys - System
|
||||
// si.SetStringValue("Hotkeys", "DecreaseSpeed", "Keyboard"); TBD
|
||||
@@ -256,7 +256,7 @@ void Pad::SetDefaultHotkeyConfig(SettingsInterface& si)
|
||||
si.SetStringValue("Hotkeys", "OpenPauseMenu", "Keyboard/Escape");
|
||||
si.SetStringValue("Hotkeys", "ToggleFrameLimit", "Keyboard/F4");
|
||||
si.SetStringValue("Hotkeys", "TogglePause", "Keyboard/Space");
|
||||
si.SetStringValue("Hotkeys", "ToggleSlowMotion", "Keyboard/LShift & Keyboard/Backtab");
|
||||
si.SetStringValue("Hotkeys", "ToggleSlowMotion", "Keyboard/Shift & Keyboard/Backtab");
|
||||
si.SetStringValue("Hotkeys", "ToggleTurbo", "Keyboard/Tab");
|
||||
si.SetStringValue("Hotkeys", "HoldTurbo", "Keyboard/Period");
|
||||
}
|
||||
|
||||
@@ -200,7 +200,6 @@ void SPU2::DoFullDump()
|
||||
fprintf(dump, " - Sound Start Address: %x\n", Cores[c].Voices[v].StartA);
|
||||
fprintf(dump, " - Next Data Address: %x\n", Cores[c].Voices[v].NextA);
|
||||
fprintf(dump, " - Play Status: %s\n", (Cores[c].Voices[v].ADSR.Phase > 0) ? "Playing" : "Not Playing");
|
||||
fprintf(dump, " - Block Sample: %d\n", Cores[c].Voices[v].SCurrent);
|
||||
}
|
||||
fprintf(dump, "#### END OF DUMP.\n\n");
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ void V_Core::StartADMAWrite(u16* pMem, u32 sz)
|
||||
if ((AutoDMACtrl & (Index + 1)) == 0)
|
||||
{
|
||||
ActiveTSA = 0x2000 + (Index << 10);
|
||||
DMAICounter = size * 4;
|
||||
DMAICounter = size * 48;
|
||||
LastClock = psxRegs.cycle;
|
||||
}
|
||||
else if (size >= 256)
|
||||
@@ -191,7 +191,7 @@ void V_Core::StartADMAWrite(u16* pMem, u32 sz)
|
||||
if (SPU2::MsgToConsole())
|
||||
SPU2::ConLog("ADMA%c Error Size of %x too small\n", GetDmaIndexChar(), size);
|
||||
InputDataLeft = 0;
|
||||
DMAICounter = size * 4;
|
||||
DMAICounter = size * 48;
|
||||
LastClock = psxRegs.cycle;
|
||||
}
|
||||
}
|
||||
@@ -248,7 +248,7 @@ void V_Core::FinishDMAwrite()
|
||||
DMA7LogWrite(DMAPtr, ReadSize << 1);
|
||||
#endif
|
||||
|
||||
u32 buff1end = ActiveTSA + std::min(ReadSize, (u32)0x100 + std::abs(DMAICounter / 4));
|
||||
u32 buff1end = ActiveTSA + std::min(ReadSize, (u32)0x100 + std::abs(DMAICounter / 48));
|
||||
u32 buff2end = 0;
|
||||
if (buff1end > 0x100000)
|
||||
{
|
||||
@@ -343,7 +343,7 @@ void V_Core::FinishDMAwrite()
|
||||
DMAPtr += TDA - ActiveTSA;
|
||||
ReadSize -= TDA - ActiveTSA;
|
||||
|
||||
DMAICounter = (DMAICounter - ReadSize) * 4;
|
||||
DMAICounter = (DMAICounter - ReadSize) * 48;
|
||||
|
||||
CounterUpdate(DMAICounter);
|
||||
|
||||
@@ -354,7 +354,7 @@ void V_Core::FinishDMAwrite()
|
||||
|
||||
void V_Core::FinishDMAread()
|
||||
{
|
||||
u32 buff1end = ActiveTSA + std::min(ReadSize, (u32)0x100 + std::abs(DMAICounter / 4));
|
||||
u32 buff1end = ActiveTSA + std::min(ReadSize, (u32)0x100 + std::abs(DMAICounter / 48));
|
||||
u32 buff2end = 0;
|
||||
|
||||
if (buff1end > 0x100000)
|
||||
@@ -426,9 +426,9 @@ void V_Core::FinishDMAread()
|
||||
|
||||
// DMA Reads are done AFTER the delay, so to get the timing right we need to scheule one last DMA to catch IRQ's
|
||||
if (ReadSize)
|
||||
DMAICounter = std::min(ReadSize, (u32)0x100) * 4;
|
||||
DMAICounter = std::min(ReadSize, (u32)0x100) * 48;
|
||||
else
|
||||
DMAICounter = 4;
|
||||
DMAICounter = 48;
|
||||
|
||||
CounterUpdate(DMAICounter);
|
||||
|
||||
@@ -446,7 +446,7 @@ void V_Core::DoDMAread(u16* pMem, u32 size)
|
||||
ReadSize = size;
|
||||
IsDMARead = true;
|
||||
LastClock = psxRegs.cycle;
|
||||
DMAICounter = std::min(ReadSize, (u32)0x100) * 4;
|
||||
DMAICounter = (std::min(ReadSize, (u32)0x100) * 48);
|
||||
Regs.STATX &= ~0x80;
|
||||
Regs.STATX |= 0x400;
|
||||
//Regs.ATTR |= 0x30;
|
||||
@@ -470,7 +470,7 @@ void V_Core::DoDMAwrite(u16* pMem, u32 size)
|
||||
{
|
||||
Regs.STATX &= ~0x80;
|
||||
//Regs.ATTR |= 0x30;
|
||||
DMAICounter = 1 * 4;
|
||||
DMAICounter = 1 * 48;
|
||||
LastClock = psxRegs.cycle;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -89,55 +89,13 @@ int g_counter_cache_ignores = 0;
|
||||
#define XAFLAG_LOOP (1ul << 1)
|
||||
#define XAFLAG_LOOP_START (1ul << 2)
|
||||
|
||||
static __forceinline s32 GetNextDataBuffered(V_Core& thiscore, uint voiceidx)
|
||||
static __forceinline void GetNextDataBuffered(V_Core& thiscore, uint voiceidx)
|
||||
{
|
||||
V_Voice& vc(thiscore.Voices[voiceidx]);
|
||||
|
||||
if ((vc.SCurrent & 3) == 0)
|
||||
if (vc.SBuffer == nullptr)
|
||||
{
|
||||
IncrementNextA(thiscore, voiceidx);
|
||||
|
||||
if ((vc.NextA & 7) == 0) // vc.SCurrent == 24 equivalent
|
||||
{
|
||||
if (vc.LoopFlags & XAFLAG_LOOP_END)
|
||||
{
|
||||
thiscore.Regs.ENDX |= (1 << voiceidx);
|
||||
vc.NextA = vc.LoopStartA | 1;
|
||||
if (!(vc.LoopFlags & XAFLAG_LOOP))
|
||||
{
|
||||
vc.Stop();
|
||||
|
||||
if (IsDevBuild)
|
||||
{
|
||||
if (SPU2::MsgVoiceOff())
|
||||
SPU2::ConLog("* SPU2: Voice Off by EndPoint: %d \n", voiceidx);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
vc.NextA++; // no, don't IncrementNextA here. We haven't read the header yet.
|
||||
}
|
||||
}
|
||||
|
||||
if (vc.SCurrent == 28)
|
||||
{
|
||||
vc.SCurrent = 0;
|
||||
|
||||
// We'll need the loop flags and buffer pointers regardless of cache status:
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
if (Cores[i].IRQEnable && Cores[i].IRQA == (vc.NextA & 0xFFFF8))
|
||||
SetIrqCall(i);
|
||||
|
||||
s16* memptr = GetMemPtr(vc.NextA & 0xFFFF8);
|
||||
vc.LoopFlags = *memptr >> 8; // grab loop flags from the upper byte.
|
||||
|
||||
if ((vc.LoopFlags & XAFLAG_LOOP_START) && !vc.LoopMode)
|
||||
{
|
||||
vc.LoopStartA = vc.NextA & 0xFFFF8;
|
||||
}
|
||||
|
||||
const int cacheIdx = vc.NextA / pcm_WordsPerBlock;
|
||||
const int cacheIdx = (vc.NextA & 0xFFFF8) / pcm_WordsPerBlock;
|
||||
PcmCacheEntry& cacheLine = pcm_cache_data[cacheIdx];
|
||||
vc.SBuffer = cacheLine.Sampledata;
|
||||
|
||||
@@ -172,46 +130,18 @@ static __forceinline s32 GetNextDataBuffered(V_Core& thiscore, uint voiceidx)
|
||||
g_counter_cache_misses++;
|
||||
}
|
||||
|
||||
|
||||
s16* memptr = GetMemPtr(vc.NextA & 0xFFFF8);
|
||||
XA_decode_block(vc.SBuffer, memptr, vc.Prev1, vc.Prev2);
|
||||
}
|
||||
}
|
||||
|
||||
return vc.SBuffer[vc.SCurrent++];
|
||||
}
|
||||
|
||||
static __forceinline void GetNextDataDummy(V_Core& thiscore, uint voiceidx)
|
||||
{
|
||||
V_Voice& vc(thiscore.Voices[voiceidx]);
|
||||
|
||||
IncrementNextA(thiscore, voiceidx);
|
||||
|
||||
if ((vc.NextA & 7) == 0) // vc.SCurrent == 24 equivalent
|
||||
// Get the sample index for NextA, we have to subtract 1 to ignore the loop header
|
||||
int sampleIdx = ((vc.NextA % pcm_WordsPerBlock) - 1) * 4;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (vc.LoopFlags & XAFLAG_LOOP_END)
|
||||
{
|
||||
thiscore.Regs.ENDX |= (1 << voiceidx);
|
||||
vc.NextA = vc.LoopStartA | 1;
|
||||
}
|
||||
else
|
||||
vc.NextA++; // no, don't IncrementNextA here. We haven't read the header yet.
|
||||
vc.DecodeFifo[(vc.DecPosWrite + i) % 32] = vc.SBuffer[sampleIdx + i];
|
||||
}
|
||||
|
||||
if (vc.SCurrent == 28)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
if (Cores[i].IRQEnable && Cores[i].IRQA == (vc.NextA & 0xFFFF8))
|
||||
SetIrqCall(i);
|
||||
|
||||
vc.LoopFlags = *GetMemPtr(vc.NextA & 0xFFFF8) >> 8; // grab loop flags from the upper byte.
|
||||
|
||||
if ((vc.LoopFlags & XAFLAG_LOOP_START) && !vc.LoopMode)
|
||||
vc.LoopStartA = vc.NextA & 0xFFFF8;
|
||||
|
||||
vc.SCurrent = 0;
|
||||
}
|
||||
|
||||
vc.SP -= 0x1000 * (4 - (vc.SCurrent & 3));
|
||||
vc.SCurrent += 4 - (vc.SCurrent & 3);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -237,6 +167,69 @@ static __forceinline StereoOut32 ApplyVolume(const StereoOut32& data, const V_Vo
|
||||
ApplyVolume(data.Right, volume.Right.Value));
|
||||
}
|
||||
|
||||
static __forceinline void UpdateBlockHeader(V_Core& thiscore, uint voiceidx)
|
||||
{
|
||||
V_Voice& vc(thiscore.Voices[voiceidx]);
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
if (Cores[i].IRQEnable && Cores[i].IRQA == (vc.NextA & 0xFFFF8))
|
||||
SetIrqCall(i);
|
||||
|
||||
s16* memptr = GetMemPtr(vc.NextA & 0xFFFF8);
|
||||
vc.LoopFlags = *memptr >> 8; // grab loop flags from the upper byte.
|
||||
|
||||
if ((vc.LoopFlags & XAFLAG_LOOP_START) && !vc.LoopMode)
|
||||
{
|
||||
vc.LoopStartA = vc.NextA & 0xFFFF8;
|
||||
}
|
||||
}
|
||||
|
||||
static __forceinline void DecodeSamples(uint coreidx, uint voiceidx)
|
||||
{
|
||||
V_Core& thiscore(Cores[coreidx]);
|
||||
V_Voice& vc(thiscore.Voices[voiceidx]);
|
||||
|
||||
// Update the block header on every audio frame
|
||||
UpdateBlockHeader(thiscore, voiceidx);
|
||||
|
||||
// When a voice is started at 0 pitch, NAX quickly advances to SSA + 5
|
||||
// So that would mean the decode buffer holds around 12 samples
|
||||
if (((int)(vc.DecPosWrite - vc.DecPosRead)) > 12) {
|
||||
// Sufficient data buffered
|
||||
return;
|
||||
}
|
||||
|
||||
if (vc.ADSR.Phase > V_ADSR::PHASE_STOPPED)
|
||||
{
|
||||
GetNextDataBuffered(thiscore, voiceidx);
|
||||
}
|
||||
|
||||
vc.DecPosWrite += 4;
|
||||
|
||||
IncrementNextA(thiscore, voiceidx);
|
||||
if ((vc.NextA & 7) == 0)
|
||||
{
|
||||
if (vc.LoopFlags & XAFLAG_LOOP_END)
|
||||
{
|
||||
thiscore.Regs.ENDX |= (1 << voiceidx);
|
||||
vc.NextA = vc.LoopStartA;
|
||||
if (!(vc.LoopFlags & XAFLAG_LOOP))
|
||||
{
|
||||
vc.Stop();
|
||||
|
||||
if (IsDevBuild)
|
||||
{
|
||||
if (SPU2::MsgVoiceOff())
|
||||
SPU2::ConLog("* SPU2: Voice Off by EndPoint: %d \n", voiceidx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IncrementNextA(thiscore, voiceidx);
|
||||
vc.SBuffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void __forceinline UpdatePitch(uint coreidx, uint voiceidx)
|
||||
{
|
||||
V_Voice& vc(Cores[coreidx].Voices[voiceidx]);
|
||||
@@ -278,33 +271,27 @@ static __forceinline void CalculateADSR(V_Core& thiscore, uint voiceidx)
|
||||
pxAssume(vc.ADSR.Value >= 0); // ADSR should never be negative...
|
||||
}
|
||||
|
||||
__forceinline static s32 GaussianInterpolate(s32 pv4, s32 pv3, s32 pv2, s32 pv1, s32 i)
|
||||
static __forceinline void ConsumeSamples(V_Core& thiscore, uint voiceidx)
|
||||
{
|
||||
s32 out = 0;
|
||||
out = (interpTable[i][0] * pv4) >> 15;
|
||||
out += (interpTable[i][1] * pv3) >> 15;
|
||||
out += (interpTable[i][2] * pv2) >> 15;
|
||||
out += (interpTable[i][3] * pv1) >> 15;
|
||||
V_Voice& vc(thiscore.Voices[voiceidx]);
|
||||
|
||||
return out;
|
||||
int consumed = vc.SP >> 12;
|
||||
vc.SP &= 0xfff;
|
||||
vc.DecPosRead += consumed;
|
||||
}
|
||||
|
||||
static __forceinline s32 GetVoiceValues(V_Core& thiscore, uint voiceidx)
|
||||
{
|
||||
V_Voice& vc(thiscore.Voices[voiceidx]);
|
||||
|
||||
while (vc.SP >= 0)
|
||||
{
|
||||
vc.PV4 = vc.PV3;
|
||||
vc.PV3 = vc.PV2;
|
||||
vc.PV2 = vc.PV1;
|
||||
vc.PV1 = GetNextDataBuffered(thiscore, voiceidx);
|
||||
vc.SP -= 0x1000;
|
||||
}
|
||||
int phase = (vc.SP & 0x0ff0) >> 4;
|
||||
s32 out = 0;
|
||||
out += (interpTable[phase][0] * vc.DecodeFifo[(vc.DecPosRead + 0) % 32]) >> 15;
|
||||
out += (interpTable[phase][1] * vc.DecodeFifo[(vc.DecPosRead + 1) % 32]) >> 15;
|
||||
out += (interpTable[phase][2] * vc.DecodeFifo[(vc.DecPosRead + 2) % 32]) >> 15;
|
||||
out += (interpTable[phase][3] * vc.DecodeFifo[(vc.DecPosRead + 3) % 32]) >> 15;
|
||||
|
||||
const s32 mu = vc.SP + 0x1000;
|
||||
|
||||
return GaussianInterpolate(vc.PV4, vc.PV3, vc.PV2, vc.PV1, (mu & 0x0ff0) >> 4);
|
||||
return out;
|
||||
}
|
||||
|
||||
// This is Dr. Hell's noise algorithm as implemented in pcsxr
|
||||
@@ -382,21 +369,13 @@ static __forceinline StereoOut32 MixVoice(uint coreidx, uint voiceidx)
|
||||
V_Core& thiscore(Cores[coreidx]);
|
||||
V_Voice& vc(thiscore.Voices[voiceidx]);
|
||||
|
||||
// If this assertion fails, it mans SCurrent is being corrupted somewhere, or is not initialized
|
||||
// properly. Invalid values in SCurrent will cause errant IRQs and corrupted audio.
|
||||
pxAssertMsg((vc.SCurrent <= 28) && (vc.SCurrent != 0), "Current sample should always range from 1->28");
|
||||
|
||||
// Most games don't use much volume slide effects. So only call the UpdateVolume
|
||||
// methods when needed by checking the flag outside the method here...
|
||||
// (Note: Ys 6 : Ark of Nephistm uses these effects)
|
||||
|
||||
vc.Volume.Update();
|
||||
|
||||
// SPU2 Note: The spu2 continues to process voices for eternity, always, so we
|
||||
// have to run through all the motions of updating the voice regardless of it's
|
||||
// audible status. Otherwise IRQs might not trigger and emulation might fail.
|
||||
|
||||
UpdatePitch(coreidx, voiceidx);
|
||||
DecodeSamples(coreidx, voiceidx);
|
||||
|
||||
StereoOut32 voiceOut(0, 0);
|
||||
s32 Value = 0;
|
||||
@@ -419,11 +398,14 @@ static __forceinline StereoOut32 MixVoice(uint coreidx, uint voiceidx)
|
||||
|
||||
voiceOut = ApplyVolume(StereoOut32(Value, Value), vc.Volume);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (vc.SP >= 0)
|
||||
GetNextDataDummy(thiscore, voiceidx); // Dummy is enough
|
||||
}
|
||||
|
||||
// SPU2 Note: The spu2 continues to process voices for eternity, always, so we
|
||||
// have to run through all the motions of updating the voice regardless of it's
|
||||
// audible status. Otherwise IRQs might not trigger and emulation might fail.
|
||||
|
||||
UpdatePitch(coreidx, voiceidx);
|
||||
|
||||
ConsumeSamples(thiscore, voiceidx);
|
||||
|
||||
// Write-back of raw voice data (post ADSR applied)
|
||||
if (voiceidx == 1)
|
||||
@@ -533,7 +515,8 @@ StereoOut32 V_Core::Mix(const VoiceMixSet& inVoices, const StereoOut32& Input, c
|
||||
return TD + ApplyVolume(RV, FxVol);
|
||||
}
|
||||
|
||||
static StereoOut32 DCFilter(StereoOut32 input) {
|
||||
static StereoOut32 DCFilter(StereoOut32 input)
|
||||
{
|
||||
// A simple DC blocking high-pass filter
|
||||
// Implementation from http://peabody.sapp.org/class/dmp2/lab/dcblock/
|
||||
// The magic number 0x7f5c is ceil(INT16_MAX * 0.995)
|
||||
@@ -634,9 +617,9 @@ __forceinline void spu2Mix()
|
||||
if (SPU2::MsgCache())
|
||||
{
|
||||
SPU2::ConLog(" * SPU2 > CacheStats > Hits: %d Misses: %d Ignores: %d\n",
|
||||
g_counter_cache_hits,
|
||||
g_counter_cache_misses,
|
||||
g_counter_cache_ignores);
|
||||
g_counter_cache_hits,
|
||||
g_counter_cache_misses,
|
||||
g_counter_cache_ignores);
|
||||
}
|
||||
|
||||
g_counter_cache_hits =
|
||||
|
||||
@@ -256,29 +256,16 @@ struct V_Voice
|
||||
// Sample pointer (19:12 bit fixed point)
|
||||
s32 SP;
|
||||
|
||||
// Sample pointer for Cubic Interpolation
|
||||
// Cubic interpolation mixes a sample behind Linear, so that it
|
||||
// can have sample data to either side of the end points from which
|
||||
// to extrapolate. This SP represents that late sample position.
|
||||
s32 SPc;
|
||||
|
||||
// Previous sample values - used for interpolation
|
||||
// Inverted order of these members to match the access order in the
|
||||
// code (might improve cache hits).
|
||||
s32 PV4;
|
||||
s32 PV3;
|
||||
s32 PV2;
|
||||
s32 PV1;
|
||||
|
||||
// Last outputted audio value, used for voice modulation.
|
||||
s32 OutX;
|
||||
s32 NextCrest; // temp value for Crest calculation
|
||||
|
||||
// SBuffer now points directly to an ADPCM cache entry.
|
||||
s16* SBuffer;
|
||||
|
||||
// sample position within the current decoded packet.
|
||||
s32 SCurrent;
|
||||
// Each voice has a buffer of decoded samples
|
||||
s32 DecodeFifo[32];
|
||||
u32 DecPosWrite;
|
||||
u32 DecPosRead;
|
||||
|
||||
// it takes a few ticks for voices to start on the real SPU2?
|
||||
void Start();
|
||||
|
||||
@@ -181,7 +181,6 @@ void V_Core::Init(int index)
|
||||
VoiceGates[v].WetR = -1;
|
||||
|
||||
Voices[v].Volume = V_VolumeSlideLR(0, 0); // V_VolumeSlideLR::Max;
|
||||
Voices[v].SCurrent = 28;
|
||||
|
||||
Voices[v].ADSR.Counter = 0;
|
||||
Voices[v].ADSR.Value = 0;
|
||||
@@ -190,6 +189,10 @@ void V_Core::Init(int index)
|
||||
Voices[v].NextA = 0x2801;
|
||||
Voices[v].StartA = 0x2800;
|
||||
Voices[v].LoopStartA = 0x2800;
|
||||
|
||||
memset(Voices[v].DecodeFifo, 0, sizeof(Voices[v].DecodeFifo));
|
||||
Voices[v].DecPosRead = 0;
|
||||
Voices[v].DecPosWrite = 0;
|
||||
}
|
||||
|
||||
DMAICounter = 0;
|
||||
@@ -212,23 +215,18 @@ void V_Voice::Start()
|
||||
}
|
||||
|
||||
ADSR.Attack();
|
||||
SCurrent = 28;
|
||||
LoopMode = 0;
|
||||
|
||||
// When SP >= 0 the next sample will be grabbed, we don't want this to happen
|
||||
// instantly because in the case of pitch being 0 we want to delay getting
|
||||
// the next block header. This is a hack to work around the fact that unlike
|
||||
// the HW we don't update the block header on every cycle.
|
||||
SP = -1;
|
||||
SP = 0;
|
||||
|
||||
LoopFlags = 0;
|
||||
NextA = StartA | 1;
|
||||
Prev1 = 0;
|
||||
Prev2 = 0;
|
||||
|
||||
PV1 = PV2 = 0;
|
||||
PV3 = PV4 = 0;
|
||||
NextCrest = -0x8000;
|
||||
SBuffer = nullptr;
|
||||
DecPosRead = 0;
|
||||
DecPosWrite = 0;
|
||||
}
|
||||
|
||||
void V_Voice::Stop()
|
||||
@@ -989,12 +987,10 @@ static void RegWrite_VoiceAddr(u16 value)
|
||||
// Wallace And Gromit: Curse Of The Were-Rabbit.
|
||||
|
||||
thisvoice.NextA = ((u32)(value & 0x0F) << 16) | (thisvoice.NextA & 0xFFF8) | 1;
|
||||
thisvoice.SCurrent = 28;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
thisvoice.NextA = (thisvoice.NextA & 0x0F0000) | (value & 0xFFF8) | 1;
|
||||
thisvoice.SCurrent = 28;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1212,7 +1208,6 @@ static void RegWrite_Core(u16 value)
|
||||
for (uint v = 0; v < 24; ++v)
|
||||
{
|
||||
Cores[1].Voices[v].Volume = V_VolumeSlideLR(0, 0); // V_VolumeSlideLR::Max;
|
||||
Cores[1].Voices[v].SCurrent = 28;
|
||||
|
||||
Cores[1].Voices[v].ADSR.Value = 0;
|
||||
Cores[1].Voices[v].ADSR.Phase = 0;
|
||||
|
||||
@@ -26,7 +26,7 @@ enum class FreezeAction
|
||||
// [SAVEVERSION+]
|
||||
// This informs the auto updater that the users savestates will be invalidated.
|
||||
|
||||
static const u32 g_SaveVersion = (0x9A55 << 16) | 0x0000;
|
||||
static const u32 g_SaveVersion = (0x9A57 << 16) | 0x0000;
|
||||
|
||||
|
||||
// the freezing data between submodules and core
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
/// Version number for GS and other shaders. Increment whenever any of the contents of the
|
||||
/// shaders change, to invalidate the cache.
|
||||
static constexpr u32 SHADER_CACHE_VERSION = 78;
|
||||
static constexpr u32 SHADER_CACHE_VERSION = 81;
|
||||
|
||||
@@ -2993,8 +2993,6 @@ void VMManager::CheckForMemoryCardConfigChanges(const Pcsx2Config& old_config)
|
||||
}
|
||||
}
|
||||
|
||||
changed |= (EmuConfig.McdFolderAutoManage != old_config.McdFolderAutoManage);
|
||||
|
||||
if (!changed)
|
||||
return;
|
||||
|
||||
|
||||
@@ -254,6 +254,7 @@
|
||||
<ClCompile Include="Host\SDLAudioStream.cpp" />
|
||||
<ClCompile Include="Hotkeys.cpp" />
|
||||
<ClCompile Include="ImGui\FullscreenUI.cpp" />
|
||||
<ClCompile Include="ImGui\FullscreenUI_Settings.cpp" />
|
||||
<ClCompile Include="ImGui\ImGuiFullscreen.cpp" />
|
||||
<ClCompile Include="ImGui\ImGuiManager.cpp" />
|
||||
<ClCompile Include="ImGui\ImGuiOverlays.cpp" />
|
||||
@@ -700,6 +701,7 @@
|
||||
<ClInclude Include="Host\AudioStream.h" />
|
||||
<ClInclude Include="Host\AudioStreamTypes.h" />
|
||||
<ClInclude Include="ImGui\FullscreenUI.h" />
|
||||
<ClInclude Include="ImGui\FullscreenUI_Internal.h" />
|
||||
<ClInclude Include="ImGui\ImGuiAnimated.h" />
|
||||
<ClInclude Include="ImGui\ImGuiFullscreen.h" />
|
||||
<ClInclude Include="ImGui\ImGuiManager.h" />
|
||||
|
||||
@@ -1352,6 +1352,9 @@
|
||||
<ClCompile Include="ImGui\FullscreenUI.cpp">
|
||||
<Filter>Misc\ImGui</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ImGui\FullscreenUI_Settings.cpp">
|
||||
<Filter>Misc\ImGui</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ImGui\ImGuiFullscreen.cpp">
|
||||
<Filter>Misc\ImGui</Filter>
|
||||
</ClCompile>
|
||||
@@ -2304,6 +2307,9 @@
|
||||
<ClInclude Include="ImGui\FullscreenUI.h">
|
||||
<Filter>Misc\ImGui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ImGui\FullscreenUI_Internal.h">
|
||||
<Filter>Misc\ImGui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ImGui\ImGuiFullscreen.h">
|
||||
<Filter>Misc\ImGui</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@@ -5,90 +5,110 @@ import os
|
||||
START_IDENT = "// TRANSLATION-STRING-AREA-BEGIN"
|
||||
END_IDENT = "// TRANSLATION-STRING-AREA-END"
|
||||
|
||||
src_file = os.path.join(os.path.dirname(__file__), "..", "pcsx2", "ImGui", "FullscreenUI.cpp")
|
||||
src_files = [
|
||||
os.path.join(os.path.dirname(__file__), "..", "pcsx2", "ImGui", "FullscreenUI.cpp"),
|
||||
os.path.join(os.path.dirname(__file__), "..", "pcsx2", "ImGui", "FullscreenUI_Settings.cpp"),
|
||||
]
|
||||
|
||||
with open(src_file, "r") as f:
|
||||
full_source = f.read()
|
||||
def extract_strings_from_source(source_content):
|
||||
"""Extract FSUI translation strings from source content."""
|
||||
strings = []
|
||||
for token in ["FSUI_STR", "FSUI_CSTR", "FSUI_FSTR", "FSUI_NSTR", "FSUI_VSTR", "FSUI_ICONSTR", "FSUI_ICONSTR_S"]:
|
||||
token_len = len(token)
|
||||
last_pos = 0
|
||||
while True:
|
||||
last_pos = source_content.find(token, last_pos)
|
||||
if last_pos < 0:
|
||||
break
|
||||
|
||||
strings = []
|
||||
for token in ["FSUI_STR", "FSUI_CSTR", "FSUI_FSTR", "FSUI_NSTR", "FSUI_VSTR", "FSUI_ICONSTR", "FSUI_ICONSTR_S"]:
|
||||
token_len = len(token)
|
||||
last_pos = 0
|
||||
while True:
|
||||
last_pos = full_source.find(token, last_pos)
|
||||
if last_pos < 0:
|
||||
break
|
||||
if last_pos >= 8 and source_content[last_pos - 8:last_pos] == "#define ":
|
||||
last_pos += len(token)
|
||||
continue
|
||||
|
||||
if last_pos >= 8 and full_source[last_pos - 8:last_pos] == "#define ":
|
||||
last_pos += len(token)
|
||||
continue
|
||||
if source_content[last_pos + token_len] == '(':
|
||||
start_pos = last_pos + token_len + 1
|
||||
end_pos = source_content.find(")", start_pos)
|
||||
s = source_content[start_pos:end_pos]
|
||||
|
||||
if full_source[last_pos + token_len] == '(':
|
||||
start_pos = last_pos + token_len + 1
|
||||
end_pos = full_source.find(")", start_pos)
|
||||
s = full_source[start_pos:end_pos]
|
||||
# Split into string arguments, removing "
|
||||
string_args = [""]
|
||||
arg = 0;
|
||||
cpos = s.find(',')
|
||||
pos = s.find('"')
|
||||
while pos >= 0 or cpos >= 0:
|
||||
assert pos == 0 or s[pos - 1] != '\\'
|
||||
if cpos == -1 or pos < cpos:
|
||||
epos = pos
|
||||
while True:
|
||||
epos = s.find('"', epos + 1)
|
||||
# found ')' in string, extend s to next ')'
|
||||
if epos == -1:
|
||||
end_pos = source_content.find(")", end_pos + 1)
|
||||
s = source_content[start_pos:end_pos]
|
||||
epos = pos
|
||||
continue
|
||||
|
||||
# Split into sting arguments, removing "
|
||||
string_args = [""]
|
||||
arg = 0;
|
||||
cpos = s.find(',')
|
||||
pos = s.find('"')
|
||||
while pos >= 0 or cpos >= 0:
|
||||
assert pos == 0 or s[pos - 1] != '\\'
|
||||
if cpos == -1 or pos < cpos:
|
||||
epos = pos
|
||||
while True:
|
||||
epos = s.find('"', epos + 1)
|
||||
# found ')' in string, extend s to next ')'
|
||||
if epos == -1:
|
||||
end_pos = full_source.find(")", end_pos + 1)
|
||||
s = full_source[start_pos:end_pos]
|
||||
epos = pos
|
||||
continue
|
||||
if s[epos - 1] == '\\':
|
||||
continue
|
||||
else:
|
||||
break
|
||||
|
||||
if s[epos - 1] == '\\':
|
||||
continue
|
||||
else:
|
||||
break
|
||||
assert epos > pos
|
||||
string_args[arg] += s[pos+1:epos]
|
||||
cpos = s.find(',', epos + 1)
|
||||
pos = s.find('"', epos + 1)
|
||||
else:
|
||||
arg += 1
|
||||
string_args.append("")
|
||||
cpos = s.find(',', cpos + 1)
|
||||
|
||||
assert epos > pos
|
||||
string_args[arg] += s[pos+1:epos]
|
||||
cpos = s.find(',', epos + 1)
|
||||
pos = s.find('"', epos + 1)
|
||||
print(string_args)
|
||||
|
||||
# FSUI_ICONSTR and FSUI_ICONSTR_S need to translate the only the second argument
|
||||
# other defines take only a single argument
|
||||
if len(string_args) >= 2:
|
||||
new_s = string_args[1]
|
||||
else:
|
||||
arg += 1
|
||||
string_args.append("")
|
||||
cpos = s.find(',', cpos + 1)
|
||||
new_s = string_args[0]
|
||||
|
||||
print(string_args)
|
||||
assert len(new_s) > 0
|
||||
|
||||
# FSUI_ICONSTR and FSUI_ICONSTR_S need to translate the only the second argument
|
||||
# other defines take only a single argument
|
||||
if len(string_args) >= 2:
|
||||
new_s = string_args[1]
|
||||
else:
|
||||
new_s = string_args[0]
|
||||
if new_s not in strings:
|
||||
strings.append(new_s)
|
||||
last_pos += len(token)
|
||||
return strings
|
||||
|
||||
assert len(new_s) > 0
|
||||
def process_file(src_file):
|
||||
"""Process a single source file extract strings and update its translation area."""
|
||||
print(f"\nProcessing: {src_file}")
|
||||
|
||||
with open(src_file, "r") as f:
|
||||
source = f.read()
|
||||
|
||||
#assert (end_pos - start_pos) < 300
|
||||
#if (end_pos - start_pos) >= 300:
|
||||
# print("WARNING: Long string")
|
||||
# print(new_s)
|
||||
if new_s not in strings:
|
||||
strings.append(new_s)
|
||||
last_pos += len(token)
|
||||
start = source.find(START_IDENT)
|
||||
end = source.find(END_IDENT)
|
||||
|
||||
if start < 0 or end <= start:
|
||||
print(f" Warning: No translation string area found in {src_file}")
|
||||
return 0
|
||||
|
||||
source_without_area = source[:start] + source[end + len(END_IDENT):]
|
||||
strings = extract_strings_from_source(source_without_area)
|
||||
|
||||
print(f" Found {len(strings)} unique strings.")
|
||||
|
||||
new_area = ""
|
||||
for string in strings:
|
||||
new_area += f"TRANSLATE_NOOP(\"FullscreenUI\", \"{string}\");\n"
|
||||
|
||||
new_source = source[:start + len(START_IDENT) + 1] + new_area + source[end:]
|
||||
with open(src_file, "w") as f:
|
||||
f.write(new_source)
|
||||
|
||||
return len(strings)
|
||||
|
||||
print(f"Found {len(strings)} unique strings.")
|
||||
total_strings = 0
|
||||
for src_file in src_files:
|
||||
total_strings += process_file(src_file)
|
||||
|
||||
start = full_source.find(START_IDENT)
|
||||
end = full_source.find(END_IDENT)
|
||||
assert start >= 0 and end > start
|
||||
|
||||
new_area = ""
|
||||
for string in list(strings):
|
||||
new_area += f"TRANSLATE_NOOP(\"FullscreenUI\", \"{string}\");\n"
|
||||
|
||||
full_source = full_source[:start+len(START_IDENT)+1] + new_area + full_source[end:]
|
||||
with open(src_file, "w") as f:
|
||||
f.write(full_source)
|
||||
print(f"\nTotal: {total_strings} unique strings across all files.")
|
||||
|
||||
Reference in New Issue
Block a user