mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-01-31 01:15:24 +01:00
Compare commits
59 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 |
@@ -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.
|
||||
@@ -7614,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.
|
||||
@@ -24777,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"
|
||||
@@ -25578,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.
|
||||
@@ -26228,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.
|
||||
@@ -35853,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.
|
||||
@@ -35924,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.
|
||||
@@ -40077,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: "おにはまばくそうぐれんたい げきとうへん"
|
||||
@@ -59824,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.
|
||||
@@ -61228,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.
|
||||
@@ -71360,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.
|
||||
@@ -74901,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.
|
||||
@@ -75679,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;
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -1397,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)},
|
||||
@@ -1427,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);
|
||||
@@ -1444,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());
|
||||
@@ -1461,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;
|
||||
@@ -2063,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;
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#include "common/ScopedGuard.h"
|
||||
#include "common/SmallString.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/Path.h"
|
||||
|
||||
#include "D3D12MemAlloc.h"
|
||||
#include "imgui.h"
|
||||
@@ -158,6 +160,96 @@ u32 GSDevice12::GetAdapterVendorID() const
|
||||
return desc.VendorId;
|
||||
}
|
||||
|
||||
uint SDKVersion(const std::string& path)
|
||||
{
|
||||
// The Agility SDK version is embeded as the minor file version.
|
||||
// This is only true for the redist files, not the OS files.
|
||||
// Alternativly, D3D12Core also exports its D3D12SDKVersion.
|
||||
std::wstring wpath = FileSystem::GetWin32Path(path);
|
||||
|
||||
const DWORD size = GetFileVersionInfoSizeW(wpath.c_str(), nullptr);
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
BOOL ret;
|
||||
|
||||
std::vector<char> info_data(size);
|
||||
ret = GetFileVersionInfoW(wpath.c_str(), 0, size, info_data.data());
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
uint len;
|
||||
VS_FIXEDFILEINFO* file_ver;
|
||||
ret = VerQueryValueW(info_data.data(), L"\\", reinterpret_cast<void**>(&file_ver), &len);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
return file_ver->dwFileVersionMS & 0xFFFF;
|
||||
}
|
||||
|
||||
void GSDevice12::LoadAgilitySDK()
|
||||
{
|
||||
static bool agility_loaded = false;
|
||||
if (agility_loaded)
|
||||
return;
|
||||
|
||||
// On older versions of Windows 10 (example 2019 LTSC) D3D12GetInterface may fail because it doesn't exist,
|
||||
// in such case we can check if D3D12GetInterface exists first.
|
||||
const HMODULE d3d12 = GetModuleHandleW(L"d3d12.dll");
|
||||
if (!d3d12)
|
||||
return;
|
||||
|
||||
using PFN_D3D12GetInterface = HRESULT(WINAPI*)(REFCLSID rclsid, REFIID riid, void** ppv);
|
||||
auto pD3D12GetInterface = reinterpret_cast<PFN_D3D12GetInterface>(GetProcAddress(d3d12, "D3D12GetInterface"));
|
||||
if (!pD3D12GetInterface)
|
||||
{
|
||||
Console.Error("D3D12: Agility SDK configuration is not available");
|
||||
return;
|
||||
}
|
||||
|
||||
// See https://microsoft.github.io/DirectX-Specs/d3d/IndependentDevices.html
|
||||
ComPtr<ID3D12SDKConfiguration1> sdk_configuration;
|
||||
HRESULT hr;
|
||||
hr = pD3D12GetInterface(CLSID_D3D12SDKConfiguration, IID_PPV_ARGS(sdk_configuration.put()));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.Error("D3D12: Agility SDK configuration is not available");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string sdk_path = Path::Combine(Path::GetDirectory(FileSystem::GetProgramPath()), "\\D3D12\\");
|
||||
std::string core_path = Path::Combine(sdk_path, "D3D12Core.dll");
|
||||
if (!FileSystem::FileExists(core_path.c_str()))
|
||||
return;
|
||||
|
||||
const uint agility_version = SDKVersion(core_path);
|
||||
if (agility_version == 0)
|
||||
return;
|
||||
|
||||
ComPtr<ID3D12DeviceFactory> device_factory;
|
||||
// CreateDeviceFactory seems to use a utf8 string for the path.
|
||||
// If the system has a newer SDK, then the system SDK seems to be returned instead.
|
||||
hr = sdk_configuration->CreateDeviceFactory(agility_version,
|
||||
StringUtil::WideStringToUTF8String(FileSystem::GetWin32Path(sdk_path)).c_str(), IID_PPV_ARGS(device_factory.put()));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.ErrorFmt("D3D12: Unable to load provided Agility SDK {:08X}", hr);
|
||||
return;
|
||||
}
|
||||
|
||||
// Windows 10 (and older drivers on 11) will apply to the global state in ID3D12DeviceFactory::CreateDevice().
|
||||
// To get consistant behaviour across all systems, always apply the global state.
|
||||
// This also allows us to use the normal D3D12*() methods with the loaded agility SDK.
|
||||
hr = device_factory->ApplyToGlobalState();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.ErrorFmt("D3D12: Unable to apply provided Agility SDK {:08X}", hr);
|
||||
return;
|
||||
}
|
||||
|
||||
agility_loaded = true;
|
||||
}
|
||||
|
||||
bool GSDevice12::CreateDevice(u32& vendor_id)
|
||||
{
|
||||
bool enable_debug_layer = GSConfig.UseDebugDevice;
|
||||
@@ -171,6 +263,9 @@ bool GSDevice12::CreateDevice(u32& vendor_id)
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
// Load the Agility SDK
|
||||
LoadAgilitySDK();
|
||||
|
||||
// Enabling the debug layer will fail if the Graphics Tools feature is not installed.
|
||||
if (enable_debug_layer)
|
||||
{
|
||||
@@ -191,6 +286,7 @@ bool GSDevice12::CreateDevice(u32& vendor_id)
|
||||
// Intel Haswell doesn't actually support DX12 even tho the device is created which results in a crash,
|
||||
// to get around this check if device can be created using feature level 12 (skylake+).
|
||||
const bool isIntel = (vendor_id == 0x163C || vendor_id == 0x8086 || vendor_id == 0x8087);
|
||||
|
||||
// Create the actual device.
|
||||
hr = D3D12CreateDevice(m_adapter.get(), isIntel ? D3D_FEATURE_LEVEL_12_0 : D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
|
||||
if (FAILED(hr))
|
||||
@@ -206,6 +302,14 @@ bool GSDevice12::CreateDevice(u32& vendor_id)
|
||||
Console.Error("D3D12: Failed to get lookup adapter by device LUID");
|
||||
}
|
||||
|
||||
ComPtr<ID3D12DeviceConfiguration> config = m_device.try_query<ID3D12DeviceConfiguration>();
|
||||
int sdkVersion = 0;
|
||||
if (config)
|
||||
{
|
||||
sdkVersion = config->GetDesc().SDKVersion;
|
||||
Console.WriteLnFmt("D3D12: Agility version: {}", sdkVersion);
|
||||
}
|
||||
|
||||
if (enable_debug_layer)
|
||||
{
|
||||
ComPtr<ID3D12InfoQueue> info_queue = m_device.try_query<ID3D12InfoQueue>();
|
||||
@@ -218,14 +322,18 @@ bool GSDevice12::CreateDevice(u32& vendor_id)
|
||||
}
|
||||
|
||||
D3D12_INFO_QUEUE_FILTER filter = {};
|
||||
std::array<D3D12_MESSAGE_ID, 5> id_list{
|
||||
std::array<D3D12_MESSAGE_ID, 6> id_list{
|
||||
D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE,
|
||||
D3D12_MESSAGE_ID_CLEARDEPTHSTENCILVIEW_MISMATCHINGCLEARVALUE,
|
||||
D3D12_MESSAGE_ID_CREATEGRAPHICSPIPELINESTATE_RENDERTARGETVIEW_NOT_SET,
|
||||
D3D12_MESSAGE_ID_CREATEINPUTLAYOUT_TYPE_MISMATCH,
|
||||
D3D12_MESSAGE_ID_DRAW_EMPTY_SCISSOR_RECTANGLE,
|
||||
// The current OS version of D3D12 (616) has a validation bug
|
||||
// This is fixed with Agility 1.618.4.
|
||||
// For now, disable this warning untill the OS updates.
|
||||
D3D12_MESSAGE_ID_INCOMPATIBLE_BARRIER_LAYOUT,
|
||||
};
|
||||
filter.DenyList.NumIDs = static_cast<UINT>(id_list.size());
|
||||
filter.DenyList.NumIDs = static_cast<UINT>(sdkVersion < 618 ? id_list.size() : id_list.size() - 1);
|
||||
filter.DenyList.pIDList = id_list.data();
|
||||
info_queue->PushStorageFilter(&filter);
|
||||
}
|
||||
@@ -318,8 +426,17 @@ bool GSDevice12::CreateCommandLists()
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
hr = m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, res.command_allocators[i].get(),
|
||||
nullptr, IID_PPV_ARGS(res.command_lists[i].put()));
|
||||
if (m_enhanced_barriers)
|
||||
{
|
||||
hr = m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, res.command_allocators[i].get(),
|
||||
nullptr, IID_PPV_ARGS(res.command_lists[i].list7.put()));
|
||||
res.command_lists[i].list4 = res.command_lists[i].list7;
|
||||
}
|
||||
else
|
||||
{
|
||||
hr = m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, res.command_allocators[i].get(),
|
||||
nullptr, IID_PPV_ARGS(res.command_lists[i].list4.put()));
|
||||
}
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.Error("D3D12: Failed to create command list: %08X", hr);
|
||||
@@ -327,7 +444,7 @@ bool GSDevice12::CreateCommandLists()
|
||||
}
|
||||
|
||||
// Close the command lists, since the first thing we do is reset them.
|
||||
hr = res.command_lists[i]->Close();
|
||||
hr = res.command_lists[i].list4->Close();
|
||||
pxAssertRel(SUCCEEDED(hr), "Closing new command list failed");
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
@@ -365,7 +482,7 @@ void GSDevice12::MoveToNextCommandList()
|
||||
|
||||
// Begin command list.
|
||||
res.command_allocators[1]->Reset();
|
||||
res.command_lists[1]->Reset(res.command_allocators[1].get(), nullptr);
|
||||
res.command_lists[1].list4->Reset(res.command_allocators[1].get(), nullptr);
|
||||
res.descriptor_allocator.Reset();
|
||||
if (res.sampler_allocator.ShouldReset())
|
||||
res.sampler_allocator.Reset();
|
||||
@@ -397,18 +514,18 @@ void GSDevice12::MoveToNextCommandList()
|
||||
res.has_timestamp_query = m_gpu_timing_enabled;
|
||||
if (m_gpu_timing_enabled)
|
||||
{
|
||||
res.command_lists[1]->EndQuery(m_timestamp_query_heap.get(), D3D12_QUERY_TYPE_TIMESTAMP,
|
||||
res.command_lists[1].list4->EndQuery(m_timestamp_query_heap.get(), D3D12_QUERY_TYPE_TIMESTAMP,
|
||||
m_current_command_list * NUM_TIMESTAMP_QUERIES_PER_CMDLIST);
|
||||
}
|
||||
|
||||
ID3D12DescriptorHeap* heaps[2] = {
|
||||
res.descriptor_allocator.GetDescriptorHeap(), res.sampler_allocator.GetDescriptorHeap()};
|
||||
res.command_lists[1]->SetDescriptorHeaps(std::size(heaps), heaps);
|
||||
res.command_lists[1].list4->SetDescriptorHeaps(std::size(heaps), heaps);
|
||||
|
||||
m_allocator->SetCurrentFrameIndex(static_cast<UINT>(m_current_fence_value));
|
||||
}
|
||||
|
||||
ID3D12GraphicsCommandList4* GSDevice12::GetInitCommandList()
|
||||
const D3D12CommandList& GSDevice12::GetInitCommandList()
|
||||
{
|
||||
CommandListResources& res = m_command_lists[m_current_command_list];
|
||||
if (!res.init_command_list_used)
|
||||
@@ -416,12 +533,12 @@ ID3D12GraphicsCommandList4* GSDevice12::GetInitCommandList()
|
||||
[[maybe_unused]] HRESULT hr = res.command_allocators[0]->Reset();
|
||||
pxAssertMsg(SUCCEEDED(hr), "Reset init command allocator failed");
|
||||
|
||||
res.command_lists[0]->Reset(res.command_allocators[0].get(), nullptr);
|
||||
res.command_lists[0].list4->Reset(res.command_allocators[0].get(), nullptr);
|
||||
pxAssertMsg(SUCCEEDED(hr), "Reset init command list failed");
|
||||
res.init_command_list_used = true;
|
||||
}
|
||||
|
||||
return res.command_lists[0].get();
|
||||
return res.command_lists[0];
|
||||
}
|
||||
|
||||
bool GSDevice12::ExecuteCommandList(WaitType wait_for_completion)
|
||||
@@ -432,16 +549,16 @@ bool GSDevice12::ExecuteCommandList(WaitType wait_for_completion)
|
||||
if (res.has_timestamp_query)
|
||||
{
|
||||
// write the timestamp back at the end of the cmdlist
|
||||
res.command_lists[1]->EndQuery(m_timestamp_query_heap.get(), D3D12_QUERY_TYPE_TIMESTAMP,
|
||||
res.command_lists[1].list4->EndQuery(m_timestamp_query_heap.get(), D3D12_QUERY_TYPE_TIMESTAMP,
|
||||
(m_current_command_list * NUM_TIMESTAMP_QUERIES_PER_CMDLIST) + 1);
|
||||
res.command_lists[1]->ResolveQueryData(m_timestamp_query_heap.get(), D3D12_QUERY_TYPE_TIMESTAMP,
|
||||
res.command_lists[1].list4->ResolveQueryData(m_timestamp_query_heap.get(), D3D12_QUERY_TYPE_TIMESTAMP,
|
||||
m_current_command_list * NUM_TIMESTAMP_QUERIES_PER_CMDLIST, NUM_TIMESTAMP_QUERIES_PER_CMDLIST,
|
||||
m_timestamp_query_buffer.get(), m_current_command_list * (sizeof(u64) * NUM_TIMESTAMP_QUERIES_PER_CMDLIST));
|
||||
}
|
||||
|
||||
if (res.init_command_list_used)
|
||||
{
|
||||
hr = res.command_lists[0]->Close();
|
||||
hr = res.command_lists[0].list4->Close();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.Error("D3D12: Closing init command list failed with HRESULT %08X", hr);
|
||||
@@ -450,7 +567,7 @@ bool GSDevice12::ExecuteCommandList(WaitType wait_for_completion)
|
||||
}
|
||||
|
||||
// Close and queue command list.
|
||||
hr = res.command_lists[1]->Close();
|
||||
hr = res.command_lists[1].list4->Close();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.Error("D3D12: Closing main command list failed with HRESULT %08X", hr);
|
||||
@@ -459,12 +576,12 @@ bool GSDevice12::ExecuteCommandList(WaitType wait_for_completion)
|
||||
|
||||
if (res.init_command_list_used)
|
||||
{
|
||||
const std::array<ID3D12CommandList*, 2> execute_lists{res.command_lists[0].get(), res.command_lists[1].get()};
|
||||
const std::array<ID3D12CommandList*, 2> execute_lists{res.command_lists[0].list4.get(), res.command_lists[1].list4.get()};
|
||||
m_command_queue->ExecuteCommandLists(static_cast<UINT>(execute_lists.size()), execute_lists.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::array<ID3D12CommandList*, 1> execute_lists{res.command_lists[1].get()};
|
||||
const std::array<ID3D12CommandList*, 1> execute_lists{res.command_lists[1].list4.get()};
|
||||
m_command_queue->ExecuteCommandLists(static_cast<UINT>(execute_lists.size()), execute_lists.data());
|
||||
}
|
||||
|
||||
@@ -598,10 +715,14 @@ bool GSDevice12::CreateTimestampQuery()
|
||||
}
|
||||
|
||||
const D3D12MA::ALLOCATION_DESC allocation_desc = {D3D12MA::ALLOCATION_FLAG_NONE, 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};
|
||||
hr = m_allocator->CreateResource(&allocation_desc, &resource_desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr,
|
||||
m_timestamp_query_allocation.put(), IID_PPV_ARGS(m_timestamp_query_buffer.put()));
|
||||
const 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}};
|
||||
if (m_enhanced_barriers)
|
||||
hr = m_allocator->CreateResource3(&allocation_desc, &resource_desc.desc1, D3D12_BARRIER_LAYOUT_UNDEFINED, nullptr,
|
||||
0, nullptr, m_timestamp_query_allocation.put(), IID_PPV_ARGS(m_timestamp_query_buffer.put()));
|
||||
else
|
||||
hr = m_allocator->CreateResource(&allocation_desc, &resource_desc.desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr,
|
||||
m_timestamp_query_allocation.put(), IID_PPV_ARGS(m_timestamp_query_buffer.put()));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.Error("D3D12: CreateResource() for timestamp failed with %08X", hr);
|
||||
@@ -638,15 +759,20 @@ bool GSDevice12::AllocatePreinitializedGPUBuffer(u32 size, ID3D12Resource** gpu_
|
||||
{
|
||||
// Try to place the fixed index buffer in GPU local memory.
|
||||
// Use the staging buffer to copy into it.
|
||||
const D3D12_RESOURCE_DESC rd = {D3D12_RESOURCE_DIMENSION_BUFFER, 0, size, 1, 1, 1, DXGI_FORMAT_UNKNOWN, {1, 0},
|
||||
D3D12_TEXTURE_LAYOUT_ROW_MAJOR, D3D12_RESOURCE_FLAG_NONE};
|
||||
const D3D12_RESOURCE_DESCU rd = {{D3D12_RESOURCE_DIMENSION_BUFFER, 0, size, 1, 1, 1, DXGI_FORMAT_UNKNOWN, {1, 0},
|
||||
D3D12_TEXTURE_LAYOUT_ROW_MAJOR, D3D12_RESOURCE_FLAG_NONE}};
|
||||
|
||||
const D3D12MA::ALLOCATION_DESC cpu_ad = {D3D12MA::ALLOCATION_FLAG_NONE, D3D12_HEAP_TYPE_UPLOAD};
|
||||
|
||||
ComPtr<ID3D12Resource> cpu_buffer;
|
||||
ComPtr<D3D12MA::Allocation> cpu_allocation;
|
||||
HRESULT hr = m_allocator->CreateResource(
|
||||
&cpu_ad, &rd, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, cpu_allocation.put(), IID_PPV_ARGS(cpu_buffer.put()));
|
||||
HRESULT hr;
|
||||
if (m_enhanced_barriers)
|
||||
hr = m_allocator->CreateResource3(
|
||||
&cpu_ad, &rd.desc1, D3D12_BARRIER_LAYOUT_UNDEFINED, nullptr, 0, nullptr, cpu_allocation.put(), IID_PPV_ARGS(cpu_buffer.put()));
|
||||
else
|
||||
hr = m_allocator->CreateResource(
|
||||
&cpu_ad, &rd.desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, cpu_allocation.put(), IID_PPV_ARGS(cpu_buffer.put()));
|
||||
pxAssertMsg(SUCCEEDED(hr), "Allocate CPU buffer");
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
@@ -662,21 +788,34 @@ bool GSDevice12::AllocatePreinitializedGPUBuffer(u32 size, ID3D12Resource** gpu_
|
||||
cpu_buffer->Unmap(0, &write_range);
|
||||
|
||||
const D3D12MA::ALLOCATION_DESC gpu_ad = {D3D12MA::ALLOCATION_FLAG_COMMITTED, D3D12_HEAP_TYPE_DEFAULT};
|
||||
|
||||
hr = m_allocator->CreateResource(
|
||||
&gpu_ad, &rd, D3D12_RESOURCE_STATE_COMMON, nullptr, gpu_allocation, IID_PPV_ARGS(gpu_buffer));
|
||||
if (m_enhanced_barriers)
|
||||
hr = m_allocator->CreateResource3(
|
||||
&gpu_ad, &rd.desc1, D3D12_BARRIER_LAYOUT_UNDEFINED, nullptr, 0, nullptr, gpu_allocation, IID_PPV_ARGS(gpu_buffer));
|
||||
else
|
||||
hr = m_allocator->CreateResource(
|
||||
&gpu_ad, &rd.desc, D3D12_RESOURCE_STATE_COMMON, nullptr, gpu_allocation, IID_PPV_ARGS(gpu_buffer));
|
||||
pxAssertMsg(SUCCEEDED(hr), "Allocate GPU buffer");
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
GetInitCommandList()->CopyBufferRegion(*gpu_buffer, 0, cpu_buffer.get(), 0, size);
|
||||
GetInitCommandList().list4->CopyBufferRegion(*gpu_buffer, 0, cpu_buffer.get(), 0, size);
|
||||
|
||||
D3D12_RESOURCE_BARRIER rb = {D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_BARRIER_FLAG_NONE};
|
||||
rb.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
rb.Transition.pResource = *gpu_buffer;
|
||||
rb.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; // COMMON -> COPY_DEST at first use.
|
||||
rb.Transition.StateAfter = D3D12_RESOURCE_STATE_INDEX_BUFFER;
|
||||
GetInitCommandList()->ResourceBarrier(1, &rb);
|
||||
if (m_enhanced_barriers)
|
||||
{
|
||||
const D3D12_BUFFER_BARRIER barrier = {D3D12_BARRIER_SYNC_COPY, D3D12_BARRIER_SYNC_INDEX_INPUT,
|
||||
D3D12_BARRIER_ACCESS_COPY_DEST, D3D12_BARRIER_ACCESS_INDEX_BUFFER, *gpu_buffer, 0, size};
|
||||
const D3D12_BARRIER_GROUP group = {.Type = D3D12_BARRIER_TYPE_BUFFER, .NumBarriers = 1, .pBufferBarriers = &barrier};
|
||||
GetInitCommandList().list7->Barrier(1, &group);
|
||||
}
|
||||
else
|
||||
{
|
||||
D3D12_RESOURCE_BARRIER rb = {D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_BARRIER_FLAG_NONE};
|
||||
rb.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
rb.Transition.pResource = *gpu_buffer;
|
||||
rb.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; // COMMON -> COPY_DEST at first use.
|
||||
rb.Transition.StateAfter = D3D12_RESOURCE_STATE_INDEX_BUFFER;
|
||||
GetInitCommandList().list4->ResourceBarrier(1, &rb);
|
||||
}
|
||||
|
||||
DeferResourceDestruction(cpu_allocation.get(), cpu_buffer.get());
|
||||
return true;
|
||||
@@ -768,7 +907,7 @@ void GSDevice12::Destroy()
|
||||
{
|
||||
GSDevice::Destroy();
|
||||
|
||||
if (GetCommandList())
|
||||
if (GetCommandList().list4)
|
||||
{
|
||||
EndRenderPass();
|
||||
ExecuteCommandList(true);
|
||||
@@ -799,6 +938,7 @@ void GSDevice12::SetVSyncMode(GSVSyncMode mode, bool allow_present_throttle)
|
||||
|
||||
if (GetSwapChainBufferCount() != old_buffer_count)
|
||||
{
|
||||
ExecuteCommandList(true);
|
||||
DestroySwapChain();
|
||||
if (!CreateSwapChain())
|
||||
pxFailRel("Failed to recreate swap chain after vsync change.");
|
||||
@@ -919,11 +1059,11 @@ bool GSDevice12::CreateSwapChain()
|
||||
// Render a frame as soon as possible to clear out whatever was previously being displayed.
|
||||
EndRenderPass();
|
||||
GSTexture12* swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer].get();
|
||||
ID3D12GraphicsCommandList4* cmdlist = GetCommandList();
|
||||
const D3D12CommandList& cmdlist = GetCommandList();
|
||||
m_current_swap_chain_buffer = ((m_current_swap_chain_buffer + 1) % static_cast<u32>(m_swap_chain_buffers.size()));
|
||||
swap_chain_buf->TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||
cmdlist->ClearRenderTargetView(swap_chain_buf->GetWriteDescriptor(), s_present_clear_color.data(), 0, nullptr);
|
||||
swap_chain_buf->TransitionToState(cmdlist, D3D12_RESOURCE_STATE_PRESENT);
|
||||
swap_chain_buf->TransitionToState(cmdlist, GSTexture12::ResourceState::RenderTarget);
|
||||
cmdlist.list4->ClearRenderTargetView(swap_chain_buf->GetWriteDescriptor(), s_present_clear_color.data(), 0, nullptr);
|
||||
swap_chain_buf->TransitionToState(cmdlist, GSTexture12::ResourceState::Present);
|
||||
ExecuteCommandList(false);
|
||||
m_swap_chain->Present(0, m_using_allow_tearing ? DXGI_PRESENT_ALLOW_TEARING : 0);
|
||||
return true;
|
||||
@@ -950,7 +1090,7 @@ bool GSDevice12::CreateSwapChainRTV()
|
||||
std::unique_ptr<GSTexture12> tex = GSTexture12::Adopt(std::move(backbuffer), GSTexture::Type::RenderTarget,
|
||||
GSTexture::Format::Color, swap_chain_desc.BufferDesc.Width, swap_chain_desc.BufferDesc.Height, 1,
|
||||
swap_chain_desc.BufferDesc.Format, DXGI_FORMAT_UNKNOWN, swap_chain_desc.BufferDesc.Format,
|
||||
DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, D3D12_RESOURCE_STATE_COMMON);
|
||||
DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, GSTexture12::ResourceState::Present);
|
||||
if (!tex)
|
||||
{
|
||||
m_swap_chain_buffers.clear();
|
||||
@@ -1122,18 +1262,18 @@ GSDevice::PresentResult GSDevice12::BeginPresent(bool frame_skip)
|
||||
|
||||
GSTexture12* swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer].get();
|
||||
|
||||
ID3D12GraphicsCommandList* cmdlist = GetCommandList();
|
||||
swap_chain_buf->TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||
cmdlist->ClearRenderTargetView(swap_chain_buf->GetWriteDescriptor(), s_present_clear_color.data(), 0, nullptr);
|
||||
cmdlist->OMSetRenderTargets(1, &swap_chain_buf->GetWriteDescriptor().cpu_handle, FALSE, nullptr);
|
||||
const D3D12CommandList& cmdlist = GetCommandList();
|
||||
swap_chain_buf->TransitionToState(cmdlist, GSTexture12::ResourceState::RenderTarget);
|
||||
cmdlist.list4->ClearRenderTargetView(swap_chain_buf->GetWriteDescriptor(), s_present_clear_color.data(), 0, nullptr);
|
||||
cmdlist.list4->OMSetRenderTargets(1, &swap_chain_buf->GetWriteDescriptor().cpu_handle, FALSE, nullptr);
|
||||
g_perfmon.Put(GSPerfMon::RenderPasses, 1);
|
||||
|
||||
const D3D12_VIEWPORT vp{0.0f, 0.0f, static_cast<float>(m_window_info.surface_width),
|
||||
static_cast<float>(m_window_info.surface_height), 0.0f, 1.0f};
|
||||
const D3D12_RECT scissor{
|
||||
0, 0, static_cast<LONG>(m_window_info.surface_width), static_cast<LONG>(m_window_info.surface_height)};
|
||||
cmdlist->RSSetViewports(1, &vp);
|
||||
cmdlist->RSSetScissorRects(1, &scissor);
|
||||
cmdlist.list4->RSSetViewports(1, &vp);
|
||||
cmdlist.list4->RSSetScissorRects(1, &scissor);
|
||||
return PresentResult::OK;
|
||||
}
|
||||
|
||||
@@ -1144,7 +1284,7 @@ void GSDevice12::EndPresent()
|
||||
GSTexture12* swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer].get();
|
||||
m_current_swap_chain_buffer = ((m_current_swap_chain_buffer + 1) % static_cast<u32>(m_swap_chain_buffers.size()));
|
||||
|
||||
swap_chain_buf->TransitionToState(GetCommandList(), D3D12_RESOURCE_STATE_PRESENT);
|
||||
swap_chain_buf->TransitionToState(GSTexture12::ResourceState::Present);
|
||||
if (!ExecuteCommandList(WaitType::None))
|
||||
{
|
||||
m_device_lost = true;
|
||||
@@ -1187,7 +1327,7 @@ void GSDevice12::PushDebugGroup(const char* fmt, ...)
|
||||
const UINT color = Palette(
|
||||
++s_debug_scope_depth, {0.5f, 0.5f, 0.5f}, {0.5f, 0.5f, 0.5f}, {1.0f, 1.0f, 0.5f}, {0.8f, 0.90f, 0.30f});
|
||||
|
||||
PIXBeginEvent(GetCommandList(), color, "%s", buf.c_str());
|
||||
PIXBeginEvent(GetCommandList().list4.get(), color, "%s", buf.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1199,7 +1339,7 @@ void GSDevice12::PopDebugGroup()
|
||||
|
||||
s_debug_scope_depth = (s_debug_scope_depth == 0) ? 0 : (s_debug_scope_depth - 1u);
|
||||
|
||||
PIXEndEvent(GetCommandList());
|
||||
PIXEndEvent(GetCommandList().list4.get());
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1230,7 +1370,7 @@ void GSDevice12::InsertDebugMessage(DebugMessageCategory category, const char* f
|
||||
static_cast<BYTE>(fcolor[1] * 255.0f),
|
||||
static_cast<BYTE>(fcolor[2] * 255.0f));
|
||||
|
||||
PIXSetMarker(GetCommandList(), color, "%s", buf.c_str());
|
||||
PIXSetMarker(GetCommandList().list4.get(), color, "%s", buf.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1263,30 +1403,43 @@ bool GSDevice12::CheckFeatures(const u32& vendor_id)
|
||||
Console.WriteLnFmt("D3D12: Programmable Sample Position: {}", m_programmable_sample_positions ? "Supported" : "Not Supported");
|
||||
|
||||
BOOL allow_tearing_supported = false;
|
||||
const HRESULT hr = m_dxgi_factory->CheckFeatureSupport(
|
||||
HRESULT hr = m_dxgi_factory->CheckFeatureSupport(
|
||||
DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported, sizeof(allow_tearing_supported));
|
||||
m_allow_tearing_supported = (SUCCEEDED(hr) && allow_tearing_supported == TRUE);
|
||||
|
||||
D3D12_FEATURE_DATA_D3D12_OPTIONS12 device_options12 = {};
|
||||
hr = m_device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS12, &device_options12, sizeof(device_options12));
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
Console.WriteLnFmt("D3D12: Enhanced Barriers: {}", device_options12.EnhancedBarriersSupported ? "Supported" : "Not Supported");
|
||||
m_enhanced_barriers = device_options12.EnhancedBarriersSupported;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLnFmt("D3D12: Failed to check for Enhanced Barriers: 0x{:08x}", static_cast<unsigned long>(hr));
|
||||
m_enhanced_barriers = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GSDevice12::DrawPrimitive()
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
|
||||
GetCommandList()->DrawInstanced(m_vertex.count, 1, m_vertex.start, 0);
|
||||
GetCommandList().list4->DrawInstanced(m_vertex.count, 1, m_vertex.start, 0);
|
||||
}
|
||||
|
||||
void GSDevice12::DrawIndexedPrimitive()
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
|
||||
GetCommandList()->DrawIndexedInstanced(m_index.count, 1, m_index.start, m_vertex.start, 0);
|
||||
GetCommandList().list4->DrawIndexedInstanced(m_index.count, 1, m_index.start, m_vertex.start, 0);
|
||||
}
|
||||
|
||||
void GSDevice12::DrawIndexedPrimitive(int offset, int count)
|
||||
{
|
||||
pxAssert(offset + count <= (int)m_index.count);
|
||||
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
|
||||
GetCommandList()->DrawIndexedInstanced(count, 1, m_index.start + offset, m_vertex.start, 0);
|
||||
GetCommandList().list4->DrawIndexedInstanced(count, 1, m_index.start + offset, m_vertex.start, 0);
|
||||
}
|
||||
|
||||
void GSDevice12::LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_format, DXGI_FORMAT* srv_format,
|
||||
@@ -1391,14 +1544,14 @@ void GSDevice12::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r,
|
||||
|
||||
if (dTex12->GetType() != GSTexture::Type::DepthStencil)
|
||||
{
|
||||
dTex12->TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||
GetCommandList()->ClearRenderTargetView(
|
||||
dTex12->TransitionToState(GSTexture12::ResourceState::RenderTarget);
|
||||
GetCommandList().list4->ClearRenderTargetView(
|
||||
dTex12->GetWriteDescriptor(), sTex12->GetUNormClearColor().v, 0, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
dTex12->TransitionToState(D3D12_RESOURCE_STATE_DEPTH_WRITE);
|
||||
GetCommandList()->ClearDepthStencilView(
|
||||
dTex12->TransitionToState(GSTexture12::ResourceState::DepthWriteStencil);
|
||||
GetCommandList().list4->ClearDepthStencilView(
|
||||
dTex12->GetWriteDescriptor(), D3D12_CLEAR_FLAG_DEPTH, sTex12->GetClearDepth(), 0, 0, nullptr);
|
||||
}
|
||||
|
||||
@@ -1418,12 +1571,12 @@ void GSDevice12::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r,
|
||||
|
||||
EndRenderPass();
|
||||
|
||||
sTex12->TransitionToState(D3D12_RESOURCE_STATE_COPY_SOURCE);
|
||||
sTex12->TransitionToState(GSTexture12::ResourceState::CopySrc);
|
||||
sTex12->SetUseFenceCounter(GetCurrentFenceValue());
|
||||
if (m_tfx_textures[0] && sTex12->GetSRVDescriptor() == m_tfx_textures[0])
|
||||
PSSetShaderResource(0, nullptr, false);
|
||||
|
||||
dTex12->TransitionToState(D3D12_RESOURCE_STATE_COPY_DEST);
|
||||
dTex12->TransitionToState(GSTexture12::ResourceState::CopyDst);
|
||||
dTex12->SetUseFenceCounter(GetCurrentFenceValue());
|
||||
|
||||
D3D12_TEXTURE_COPY_LOCATION srcloc;
|
||||
@@ -1440,13 +1593,13 @@ void GSDevice12::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r,
|
||||
const bool full_rt_copy = src_dst_rect_match && ((sTex12->IsDepthStencil() && !m_programmable_sample_positions) || (destX == 0 && destY == 0 && r.eq(src_rect)));
|
||||
if (full_rt_copy)
|
||||
{
|
||||
GetCommandList()->CopyResource(dTex12->GetResource(), sTex12->GetResource());
|
||||
GetCommandList().list4->CopyResource(dTex12->GetResource(), sTex12->GetResource());
|
||||
}
|
||||
else
|
||||
{
|
||||
const D3D12_BOX srcbox{static_cast<UINT>(r.left), static_cast<UINT>(r.top), 0u, static_cast<UINT>(r.right),
|
||||
static_cast<UINT>(r.bottom), 1u};
|
||||
GetCommandList()->CopyTextureRegion(&dstloc, destX, destY, 0, &srcloc, &srcbox);
|
||||
GetCommandList().list4->CopyTextureRegion(&dstloc, destX, destY, 0, &srcloc, &srcbox);
|
||||
}
|
||||
|
||||
dTex12->SetState(GSTexture::State::Dirty);
|
||||
@@ -1483,14 +1636,14 @@ void GSDevice12::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};
|
||||
SetUtilityRootSignature();
|
||||
SetUtilityPushConstants(&cb, sizeof(cb));
|
||||
|
||||
@@ -1504,14 +1657,15 @@ void GSDevice12::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};
|
||||
SetUtilityRootSignature();
|
||||
SetUtilityPushConstants(&cb, sizeof(cb));
|
||||
|
||||
@@ -1523,7 +1677,7 @@ void GSDevice12::ConvertToIndexedTexture(
|
||||
|
||||
void GSDevice12::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;
|
||||
@@ -1560,10 +1714,10 @@ void GSDevice12::DrawMultiStretchRects(
|
||||
{
|
||||
GSTexture12* const stex = static_cast<GSTexture12*>(rects[i].src);
|
||||
stex->CommitClear();
|
||||
if (stex->GetResourceState() != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)
|
||||
if (stex->GetResourceState() != GSTexture12::ResourceState::PixelShaderResource)
|
||||
{
|
||||
EndRenderPass();
|
||||
stex->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
stex->TransitionToState(GSTexture12::ResourceState::PixelShaderResource);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1689,11 +1843,11 @@ void GSDevice12::BeginRenderPassForStretchRect(
|
||||
void GSDevice12::DoStretchRect(GSTexture12* sTex, const GSVector4& sRect, GSTexture12* dTex, const GSVector4& dRect,
|
||||
const ID3D12PipelineState* pipeline, bool linear, bool allow_discard)
|
||||
{
|
||||
if (sTex->GetResourceState() != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)
|
||||
if (sTex->GetResourceState() != GSTexture12::ResourceState::PixelShaderResource)
|
||||
{
|
||||
// can't transition in a render pass
|
||||
EndRenderPass();
|
||||
sTex->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
sTex->TransitionToState(GSTexture12::ResourceState::PixelShaderResource);
|
||||
}
|
||||
|
||||
SetUtilityRootSignature();
|
||||
@@ -1770,14 +1924,14 @@ void GSDevice12::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
|
||||
if (has_input_0)
|
||||
{
|
||||
static_cast<GSTexture12*>(sTex[0])->CommitClear();
|
||||
static_cast<GSTexture12*>(sTex[0])->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
static_cast<GSTexture12*>(sTex[0])->TransitionToState(GSTexture12::ResourceState::PixelShaderResource);
|
||||
}
|
||||
if (has_input_1)
|
||||
{
|
||||
static_cast<GSTexture12*>(sTex[1])->CommitClear();
|
||||
static_cast<GSTexture12*>(sTex[1])->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
static_cast<GSTexture12*>(sTex[1])->TransitionToState(GSTexture12::ResourceState::PixelShaderResource);
|
||||
}
|
||||
static_cast<GSTexture12*>(dTex)->TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||
static_cast<GSTexture12*>(dTex)->TransitionToState(GSTexture12::ResourceState::RenderTarget);
|
||||
|
||||
// Upload constant to select YUV algo, but skip constant buffer update if we don't need it
|
||||
if (feedback_write_2 || feedback_write_1 || sTex[0])
|
||||
@@ -1830,7 +1984,7 @@ void GSDevice12::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
|
||||
if (sTex[0] == sTex[2])
|
||||
{
|
||||
// need a barrier here because of the render pass
|
||||
static_cast<GSTexture12*>(sTex[2])->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
static_cast<GSTexture12*>(sTex[2])->TransitionToState(GSTexture12::ResourceState::PixelShaderResource);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1877,13 +2031,13 @@ void GSDevice12::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
|
||||
|
||||
// this texture is going to get used as an input, so make sure we don't read undefined data
|
||||
static_cast<GSTexture12*>(dTex)->CommitClear();
|
||||
static_cast<GSTexture12*>(dTex)->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
static_cast<GSTexture12*>(dTex)->TransitionToState(GSTexture12::ResourceState::PixelShaderResource);
|
||||
}
|
||||
|
||||
void GSDevice12::DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
|
||||
ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb)
|
||||
{
|
||||
static_cast<GSTexture12*>(dTex)->TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||
static_cast<GSTexture12*>(dTex)->TransitionToState(GSTexture12::ResourceState::RenderTarget);
|
||||
|
||||
const GSVector4i rc = GSVector4i(dRect);
|
||||
const GSVector4i dtex_rc = dTex->GetRect();
|
||||
@@ -1899,7 +2053,7 @@ void GSDevice12::DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture*
|
||||
EndRenderPass();
|
||||
|
||||
// this texture is going to get used as an input, so make sure we don't read undefined data
|
||||
static_cast<GSTexture12*>(dTex)->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
static_cast<GSTexture12*>(dTex)->TransitionToState(GSTexture12::ResourceState::PixelShaderResource);
|
||||
}
|
||||
|
||||
void GSDevice12::DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4])
|
||||
@@ -1918,7 +2072,7 @@ void GSDevice12::DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float para
|
||||
DrawStretchRect(sRect, GSVector4(dRect), dTex->GetSize());
|
||||
EndRenderPass();
|
||||
|
||||
static_cast<GSTexture12*>(dTex)->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
static_cast<GSTexture12*>(dTex)->TransitionToState(GSTexture12::ResourceState::PixelShaderResource);
|
||||
}
|
||||
|
||||
void GSDevice12::DoFXAA(GSTexture* sTex, GSTexture* dTex)
|
||||
@@ -1936,7 +2090,7 @@ void GSDevice12::DoFXAA(GSTexture* sTex, GSTexture* dTex)
|
||||
DrawStretchRect(sRect, GSVector4(dRect), dTex->GetSize());
|
||||
EndRenderPass();
|
||||
|
||||
static_cast<GSTexture12*>(dTex)->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
static_cast<GSTexture12*>(dTex)->TransitionToState(GSTexture12::ResourceState::PixelShaderResource);
|
||||
}
|
||||
|
||||
bool GSDevice12::CompileCASPipelines()
|
||||
@@ -2032,7 +2186,7 @@ void GSDevice12::RenderImGui()
|
||||
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 },
|
||||
@@ -2100,7 +2254,7 @@ void GSDevice12::RenderImGui()
|
||||
D3D12DescriptorHandle handle = m_null_texture->GetSRVDescriptor();
|
||||
if (tex)
|
||||
{
|
||||
tex->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
tex->TransitionToState(GSTexture12::ResourceState::PixelShaderResource);
|
||||
handle = tex->GetSRVDescriptor();
|
||||
}
|
||||
|
||||
@@ -2118,7 +2272,7 @@ void GSDevice12::RenderImGui()
|
||||
|
||||
if (ApplyUtilityState())
|
||||
{
|
||||
GetCommandList()->DrawIndexedInstanced(
|
||||
GetCommandList().list4->DrawIndexedInstanced(
|
||||
pcmd->ElemCount, 1, m_index.start + pcmd->IdxOffset, vertex_offset + pcmd->VtxOffset, 0);
|
||||
}
|
||||
}
|
||||
@@ -2147,23 +2301,23 @@ bool GSDevice12::DoCAS(
|
||||
}
|
||||
}
|
||||
|
||||
ID3D12GraphicsCommandList* const cmdlist = GetCommandList();
|
||||
const D3D12_RESOURCE_STATES old_state = sTex12->GetResourceState();
|
||||
sTex12->TransitionToState(cmdlist, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
|
||||
dTex12->TransitionToState(cmdlist, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
|
||||
const D3D12CommandList& cmdlist = GetCommandList();
|
||||
const GSTexture12::ResourceState old_state = sTex12->GetResourceState();
|
||||
sTex12->TransitionToState(cmdlist, GSTexture12::ResourceState::ComputeShaderResource);
|
||||
dTex12->TransitionToState(cmdlist, GSTexture12::ResourceState::CASShaderUAV);
|
||||
|
||||
cmdlist->SetComputeRootSignature(m_cas_root_signature.get());
|
||||
cmdlist->SetComputeRoot32BitConstants(
|
||||
cmdlist.list4->SetComputeRootSignature(m_cas_root_signature.get());
|
||||
cmdlist.list4->SetComputeRoot32BitConstants(
|
||||
CAS_ROOT_SIGNATURE_PARAM_PUSH_CONSTANTS, NUM_CAS_CONSTANTS, constants.data(), 0);
|
||||
cmdlist->SetComputeRootDescriptorTable(CAS_ROOT_SIGNATURE_PARAM_SRC_TEXTURE, sTexDH);
|
||||
cmdlist->SetComputeRootDescriptorTable(CAS_ROOT_SIGNATURE_PARAM_DST_TEXTURE, dTexDH);
|
||||
cmdlist->SetPipelineState(sharpen_only ? m_cas_sharpen_pipeline.get() : m_cas_upscale_pipeline.get());
|
||||
cmdlist.list4->SetComputeRootDescriptorTable(CAS_ROOT_SIGNATURE_PARAM_SRC_TEXTURE, sTexDH);
|
||||
cmdlist.list4->SetComputeRootDescriptorTable(CAS_ROOT_SIGNATURE_PARAM_DST_TEXTURE, dTexDH);
|
||||
cmdlist.list4->SetPipelineState(sharpen_only ? m_cas_sharpen_pipeline.get() : m_cas_upscale_pipeline.get());
|
||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE;
|
||||
|
||||
static const int threadGroupWorkRegionDim = 16;
|
||||
const int dispatchX = (dTex->GetWidth() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
||||
const int dispatchY = (dTex->GetHeight() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
||||
cmdlist->Dispatch(dispatchX, dispatchY, 1);
|
||||
cmdlist.list4->Dispatch(dispatchX, dispatchY, 1);
|
||||
|
||||
sTex12->TransitionToState(cmdlist, old_state);
|
||||
return true;
|
||||
@@ -2243,9 +2397,9 @@ void GSDevice12::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector
|
||||
if (!InRenderPass())
|
||||
{
|
||||
if (vkRt)
|
||||
vkRt->TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||
vkRt->TransitionToState(GSTexture12::ResourceState::RenderTarget);
|
||||
if (vkDs)
|
||||
vkDs->TransitionToState(depth_read ? (D3D12_RESOURCE_STATE_DEPTH_READ | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) : D3D12_RESOURCE_STATE_DEPTH_WRITE);
|
||||
vkDs->TransitionToState(depth_read ? GSTexture12::ResourceState::DepthReadStencil : GSTexture12::ResourceState::DepthWriteStencil);
|
||||
}
|
||||
|
||||
// This is used to set/initialize the framebuffer for tfx rendering.
|
||||
@@ -2374,7 +2528,7 @@ bool GSDevice12::CreateNullTexture()
|
||||
if (!m_null_texture)
|
||||
return false;
|
||||
|
||||
m_null_texture->TransitionToState(GetCommandList(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
m_null_texture->TransitionToState(GSTexture12::ResourceState::PixelShaderResource);
|
||||
D3D12::SetObjectName(m_null_texture->GetResource(), "Null texture");
|
||||
return true;
|
||||
}
|
||||
@@ -3153,7 +3307,7 @@ void GSDevice12::ExecuteCommandListAndRestartRenderPass(bool wait_for_completion
|
||||
if (was_in_render_pass)
|
||||
{
|
||||
// rebind everything except RT, because the RP does that for us
|
||||
ApplyBaseState(m_dirty_flags & ~DIRTY_FLAG_RENDER_TARGET, GetCommandList());
|
||||
ApplyBaseState(m_dirty_flags & ~DIRTY_FLAG_RENDER_TARGET, GetCommandList().list4.get());
|
||||
m_dirty_flags &= ~DIRTY_BASE_STATE;
|
||||
|
||||
// restart render pass
|
||||
@@ -3244,17 +3398,17 @@ void GSDevice12::PSSetShaderResource(int i, GSTexture* sr, bool check_state, boo
|
||||
GSTexture12* dtex = static_cast<GSTexture12*>(sr);
|
||||
if (check_state)
|
||||
{
|
||||
if (dtex->GetResourceState() != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE && InRenderPass())
|
||||
if (dtex->GetResourceState() != GSTexture12::ResourceState::PixelShaderResource && InRenderPass())
|
||||
{
|
||||
GL_INS("Ending render pass due to resource transition");
|
||||
EndRenderPass();
|
||||
}
|
||||
|
||||
dtex->CommitClear();
|
||||
dtex->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
dtex->TransitionToState(GSTexture12::ResourceState::PixelShaderResource);
|
||||
}
|
||||
dtex->SetUseFenceCounter(GetCurrentFenceValue());
|
||||
handle = feedback ? dtex->GetFBLDescriptor() : dtex->GetSRVDescriptor();
|
||||
handle = (feedback && !m_enhanced_barriers) ? dtex->GetFBLDescriptor() : dtex->GetSRVDescriptor();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3285,7 +3439,7 @@ void GSDevice12::SetUtilityRootSignature()
|
||||
|
||||
m_current_root_signature = RootSignature::Utility;
|
||||
m_dirty_flags |= DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE | DIRTY_FLAG_SAMPLERS_DESCRIPTOR_TABLE | DIRTY_FLAG_PIPELINE;
|
||||
GetCommandList()->SetGraphicsRootSignature(m_utility_root_signature.get());
|
||||
GetCommandList().list4->SetGraphicsRootSignature(m_utility_root_signature.get());
|
||||
}
|
||||
|
||||
void GSDevice12::SetUtilityTexture(GSTexture* dtex, const D3D12DescriptorHandle& sampler)
|
||||
@@ -3295,7 +3449,7 @@ void GSDevice12::SetUtilityTexture(GSTexture* dtex, const D3D12DescriptorHandle&
|
||||
{
|
||||
GSTexture12* d12tex = static_cast<GSTexture12*>(dtex);
|
||||
d12tex->CommitClear();
|
||||
d12tex->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
d12tex->TransitionToState(GSTexture12::ResourceState::PixelShaderResource);
|
||||
d12tex->SetUseFenceCounter(GetCurrentFenceValue());
|
||||
handle = d12tex->GetSRVDescriptor();
|
||||
}
|
||||
@@ -3333,7 +3487,7 @@ void GSDevice12::SetUtilityTexture(GSTexture* dtex, const D3D12DescriptorHandle&
|
||||
|
||||
void GSDevice12::SetUtilityPushConstants(const void* data, u32 size)
|
||||
{
|
||||
GetCommandList()->SetGraphicsRoot32BitConstants(
|
||||
GetCommandList().list4->SetGraphicsRoot32BitConstants(
|
||||
UTILITY_ROOT_SIGNATURE_PARAM_PUSH_CONSTANTS, (size + 3) / sizeof(u32), data, 0);
|
||||
}
|
||||
|
||||
@@ -3396,13 +3550,13 @@ void GSDevice12::RenderTextureMipmap(
|
||||
}
|
||||
|
||||
// *now* we don't have to worry about running out of anything.
|
||||
ID3D12GraphicsCommandList* cmdlist = GetCommandList();
|
||||
if (texture->GetResourceState() != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)
|
||||
const D3D12CommandList& cmdlist = GetCommandList();
|
||||
if (texture->GetResourceState() != GSTexture12::ResourceState::PixelShaderResource)
|
||||
texture->TransitionSubresourceToState(
|
||||
cmdlist, src_level, texture->GetResourceState(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
if (texture->GetResourceState() != D3D12_RESOURCE_STATE_RENDER_TARGET)
|
||||
cmdlist, src_level, texture->GetResourceState(), GSTexture12::ResourceState::PixelShaderResource);
|
||||
if (texture->GetResourceState() != GSTexture12::ResourceState::RenderTarget)
|
||||
texture->TransitionSubresourceToState(
|
||||
cmdlist, dst_level, texture->GetResourceState(), D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||
cmdlist, dst_level, texture->GetResourceState(), GSTexture12::ResourceState::RenderTarget);
|
||||
|
||||
// We set the state directly here.
|
||||
constexpr u32 MODIFIED_STATE = DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR | DIRTY_FLAG_RENDER_TARGET;
|
||||
@@ -3410,14 +3564,14 @@ void GSDevice12::RenderTextureMipmap(
|
||||
|
||||
// Using a render pass is probably a bit overkill.
|
||||
const D3D12_DISCARD_REGION discard_region = {0u, nullptr, dst_level, 1u};
|
||||
cmdlist->DiscardResource(texture->GetResource(), &discard_region);
|
||||
cmdlist->OMSetRenderTargets(1, &rtv_handle.cpu_handle, FALSE, nullptr);
|
||||
cmdlist.list4->DiscardResource(texture->GetResource(), &discard_region);
|
||||
cmdlist.list4->OMSetRenderTargets(1, &rtv_handle.cpu_handle, FALSE, nullptr);
|
||||
|
||||
const D3D12_VIEWPORT vp = {0.0f, 0.0f, static_cast<float>(dst_width), static_cast<float>(dst_height), 0.0f, 1.0f};
|
||||
cmdlist->RSSetViewports(1, &vp);
|
||||
cmdlist.list4->RSSetViewports(1, &vp);
|
||||
|
||||
const D3D12_RECT scissor = {0, 0, static_cast<LONG>(dst_width), static_cast<LONG>(dst_height)};
|
||||
cmdlist->RSSetScissorRects(1, &scissor);
|
||||
cmdlist.list4->RSSetScissorRects(1, &scissor);
|
||||
|
||||
SetUtilityRootSignature();
|
||||
SetPipeline(m_convert[static_cast<int>(ShaderConvert::COPY)].get());
|
||||
@@ -3425,12 +3579,12 @@ void GSDevice12::RenderTextureMipmap(
|
||||
GSVector4(0.0f, 0.0f, static_cast<float>(dst_width), static_cast<float>(dst_height)),
|
||||
GSVector2i(dst_width, dst_height));
|
||||
|
||||
if (texture->GetResourceState() != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)
|
||||
if (texture->GetResourceState() != GSTexture12::ResourceState::PixelShaderResource)
|
||||
texture->TransitionSubresourceToState(
|
||||
cmdlist, src_level, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, texture->GetResourceState());
|
||||
if (texture->GetResourceState() != D3D12_RESOURCE_STATE_RENDER_TARGET)
|
||||
cmdlist, src_level, GSTexture12::ResourceState::PixelShaderResource, texture->GetResourceState());
|
||||
if (texture->GetResourceState() != GSTexture12::ResourceState::RenderTarget)
|
||||
texture->TransitionSubresourceToState(
|
||||
cmdlist, dst_level, D3D12_RESOURCE_STATE_RENDER_TARGET, texture->GetResourceState());
|
||||
cmdlist, dst_level, GSTexture12::ResourceState::RenderTarget, texture->GetResourceState());
|
||||
|
||||
// Must destroy after current cmdlist.
|
||||
DeferDescriptorDestruction(m_descriptor_heap_manager, &srv_handle);
|
||||
@@ -3496,7 +3650,7 @@ void GSDevice12::BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE color_b
|
||||
}
|
||||
}
|
||||
|
||||
GetCommandList()->BeginRenderPass(m_current_render_target ? 1 : 0,
|
||||
GetCommandList().list4->BeginRenderPass(m_current_render_target ? 1 : 0,
|
||||
m_current_render_target ? &rt : nullptr, m_current_depth_target ? &ds : nullptr,
|
||||
(m_current_depth_target && m_current_depth_read_only) ? (D3D12_RENDER_PASS_FLAG_BIND_READ_ONLY_DEPTH) : D3D12_RENDER_PASS_FLAG_NONE);
|
||||
}
|
||||
@@ -3513,7 +3667,7 @@ void GSDevice12::EndRenderPass()
|
||||
|
||||
g_perfmon.Put(GSPerfMon::RenderPasses, 1);
|
||||
|
||||
GetCommandList()->EndRenderPass();
|
||||
GetCommandList().list4->EndRenderPass();
|
||||
}
|
||||
|
||||
void GSDevice12::SetViewport(const D3D12_VIEWPORT& viewport)
|
||||
@@ -3675,7 +3829,7 @@ bool GSDevice12::ApplyTFXState(bool already_execed)
|
||||
flags |= DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE_2;
|
||||
}
|
||||
|
||||
ID3D12GraphicsCommandList* cmdlist = GetCommandList();
|
||||
ID3D12GraphicsCommandList* cmdlist = GetCommandList().list4.get();
|
||||
|
||||
if (m_current_root_signature != RootSignature::TFX)
|
||||
{
|
||||
@@ -3714,7 +3868,7 @@ bool GSDevice12::ApplyUtilityState(bool already_execed)
|
||||
u32 flags = m_dirty_flags;
|
||||
m_dirty_flags &= ~DIRTY_UTILITY_STATE;
|
||||
|
||||
ID3D12GraphicsCommandList* cmdlist = GetCommandList();
|
||||
ID3D12GraphicsCommandList* cmdlist = GetCommandList().list4.get();
|
||||
|
||||
if (m_current_root_signature != RootSignature::Utility)
|
||||
{
|
||||
@@ -3846,11 +4000,37 @@ GSTexture12* GSDevice12::SetupPrimitiveTrackingDATE(GSHWDrawConfig& config, Pipe
|
||||
config.alpha_second_pass.ps.date = 3;
|
||||
|
||||
// and bind the image to the primitive sampler
|
||||
image->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
image->TransitionToState(GSTexture12::ResourceState::PixelShaderResource);
|
||||
PSSetShaderResource(3, image, false);
|
||||
return image;
|
||||
}
|
||||
|
||||
void GSDevice12::FeedbackBarrier(const GSTexture12* texture)
|
||||
{
|
||||
if (m_enhanced_barriers)
|
||||
{
|
||||
// Enhanced barriers allows for single resource feedback.
|
||||
const D3D12_BARRIER_SYNC sync = D3D12_BARRIER_SYNC_RENDER_TARGET | D3D12_BARRIER_SYNC_PIXEL_SHADING;
|
||||
const D3D12_BARRIER_ACCESS access = D3D12_BARRIER_ACCESS_RENDER_TARGET | D3D12_BARRIER_ACCESS_SHADER_RESOURCE;
|
||||
const D3D12_TEXTURE_BARRIER barrier = {sync, sync, access, access, D3D12_BARRIER_LAYOUT_COMMON, D3D12_BARRIER_LAYOUT_COMMON,
|
||||
texture->GetResource(), {D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, 0, 0, 0, 0, 0}, D3D12_TEXTURE_BARRIER_FLAG_NONE};
|
||||
const D3D12_BARRIER_GROUP group = {.Type = D3D12_BARRIER_TYPE_TEXTURE, .NumBarriers = 1, .pTextureBarriers = &barrier};
|
||||
GetCommandList().list7->Barrier(1, &group);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The DX12 spec notes "You may not read from, or consume, a write that occurred within the same render pass".
|
||||
// The only exception being the implicit reads for render target blending or depth testing.
|
||||
// Thus, in addition to a barrier, we need to end the render pass.
|
||||
EndRenderPass();
|
||||
// Specify null for the after resource as both resources are used after the barrier.
|
||||
// While this may also be true before the barrier, we only write using the main resource.
|
||||
D3D12_RESOURCE_BARRIER barrier = {D3D12_RESOURCE_BARRIER_TYPE_ALIASING, D3D12_RESOURCE_BARRIER_FLAG_NONE};
|
||||
barrier.Aliasing = {texture->GetResource(), nullptr};
|
||||
GetCommandList().list4->ResourceBarrier(1, &barrier);
|
||||
}
|
||||
}
|
||||
|
||||
void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
{
|
||||
|
||||
@@ -3871,12 +4051,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
if (m_features.texture_barrier && config.tex && (config.tex == config.rt) && !(config.require_one_barrier || config.require_full_barrier))
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::Barriers, 1);
|
||||
|
||||
EndRenderPass();
|
||||
// Specify null for the after resource as both resources are used after the barrier.
|
||||
D3D12_RESOURCE_BARRIER barrier = {D3D12_RESOURCE_BARRIER_TYPE_ALIASING, D3D12_RESOURCE_BARRIER_FLAG_NONE};
|
||||
barrier.Aliasing = {draw_rt->GetResource(), nullptr};
|
||||
GetCommandList()->ResourceBarrier(1, &barrier);
|
||||
FeedbackBarrier(draw_rt);
|
||||
}
|
||||
|
||||
// now blit the colclip texture back to the original target
|
||||
@@ -3887,7 +4062,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
GL_PUSH("Blit ColorClip back to RT");
|
||||
|
||||
EndRenderPass();
|
||||
colclip_rt->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
colclip_rt->TransitionToState(GSTexture12::ResourceState::PixelShaderResource);
|
||||
|
||||
draw_rt = static_cast<GSTexture12*>(config.rt);
|
||||
OMSetRenderTargets(draw_rt, draw_ds, config.colclip_update_area);
|
||||
@@ -4001,7 +4176,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
else if (draw_rt->GetState() == GSTexture::State::Dirty)
|
||||
{
|
||||
GL_PUSH_("ColorClip Render Target Setup");
|
||||
draw_rt->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
draw_rt->TransitionToState(GSTexture12::ResourceState::PixelShaderResource);
|
||||
}
|
||||
|
||||
// we're not drawing to the RT, so we can use it as a source
|
||||
@@ -4064,10 +4239,10 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
{
|
||||
EndRenderPass();
|
||||
// Make sure the DSV is in writeable state
|
||||
draw_ds->TransitionToState(D3D12_RESOURCE_STATE_DEPTH_WRITE);
|
||||
draw_ds->TransitionToState(GSTexture12::ResourceState::DepthWriteStencil);
|
||||
|
||||
D3D12_RECT rect = {config.drawarea.left, config.drawarea.top, config.drawarea.left + config.drawarea.width(), config.drawarea.top + config.drawarea.height()};
|
||||
GetCommandList()->ClearDepthStencilView(draw_ds->GetWriteDescriptor(), D3D12_CLEAR_FLAG_STENCIL, 0.0f, 1, 1, &rect);
|
||||
GetCommandList().list4->ClearDepthStencilView(draw_ds->GetWriteDescriptor(), D3D12_CLEAR_FLAG_STENCIL, 0.0f, 1, 1, &rect);
|
||||
}
|
||||
|
||||
// Begin render pass if new target or out of the area.
|
||||
@@ -4167,7 +4342,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
GL_PUSH("Blit ColorClip back to RT");
|
||||
|
||||
EndRenderPass();
|
||||
colclip_rt->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
colclip_rt->TransitionToState(GSTexture12::ResourceState::PixelShaderResource);
|
||||
|
||||
draw_rt = static_cast<GSTexture12*>(config.rt);
|
||||
OMSetRenderTargets(draw_rt, draw_ds, config.colclip_update_area);
|
||||
@@ -4223,12 +4398,7 @@ void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig&
|
||||
{
|
||||
const u32 count = (*config.drawlist)[n] * indices_per_prim;
|
||||
|
||||
EndRenderPass();
|
||||
// Specify null for the after resource as both resources are used after the barrier.
|
||||
// While this may also be true before the barrier, we only write using the main resource.
|
||||
D3D12_RESOURCE_BARRIER barrier = {D3D12_RESOURCE_BARRIER_TYPE_ALIASING, D3D12_RESOURCE_BARRIER_FLAG_NONE};
|
||||
barrier.Aliasing = {draw_rt->GetResource(), nullptr};
|
||||
GetCommandList()->ResourceBarrier(1, &barrier);
|
||||
FeedbackBarrier(draw_rt);
|
||||
|
||||
if (BindDrawPipeline(pipe))
|
||||
DrawIndexedPrimitive(p, count);
|
||||
@@ -4242,11 +4412,7 @@ void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig&
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::Barriers, 1);
|
||||
|
||||
EndRenderPass();
|
||||
// Specify null for the after resource as both resources are used after the barrier.
|
||||
D3D12_RESOURCE_BARRIER barrier = {D3D12_RESOURCE_BARRIER_TYPE_ALIASING, D3D12_RESOURCE_BARRIER_FLAG_NONE};
|
||||
barrier.Aliasing = {draw_rt->GetResource(), nullptr};
|
||||
GetCommandList()->ResourceBarrier(1, &barrier);
|
||||
FeedbackBarrier(draw_rt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
@@ -137,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;
|
||||
@@ -147,6 +163,8 @@ private:
|
||||
bool has_timestamp_query = false;
|
||||
};
|
||||
|
||||
void LoadAgilitySDK();
|
||||
|
||||
bool CreateDevice(u32& vendor_id);
|
||||
bool CreateDescriptorHeaps();
|
||||
bool CreateCommandLists();
|
||||
@@ -296,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;
|
||||
@@ -377,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.
|
||||
|
||||
@@ -756,7 +756,7 @@ bool GSHwHack::GSC_PolyphonyDigitalGames(GSRendererHW& r, int& skip)
|
||||
config.ps.channel = ChannelFetch_RGB;
|
||||
config.colormask.wrgba = 1 | 2 | 4;
|
||||
r.EndHLEHardwareDraw(false);
|
||||
|
||||
src->m_last_draw = r.s_n;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@@ -814,6 +814,7 @@ bool GSHwHack::GSC_PolyphonyDigitalGames(GSRendererHW& r, int& skip)
|
||||
config.ps.channel = ChannelFetch_RED + channel;
|
||||
config.colormask.wrgba = 8;
|
||||
r.EndHLEHardwareDraw(false);
|
||||
dst->m_last_draw = r.s_n;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -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 = {};
|
||||
@@ -3197,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)
|
||||
{
|
||||
@@ -3230,8 +3277,6 @@ void GSRendererHW::Draw()
|
||||
scale_draw = 1;
|
||||
scaled_copy = true;
|
||||
}
|
||||
|
||||
m_downscale_source = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3905,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.
|
||||
@@ -4042,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)
|
||||
@@ -4741,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)
|
||||
@@ -5148,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)
|
||||
{
|
||||
@@ -5547,7 +5596,6 @@ __ri u32 GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool t
|
||||
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)
|
||||
{
|
||||
@@ -5557,21 +5605,14 @@ __ri u32 GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool t
|
||||
|
||||
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())
|
||||
@@ -5595,7 +5636,6 @@ __ri u32 GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool t
|
||||
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);
|
||||
@@ -5614,13 +5654,15 @@ __ri u32 GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool t
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -5868,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;
|
||||
@@ -6426,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).
|
||||
@@ -6818,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))
|
||||
@@ -6901,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
|
||||
@@ -8893,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;
|
||||
@@ -8914,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)
|
||||
@@ -8927,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8945,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.
|
||||
@@ -8960,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)
|
||||
@@ -8994,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)
|
||||
@@ -9002,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++)
|
||||
@@ -9017,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();
|
||||
@@ -9044,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1788,8 +1788,10 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
||||
(GSLocalMemory::m_psm[color_psm].bpp >= 16 || (/*possible_shuffle &&*/ GSLocalMemory::m_psm[color_psm].bpp == 8 && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp >= 16)) && // Channel shuffles or non indexed lookups.
|
||||
t->m_age <= 1 && (!found_t || t->m_last_draw > dst->m_last_draw) /*&& CanTranslate(bp, bw, psm, block_boundary_rect, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW)*/)
|
||||
{
|
||||
const u32 end_block = GSLocalMemory::GetEndBlockAddress(bp, TEX0.TBW, TEX0.PSM, r);
|
||||
const u32 adj_bp = (end_block < t->m_TEX0.TBP0 && t->UnwrappedEndBlock() > GS_MAX_BLOCKS) ? (bp + GS_MAX_BLOCKS) : bp;
|
||||
u32 rt_tbw = std::max(1U, t->m_TEX0.TBW);
|
||||
u32 horz_page_offset = ((bp - t->m_TEX0.TBP0) >> 5) % rt_tbw;
|
||||
u32 horz_page_offset = ((adj_bp - t->m_TEX0.TBP0) >> 5) % rt_tbw;
|
||||
|
||||
if (GSLocalMemory::m_psm[psm].bpp == GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp && bw != rt_tbw && block_boundary_rect.height() > GSLocalMemory::m_psm[psm].pgs.y)
|
||||
continue;
|
||||
@@ -1802,7 +1804,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
||||
((t->m_TEX0.TBW < (horz_page_offset + ((block_boundary_rect.z + GSLocalMemory::m_psm[psm].pgs.x - 1) / GSLocalMemory::m_psm[psm].pgs.x)) ||
|
||||
(t->m_TEX0.TBW != bw && block_boundary_rect.w > GSLocalMemory::m_psm[psm].pgs.y))))
|
||||
{
|
||||
DbgCon.Warning("BP %x - 16bit bad match for target bp %x bw %d src %d format %d", bp, t->m_TEX0.TBP0, t->m_TEX0.TBW, bw, t->m_TEX0.PSM);
|
||||
DbgCon.Warning("BP %x - 16bit bad match for target bp %x bw %d src %d format %d", adj_bp, t->m_TEX0.TBP0, t->m_TEX0.TBW, bw, t->m_TEX0.PSM);
|
||||
continue;
|
||||
}
|
||||
// Keep note that 2 bw is basically 1 normal page, as bw is in 64 pixels, and 8bit pages are 128 pixels wide, aka 2 bw.
|
||||
@@ -1814,7 +1816,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
||||
((GSLocalMemory::m_psm[psm].bpp == 32) ? bw : ((bw + 1) / 2)) <= t->m_TEX0.TBW) &&
|
||||
!(((GSLocalMemory::m_psm[psm].bpp == 32) ? bw : ((bw + 1) / 2)) == rt_tbw)))))
|
||||
{
|
||||
DbgCon.Warning("BP %x - 8bit bad match for target bp %x bw %d src %d format %d", bp, t->m_TEX0.TBP0, t->m_TEX0.TBW, bw, t->m_TEX0.PSM);
|
||||
DbgCon.Warning("BP %x - 8bit bad match for target bp %x bw %d src %d format %d", adj_bp, t->m_TEX0.TBP0, t->m_TEX0.TBW, bw, t->m_TEX0.PSM);
|
||||
continue;
|
||||
}
|
||||
else if (!possible_shuffle && GSLocalMemory::m_psm[psm].bpp <= 8 && TEX0.TBW == 1)
|
||||
@@ -1852,16 +1854,16 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
||||
else // Formats are not compatible for normal draws, only shuffles.
|
||||
continue;
|
||||
}
|
||||
if (bp > t->m_TEX0.TBP0)
|
||||
if (adj_bp > t->m_TEX0.TBP0)
|
||||
{
|
||||
if (!region.HasEither() && GSLocalMemory::m_psm[psm].bpp == 32 && (t->m_TEX0.TBW - (((bp - t->m_TEX0.TBP0) >> 5) % rt_tbw)) < static_cast<u32>((block_boundary_rect.width() + 63) / 64))
|
||||
if (!region.HasEither() && GSLocalMemory::m_psm[psm].bpp == 32 && (t->m_TEX0.TBW - (((adj_bp - t->m_TEX0.TBP0) >> 5) % rt_tbw)) < static_cast<u32>((block_boundary_rect.width() + 63) / 64))
|
||||
{
|
||||
DbgCon.Warning("Bad alignmenet");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Make sure it's inside if not a shuffle, sometimes valid areas can get messy, like TOCA Race Driver 2 where it goes over to 480, but it's rounded up to 512 in the shuffle.
|
||||
if (!possible_shuffle && !t->Inside(bp, bw, psm, block_boundary_rect))
|
||||
if (!possible_shuffle && !t->Inside(adj_bp, bw, psm, block_boundary_rect))
|
||||
continue;
|
||||
|
||||
GSVector4i new_rect = (GSLocalMemory::m_psm[color_psm].bpp != GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp && (psm & 0x7) != PSMCT16) ? block_boundary_rect : rect;
|
||||
@@ -1871,7 +1873,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
||||
// Hitman Blood Money is an example of this in the theatre.
|
||||
const u32 rt_tbw = (possible_shuffle || bw == 1 || GSUtil::GetChannelMask(psm) != 0x8 || frame.FBW <= bw || frame.FBW == t->m_TEX0.TBW || bw == t->m_TEX0.TBW) ? t->m_TEX0.TBW : frame.FBW;
|
||||
|
||||
const bool can_translate = CanTranslate(bp, bw, src_psm, new_rect, t->m_TEX0.TBP0, t->m_TEX0.PSM, rt_tbw);
|
||||
const bool can_translate = CanTranslate(adj_bp, bw, src_psm, new_rect, t->m_TEX0.TBP0, t->m_TEX0.PSM, rt_tbw);
|
||||
if (can_translate)
|
||||
{
|
||||
const bool swizzle_match = GSLocalMemory::m_psm[src_psm].depth == GSLocalMemory::m_psm[t->m_TEX0.PSM].depth;
|
||||
@@ -1881,7 +1883,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
||||
|
||||
if (swizzle_match)
|
||||
{
|
||||
rect = TranslateAlignedRectByPage(t->m_TEX0.TBP0, t->m_end_block, rt_tbw, t->m_TEX0.PSM, t->m_valid, bp, src_psm, bw, new_rect);
|
||||
rect = TranslateAlignedRectByPage(t->m_TEX0.TBP0, t->m_end_block, rt_tbw, t->m_TEX0.PSM, t->m_valid, adj_bp, src_psm, bw, new_rect);
|
||||
rect.x -= new_rect.x;
|
||||
rect.y -= new_rect.y;
|
||||
}
|
||||
@@ -1901,7 +1903,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
||||
new_rect.z = (new_rect.z + (page_size.x - 1)) & ~(page_size.x - 1);
|
||||
new_rect.w = (new_rect.w + (page_size.y - 1)) & ~(page_size.y - 1);
|
||||
}
|
||||
rect = TranslateAlignedRectByPage(t, bp & ~((1 << 5) - 1), src_psm, bw, new_rect);
|
||||
rect = TranslateAlignedRectByPage(t, adj_bp & ~((1 << 5) - 1), src_psm, bw, new_rect);
|
||||
rect.x -= new_rect.x & ~(page_size.x - 1);
|
||||
rect.y -= new_rect.y & ~(page_size.y - 1);
|
||||
}
|
||||
@@ -1933,7 +1935,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
||||
}
|
||||
else
|
||||
{
|
||||
SurfaceOffset so = ComputeSurfaceOffset(bp, bw, src_psm, new_rect, t);
|
||||
SurfaceOffset so = ComputeSurfaceOffset(adj_bp, bw, src_psm, new_rect, t);
|
||||
if (!so.is_valid && t->Wraps())
|
||||
{
|
||||
// Improves Beyond Good & Evil shadow.
|
||||
@@ -2402,250 +2404,289 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
};
|
||||
|
||||
Target* dst = nullptr;
|
||||
auto& list = m_dst[type];
|
||||
Target* dst_match = nullptr;
|
||||
auto* list = &m_dst[type];
|
||||
|
||||
const GSVector4i min_rect = draw_rect.max_u32(GSVector4i(0, 0, draw_rect.x, draw_rect.y));
|
||||
// TODO: Move all frame stuff to its own routine too.
|
||||
if (!is_frame)
|
||||
{
|
||||
for (auto i = list.begin(); i != list.end();)
|
||||
for (int iteration = 0; iteration < 2; iteration++)
|
||||
{
|
||||
Target* t = *i;
|
||||
if (bp == t->m_TEX0.TBP0)
|
||||
{
|
||||
bool can_use = true;
|
||||
if (dst != nullptr)
|
||||
break;
|
||||
|
||||
if (dst && ((GSState::s_n - dst->m_last_draw) < (GSState::s_n - t->m_last_draw) && dst->m_TEX0.TBP0 <= bp))
|
||||
auto& new_dst = iteration == 0 ? dst : dst_match;
|
||||
list = &m_dst[iteration == 0 ? type : (1 - type)];
|
||||
for (auto i = list->begin(); i != list->end();)
|
||||
{
|
||||
Target* t = *i;
|
||||
if (bp == t->m_TEX0.TBP0)
|
||||
{
|
||||
DevCon.Warning("Ignoring target at %x as one at %x is newer", t->m_TEX0.TBP0, dst->m_TEX0.TBP0);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
// if It's an old target and it's being completely overwritten, kill it.
|
||||
// Dragon Quest 8 reuses a render-target sized buffer as a single-page buffer, without clearing it. But,
|
||||
// it does dirty it by writing over the 64x64 region. So while we can't use this heuristic for tossing
|
||||
// targets at BW=1 because it breaks other games, we can when the *new* buffer area is completely dirty.
|
||||
if (((!preserve_rgb && !preserve_alpha) || (t->m_was_dst_matched && fbmask == 0xffffff)) && TEX0.TBW != t->m_TEX0.TBW)
|
||||
{
|
||||
// Old targets or shrunk targets where Y draw height goes outside the page.
|
||||
if (TEX0.TBW > 1 && (t->m_age >= 1 || (type == RenderTarget && draw_rect.w > GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y && TEX0.TBW < t->m_TEX0.TBW)))
|
||||
bool can_use = true;
|
||||
|
||||
if (new_dst && ((GSState::s_n - new_dst->m_last_draw) < (GSState::s_n - t->m_last_draw) && new_dst->m_TEX0.TBP0 <= bp))
|
||||
{
|
||||
DevCon.Warning("Ignoring target at %x as one at %x is newer", t->m_TEX0.TBP0, new_dst->m_TEX0.TBP0);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If there's no valid RGB, it can't be interchanged between RT and Depth
|
||||
if (iteration == 1)
|
||||
{
|
||||
const u32 valid_mask = (t->m_valid_rgb ? 0x7 : 0x0) | ((t->m_valid_alpha_low || t->m_valid_alpha_high) ? 0x8 : 0x0);
|
||||
if ((!(valid_mask & GSUtil::GetChannelMask(TEX0.PSM)) || (!is_shuffle && TEX0.TBW < (t->m_TEX0.TBW / 2))) ||
|
||||
(!is_shuffle && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp != GSLocalMemory::m_psm[TEX0.PSM].bpp))
|
||||
{
|
||||
if (!preserve_rgb && !preserve_alpha && (!src || src->m_from_target != t) && (valid_mask & GSUtil::GetChannelMask(TEX0.PSM)))
|
||||
{
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list->erase(i);
|
||||
delete t;
|
||||
}
|
||||
else
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// if It's an old target and it's being completely overwritten, kill it.
|
||||
// Dragon Quest 8 reuses a render-target sized buffer as a single-page buffer, without clearing it. But,
|
||||
// it does dirty it by writing over the 64x64 region. So while we can't use this heuristic for tossing
|
||||
// targets at BW=1 because it breaks other games, we can when the *new* buffer area is completely dirty.
|
||||
if (((!preserve_rgb && !preserve_alpha) || (t->m_was_dst_matched && fbmask == 0xffffff)) && TEX0.TBW != t->m_TEX0.TBW)
|
||||
{
|
||||
// Old targets or shrunk targets where Y draw height goes outside the page.
|
||||
if (TEX0.TBW > 1 && (t->m_age >= 1 || (type == RenderTarget && draw_rect.w > GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y && TEX0.TBW < t->m_TEX0.TBW)))
|
||||
{
|
||||
can_use = false;
|
||||
}
|
||||
else if (!t->m_dirty.empty())
|
||||
{
|
||||
const GSVector4i size_rect = GSVector4i::loadh(size);
|
||||
can_use = !t->m_dirty.GetTotalRect(TEX0, size).rintersect(size_rect).eq(size_rect);
|
||||
}
|
||||
}
|
||||
else if (type == RenderTarget && (fbmask == 0xffffff && !t->m_was_dst_matched && TEX0.TBW != t->m_TEX0.TBW))
|
||||
{
|
||||
// When returning to being matched with the Z buffer in width, we need to make sure the RGB is up to date as it could get used later (Hitman Contracts).
|
||||
auto& rev_list = m_dst[1 - type];
|
||||
for (auto j = rev_list.begin(); j != rev_list.end(); ++j)
|
||||
{
|
||||
Target* ds = *j;
|
||||
|
||||
if (t->m_TEX0.TBP0 != ds->m_TEX0.TBP0 || !ds->m_valid_rgb || TEX0.TBW != ds->m_TEX0.TBW)
|
||||
continue;
|
||||
|
||||
t->m_was_dst_matched = true;
|
||||
t->m_valid_rgb = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// TODO: What might be a nicer solution than this, is to rearrange the targets to match the new layout, however this comes with some caviets:
|
||||
// 1. They can draw wider than the FBW
|
||||
// 2. The dirty+valid rects will need to also be rearranged
|
||||
// 3. This could mean larger targets hanging around more
|
||||
// 4. Sources which reference a target may become invalid and will need to be removed
|
||||
// 5. Potential performance implications from additional render passes/copying
|
||||
//
|
||||
// But the bonuses are:
|
||||
// 1. Rearranging the page layout will fix quite a few games which do this
|
||||
// 2. Preserved data will be in the correct place (in most cases)
|
||||
// 3. Less deleting sources/targets
|
||||
// 4. We can basically do clears in hardware, if they aren't insane ones
|
||||
bool dirtied_area = t->m_dirty.size() >= 1;
|
||||
|
||||
// Check it covers the whole area of the new draw
|
||||
if (!is_shuffle && dirtied_area)
|
||||
{
|
||||
const u32 draw_start = GSLocalMemory::GetStartBlockAddress(TEX0.TBP0, TEX0.TBW, TEX0.PSM, draw_rect);
|
||||
const u32 draw_end = GSLocalMemory::GetEndBlockAddress(TEX0.TBP0, TEX0.TBW, TEX0.PSM, draw_rect);
|
||||
|
||||
const GSVector4i dirty_rect = t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size);
|
||||
const u32 dirty_start = GSLocalMemory::GetStartBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, dirty_rect);
|
||||
const u32 dirty_end = GSLocalMemory::GetEndBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, dirty_rect);
|
||||
|
||||
if (dirty_end < draw_end || dirty_start > draw_start)
|
||||
dirtied_area = false;
|
||||
}
|
||||
|
||||
if (can_use && ((!is_shuffle && dirtied_area) || (is_shuffle && src && GSLocalMemory::m_psm[src->m_TEX0.PSM].bpp == 8 && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == 16)) && ((preserve_alpha && preserve_rgb) || (draw_rect.w > GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y && !possible_clear)) && TEX0.TBW != t->m_TEX0.TBW)
|
||||
{
|
||||
can_use = false;
|
||||
}
|
||||
else if (!t->m_dirty.empty())
|
||||
|
||||
if (can_use)
|
||||
{
|
||||
const GSVector4i size_rect = GSVector4i::loadh(size);
|
||||
can_use = !t->m_dirty.GetTotalRect(TEX0, size).rintersect(size_rect).eq(size_rect);
|
||||
}
|
||||
}
|
||||
else if (type == RenderTarget && (fbmask == 0xffffff && !t->m_was_dst_matched && TEX0.TBW != t->m_TEX0.TBW))
|
||||
{
|
||||
// When returning to being matched with the Z buffer in width, we need to make sure the RGB is up to date as it could get used later (Hitman Contracts).
|
||||
auto& rev_list = m_dst[1 - type];
|
||||
for (auto j = rev_list.begin(); j != rev_list.end(); ++j)
|
||||
{
|
||||
Target* ds = *j;
|
||||
|
||||
if (t->m_TEX0.TBP0 != ds->m_TEX0.TBP0 || !ds->m_valid_rgb || TEX0.TBW != ds->m_TEX0.TBW)
|
||||
continue;
|
||||
|
||||
t->m_was_dst_matched = true;
|
||||
t->m_valid_rgb = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// TODO: What might be a nicer solution than this, is to rearrange the targets to match the new layout, however this comes with some caviets:
|
||||
// 1. They can draw wider than the FBW
|
||||
// 2. The dirty+valid rects will need to also be rearranged
|
||||
// 3. This could mean larger targets hanging around more
|
||||
// 4. Sources which reference a target may become invalid and will need to be removed
|
||||
// 5. Potential performance implications from additional render passes/copying
|
||||
//
|
||||
// But the bonuses are:
|
||||
// 1. Rearranging the page layout will fix quite a few games which do this
|
||||
// 2. Preserved data will be in the correct place (in most cases)
|
||||
// 3. Less deleting sources/targets
|
||||
// 4. We can basically do clears in hardware, if they aren't insane ones
|
||||
bool dirtied_area = t->m_dirty.size() >= 1;
|
||||
|
||||
// Check it covers the whole area of the new draw
|
||||
if (!is_shuffle && dirtied_area)
|
||||
{
|
||||
const u32 draw_start = GSLocalMemory::GetStartBlockAddress(TEX0.TBP0, TEX0.TBW, TEX0.PSM, draw_rect);
|
||||
const u32 draw_end = GSLocalMemory::GetEndBlockAddress(TEX0.TBP0, TEX0.TBW, TEX0.PSM, draw_rect);
|
||||
|
||||
const GSVector4i dirty_rect = t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size);
|
||||
const u32 dirty_start = GSLocalMemory::GetStartBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, dirty_rect);
|
||||
const u32 dirty_end = GSLocalMemory::GetEndBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, dirty_rect);
|
||||
|
||||
if (dirty_end < draw_end || dirty_start > draw_start)
|
||||
dirtied_area = false;
|
||||
}
|
||||
|
||||
if (can_use && ((!is_shuffle && dirtied_area) || (is_shuffle && src && GSLocalMemory::m_psm[src->m_TEX0.PSM].bpp == 8 && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == 16)) && ((preserve_alpha && preserve_rgb) || (draw_rect.w > GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y && !possible_clear)) && TEX0.TBW != t->m_TEX0.TBW)
|
||||
{
|
||||
can_use = false;
|
||||
}
|
||||
|
||||
if (can_use)
|
||||
{
|
||||
if (used)
|
||||
list.MoveFront(i.Index());
|
||||
dst = t;
|
||||
|
||||
dst->m_32_bits_fmt |= (psm_s.bpp != 16);
|
||||
break;
|
||||
}
|
||||
else if (!(src && src->m_from_target == t))
|
||||
{
|
||||
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s due to change in target", t->m_TEX0.TBP0, t->m_TEX0.TBW, GSUtil::GetPSMName(t->m_TEX0.PSM));
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list.erase(i);
|
||||
delete t;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Probably pointing to half way through the target
|
||||
else if (!min_rect.rempty() && GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets)
|
||||
{
|
||||
// Some games misuse the scissor so it ends up valid 1 pixel over, which causes hell for us. So check if it still overlaps without the extra pixel.
|
||||
const GSVector4i adjusted_valid = GSVector4i(t->m_valid.x, t->m_valid.y, std::min(t->m_valid.z, static_cast<int>(t->m_TEX0.TBW) * 64), t->m_valid.w - 1);
|
||||
const u32 adjusted_endblock = GSLocalMemory::GetEndBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, adjusted_valid);
|
||||
if (adjusted_endblock <= bp)
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const u32 widthpage_offset = (std::abs(static_cast<int>(bp - t->m_TEX0.TBP0)) >> 5) % std::max(t->m_TEX0.TBW, 1U);
|
||||
const bool is_aligned_ok = widthpage_offset == 0 || ((min_rect.width() <= static_cast<int>((t->m_TEX0.TBW - widthpage_offset) * 64) && (t->m_TEX0.TBW == TEX0.TBW || TEX0.TBW == 1)) && bp >= t->m_TEX0.TBP0);
|
||||
const bool no_target_or_newer = (!dst || ((GSState::s_n - dst->m_last_draw) < (GSState::s_n - t->m_last_draw)));
|
||||
const bool width_match = (t->m_TEX0.TBW == TEX0.TBW || (TEX0.TBW == 1 && draw_rect.w <= GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y));
|
||||
const bool ds_offset = !ds || offset != 0;
|
||||
const bool is_double_buffer = TEX0.TBP0 == ((((t->m_end_block + 1) - t->m_TEX0.TBP0) / 2) + t->m_TEX0.TBP0);
|
||||
const bool source_match = src && src->m_TEX0.TBP0 <= bp && src->m_end_block > bp && src->m_TEX0.TBW == TEX0.TBW && src->m_from_target && src->m_from_target == t && t->Inside(bp, TEX0.TBW, TEX0.PSM, min_rect);
|
||||
const bool was_used_last_draw = t->m_last_draw == (GSState::s_n - 1);
|
||||
// if it's a shuffle, some games tend to offset back by a page, such as Tomb Raider, for no disernable reason, but it then causes problems.
|
||||
// This can also happen horizontally (Catwoman moves everything one page left with shuffles), but this is too messy to deal with right now.
|
||||
const bool overlaps = t->Overlaps(bp, TEX0.TBW, TEX0.PSM, min_rect) || (is_shuffle && src && GSLocalMemory::m_psm[src->m_TEX0.PSM].bpp == 8 && t->Overlaps(bp, TEX0.TBW, TEX0.PSM, min_rect + GSVector4i(0, 0, 0, 32)));
|
||||
if (source_match || (no_target_or_newer && is_aligned_ok && width_match && overlaps && (is_shuffle || ds_offset || is_double_buffer || was_used_last_draw)))
|
||||
{
|
||||
const GSLocalMemory::psm_t& s_psm = GSLocalMemory::m_psm[TEX0.PSM];
|
||||
|
||||
// If it overlaps but the target is huge and the Z isn't offset, we need to split the buffer, so let's shrink this one down.
|
||||
// 896 is just 448 * 2,just gives the buffer chance to be larger than normal, in case they do something like 640x640, or something ridiculous.
|
||||
if (!is_shuffle && (ds && offset == 0 && (t->m_valid.w >= 896) && ((((t->m_end_block + 1) - t->m_TEX0.TBP0) >> 1) + t->m_TEX0.TBP0) <= bp))
|
||||
{
|
||||
const u32 local_offset = (((bp - t->m_TEX0.TBP0) >> 5) / std::max(t->m_TEX0.TBW, 1U)) * s_psm.pgs.y;
|
||||
if ((dst = CreateTarget(TEX0, GSVector2i(t->m_valid.z, t->m_valid.w - local_offset), GSVector2i(t->m_valid.z, t->m_valid.w - local_offset), scale, type, true, fbmask, false, false, preserve_rgb || preserve_alpha, GSVector4i::zero(), src)))
|
||||
dst->m_32_bits_fmt |= (psm_s.bpp != 16);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// I know what you're thinking, and I hate the guy who wrote it too (me). Project Snowblind, Tomb Raider etc decide to offset where they're drawing using a channel shuffle, and this gets messy, so best just to kill the old target.
|
||||
if (is_shuffle && src && src->m_TEX0.PSM == PSMT8 && GSRendererHW::GetInstance()->m_context->FRAME.FBW == 1 && t->m_last_draw != (GSState::s_n - 1) && src->m_from_target && (src->m_from_target->m_TEX0.TBP0 == src->m_TEX0.TBP0 || (((src->m_TEX0.TBP0 - src->m_from_target->m_TEX0.TBP0) >> 5) % std::max(src->m_from_target->m_TEX0.TBW, 1U) == 0)) && widthpage_offset && src->m_from_target != t)
|
||||
{
|
||||
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s offset overwrite shuffle", t->m_TEX0.TBP0, t->m_TEX0.TBW, GSUtil::GetPSMName(t->m_TEX0.PSM));
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list.erase(i);
|
||||
delete t;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_shuffle && (!GSUtil::HasSameSwizzleBits(t->m_TEX0.PSM, TEX0.PSM) ||
|
||||
((widthpage_offset % std::max(t->m_TEX0.TBW, 1U)) != 0 && ((widthpage_offset + (min_rect.width() + (s_psm.pgs.x - 1)) / s_psm.pgs.x)) > t->m_TEX0.TBW)))
|
||||
{
|
||||
const int page_offset = TEX0.TBP0 - t->m_TEX0.TBP0;
|
||||
const int number_pages = page_offset / 32;
|
||||
const u32 tbw = std::max(t->m_TEX0.TBW, 1u);
|
||||
const int row_offset = number_pages / tbw;
|
||||
const int page_height = GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y;
|
||||
const int vertical_position = row_offset * page_height;
|
||||
|
||||
if (src && src->m_from_target == t && src->m_target_direct && vertical_position >= t->m_valid.w / 2)
|
||||
{
|
||||
// Valids and drawn since last read doesn't match, keep the target but resize it.
|
||||
src->m_valid_rect.w = std::min(vertical_position, src->m_valid_rect.w);
|
||||
t->m_valid.w = std::min(vertical_position, t->m_valid.w);
|
||||
t->ResizeValidity(t->m_valid);
|
||||
t->ResizeDrawn(t->m_valid);
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s due to change in target", t->m_TEX0.TBP0, t->m_TEX0.TBW, GSUtil::GetPSMName(t->m_TEX0.PSM));
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list.erase(i);
|
||||
delete t;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
GSVector4i lookup_rect = min_rect;
|
||||
|
||||
if (is_shuffle)
|
||||
lookup_rect = lookup_rect & GSVector4i(~8);
|
||||
|
||||
const GSVector4i translated_rect = GSVector4i(0, 0, 0, 0).max_i32(TranslateAlignedRectByPage(t, TEX0.TBP0, TEX0.PSM, TEX0.TBW, lookup_rect));
|
||||
const GSVector4i dirty_rect = t->m_dirty.empty() ? GSVector4i::zero() : t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size);
|
||||
const bool all_dirty = dirty_rect.eq(t->m_valid);
|
||||
|
||||
|
||||
if (!is_shuffle && !dirty_rect.rempty() && (!preserve_alpha && !preserve_rgb) && (GSState::s_n - 3) > t->m_last_draw)
|
||||
{
|
||||
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s due to dirty areas not preserved (Likely change in target)", t->m_TEX0.TBP0, t->m_TEX0.TBW, GSUtil::GetPSMName(t->m_TEX0.PSM));
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list.erase(i);
|
||||
delete t;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!all_dirty && ((translated_rect.w <= t->m_valid.w) || widthpage_offset == 0 || (GSState::s_n - 3) <= t->m_last_draw))
|
||||
{
|
||||
if (TEX0.TBW == t->m_TEX0.TBW && !is_shuffle && widthpage_offset == 0 && ((min_rect.w + 63) / 64) > 1)
|
||||
{
|
||||
// Beyond Good and Evil does this awful thing where it puts one framebuffer at 0xf00, with the first row of pages blanked out, and the whole thing goes down to 0x2080
|
||||
// which is a problem, because it then puts the Z buffer at 0x1fc0, then offsets THAT by 1 row of pages, so it starts at, you guessed it, 2080.
|
||||
// So let's check the *real* start.
|
||||
u32 real_start_address = GSLocalMemory::GetStartBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, t->m_drawn_since_read);
|
||||
u32 new_end_address = GSLocalMemory::GetEndBlockAddress(TEX0.TBP0, TEX0.TBW, TEX0.PSM, min_rect);
|
||||
|
||||
// Not really overlapping.
|
||||
if (real_start_address > new_end_address)
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//DevCon.Warning("Here draw %d wanted %x PSM %x got %x PSM %x offset of %d pages width %d pages draw width %d", GSState::s_n, bp, TEX0.PSM, t->m_TEX0.TBP0, t->m_TEX0.PSM, (bp - t->m_TEX0.TBP0) >> 5, t->m_TEX0.TBW, draw_rect.width());
|
||||
dst = t;
|
||||
|
||||
dst->m_32_bits_fmt |= (psm_s.bpp != 16);
|
||||
//Continue just in case there's a newer target
|
||||
if (used)
|
||||
list.MoveFront(i.Index());
|
||||
if (t->m_TEX0.TBP0 <= bp || GSLocalMemory::GetStartBlockAddress(TEX0.TBP0, TEX0.TBW, TEX0.PSM, min_rect) >= bp)
|
||||
break;
|
||||
else
|
||||
continue;
|
||||
list->MoveFront(i.Index());
|
||||
|
||||
new_dst = t;
|
||||
new_dst->m_32_bits_fmt |= (psm_s.bpp != 16);
|
||||
|
||||
break;
|
||||
}
|
||||
else if (!(src && src->m_from_target == t))
|
||||
{
|
||||
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s due to change in target", t->m_TEX0.TBP0, t->m_TEX0.TBW, GSUtil::GetPSMName(t->m_TEX0.PSM));
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list->erase(i);
|
||||
delete t;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Probably pointing to half way through the target
|
||||
else if (!min_rect.rempty() && GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets)
|
||||
{
|
||||
// Some games misuse the scissor so it ends up valid 1 pixel over, which causes hell for us. So check if it still overlaps without the extra pixel.
|
||||
const GSVector4i adjusted_valid = GSVector4i(t->m_valid.x, t->m_valid.y, std::min(t->m_valid.z, static_cast<int>(t->m_TEX0.TBW) * 64), t->m_valid.w - 1);
|
||||
const u32 adjusted_endblock = GSLocalMemory::GetEndBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, adjusted_valid);
|
||||
if (adjusted_endblock <= bp)
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
i++;
|
||||
const u32 widthpage_offset = (std::abs(static_cast<int>(bp - t->m_TEX0.TBP0)) >> 5) % std::max(t->m_TEX0.TBW, 1U);
|
||||
const bool is_aligned_ok = widthpage_offset == 0 || ((min_rect.width() <= static_cast<int>((t->m_TEX0.TBW - widthpage_offset) * 64) && (t->m_TEX0.TBW == TEX0.TBW || TEX0.TBW == 1)) && bp >= t->m_TEX0.TBP0);
|
||||
const bool no_target_or_newer = (!new_dst || ((GSState::s_n - new_dst->m_last_draw) < (GSState::s_n - t->m_last_draw)));
|
||||
const bool width_match = (t->m_TEX0.TBW == TEX0.TBW || (TEX0.TBW == 1 && draw_rect.w <= GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y));
|
||||
const bool ds_offset = !ds || offset != 0;
|
||||
const bool is_double_buffer = TEX0.TBP0 == ((((t->m_end_block + 1) - t->m_TEX0.TBP0) / 2) + t->m_TEX0.TBP0);
|
||||
const bool source_match = src && src->m_TEX0.TBP0 <= bp && src->m_end_block > bp && src->m_TEX0.TBW == TEX0.TBW && src->m_from_target && src->m_from_target == t && t->Inside(bp, TEX0.TBW, TEX0.PSM, min_rect);
|
||||
const bool was_used_last_draw = t->m_last_draw == (GSState::s_n - 1);
|
||||
// if it's a shuffle, some games tend to offset back by a page, such as Tomb Raider, for no disernable reason, but it then causes problems.
|
||||
// This can also happen horizontally (Catwoman moves everything one page left with shuffles), but this is too messy to deal with right now.
|
||||
const bool overlaps = t->Overlaps(bp, TEX0.TBW, TEX0.PSM, min_rect) || (is_shuffle && src && GSLocalMemory::m_psm[src->m_TEX0.PSM].bpp == 8 && t->Overlaps(bp, TEX0.TBW, TEX0.PSM, min_rect + GSVector4i(0, 0, 0, 32)));
|
||||
if (source_match || (no_target_or_newer && is_aligned_ok && width_match && overlaps && (is_shuffle || ds_offset || is_double_buffer || was_used_last_draw)))
|
||||
{
|
||||
const GSLocalMemory::psm_t& s_psm = GSLocalMemory::m_psm[TEX0.PSM];
|
||||
|
||||
// If it overlaps but the target is huge and the Z isn't offset, we need to split the buffer, so let's shrink this one down.
|
||||
// 896 is just 448 * 2,just gives the buffer chance to be larger than normal, in case they do something like 640x640, or something ridiculous.
|
||||
if (!is_shuffle && (ds && offset == 0 && (t->m_valid.w >= 896) && ((((t->m_end_block + 1) - t->m_TEX0.TBP0) >> 1) + t->m_TEX0.TBP0) <= bp))
|
||||
{
|
||||
const u32 local_offset = (((bp - t->m_TEX0.TBP0) >> 5) / std::max(t->m_TEX0.TBW, 1U)) * s_psm.pgs.y;
|
||||
if ((new_dst = CreateTarget(TEX0, GSVector2i(t->m_valid.z, t->m_valid.w - local_offset), GSVector2i(t->m_valid.z, t->m_valid.w - local_offset), scale, type, true, fbmask, false, false, preserve_rgb || preserve_alpha, GSVector4i::zero(), src)))
|
||||
new_dst->m_32_bits_fmt |= (psm_s.bpp != 16);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// I know what you're thinking, and I hate the guy who wrote it too (me). Project Snowblind, Tomb Raider etc decide to offset where they're drawing using a channel shuffle, and this gets messy, so best just to kill the old target.
|
||||
if (is_shuffle && src && src->m_TEX0.PSM == PSMT8 && GSRendererHW::GetInstance()->m_context->FRAME.FBW == 1 && t->m_last_draw != (GSState::s_n - 1) && src->m_from_target && (src->m_from_target->m_TEX0.TBP0 == src->m_TEX0.TBP0 || (((src->m_TEX0.TBP0 - src->m_from_target->m_TEX0.TBP0) >> 5) % std::max(src->m_from_target->m_TEX0.TBW, 1U) == 0)) && widthpage_offset && src->m_from_target != t)
|
||||
{
|
||||
if (iteration == 0)
|
||||
{
|
||||
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s offset overwrite shuffle", t->m_TEX0.TBP0, t->m_TEX0.TBW, GSUtil::GetPSMName(t->m_TEX0.PSM));
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list->erase(i);
|
||||
delete t;
|
||||
}
|
||||
else
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_shuffle && (!GSUtil::HasSameSwizzleBits(t->m_TEX0.PSM, TEX0.PSM) ||
|
||||
((widthpage_offset % std::max(t->m_TEX0.TBW, 1U)) != 0 && ((widthpage_offset + (min_rect.width() + (s_psm.pgs.x - 1)) / s_psm.pgs.x)) > t->m_TEX0.TBW)))
|
||||
{
|
||||
const int page_offset = TEX0.TBP0 - t->m_TEX0.TBP0;
|
||||
const int number_pages = page_offset / 32;
|
||||
const u32 tbw = std::max(t->m_TEX0.TBW, 1u);
|
||||
const int row_offset = number_pages / tbw;
|
||||
const int page_height = GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y;
|
||||
const int vertical_position = row_offset * page_height;
|
||||
|
||||
if (src && src->m_from_target == t && src->m_target_direct && vertical_position >= t->m_valid.w / 2)
|
||||
{
|
||||
// Valids and drawn since last read doesn't match, keep the target but resize it.
|
||||
src->m_valid_rect.w = std::min(vertical_position, src->m_valid_rect.w);
|
||||
t->m_valid.w = std::min(vertical_position, t->m_valid.w);
|
||||
t->ResizeValidity(t->m_valid);
|
||||
t->ResizeDrawn(t->m_valid);
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (iteration == 0)
|
||||
{
|
||||
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s due to change in target", t->m_TEX0.TBP0, t->m_TEX0.TBW, GSUtil::GetPSMName(t->m_TEX0.PSM));
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list->erase(i);
|
||||
delete t;
|
||||
}
|
||||
else
|
||||
i++;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
GSVector4i lookup_rect = min_rect;
|
||||
|
||||
if (is_shuffle)
|
||||
lookup_rect = lookup_rect & GSVector4i(~8);
|
||||
|
||||
const GSVector4i translated_rect = GSVector4i(0, 0, 0, 0).max_i32(TranslateAlignedRectByPage(t, TEX0.TBP0, TEX0.PSM, TEX0.TBW, lookup_rect));
|
||||
const GSVector4i dirty_rect = t->m_dirty.empty() ? GSVector4i::zero() : t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size);
|
||||
const bool all_dirty = dirty_rect.eq(t->m_valid);
|
||||
|
||||
|
||||
if (!is_shuffle && !dirty_rect.rempty() && (!preserve_alpha && !preserve_rgb) && (GSState::s_n - 3) > t->m_last_draw)
|
||||
{
|
||||
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s due to dirty areas not preserved (Likely change in target)", t->m_TEX0.TBP0, t->m_TEX0.TBW, GSUtil::GetPSMName(t->m_TEX0.PSM));
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list->erase(i);
|
||||
delete t;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!all_dirty && ((translated_rect.w <= t->m_valid.w) || widthpage_offset == 0 || (GSState::s_n - 3) <= t->m_last_draw))
|
||||
{
|
||||
if (TEX0.TBW == t->m_TEX0.TBW && !is_shuffle && widthpage_offset == 0 && ((min_rect.w + 63) / 64) > 1)
|
||||
{
|
||||
// Beyond Good and Evil does this awful thing where it puts one framebuffer at 0xf00, with the first row of pages blanked out, and the whole thing goes down to 0x2080
|
||||
// which is a problem, because it then puts the Z buffer at 0x1fc0, then offsets THAT by 1 row of pages, so it starts at, you guessed it, 2080.
|
||||
// So let's check the *real* start.
|
||||
u32 real_start_address = GSLocalMemory::GetStartBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, t->m_drawn_since_read);
|
||||
u32 new_end_address = GSLocalMemory::GetEndBlockAddress(TEX0.TBP0, TEX0.TBW, TEX0.PSM, min_rect);
|
||||
|
||||
// Not really overlapping.
|
||||
if (real_start_address > new_end_address)
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//DevCon.Warning("Here draw %d wanted %x PSM %x got %x PSM %x offset of %d pages width %d pages draw width %d", GSState::s_n, bp, TEX0.PSM, t->m_TEX0.TBP0, t->m_TEX0.PSM, (bp - t->m_TEX0.TBP0) >> 5, t->m_TEX0.TBW, draw_rect.width());
|
||||
new_dst = t;
|
||||
new_dst->m_32_bits_fmt |= (psm_s.bpp != 16);
|
||||
|
||||
//Continue just in case there's a newer target
|
||||
if (used)
|
||||
list->MoveFront(i.Index());
|
||||
if (t->m_TEX0.TBP0 <= bp || GSLocalMemory::GetStartBlockAddress(TEX0.TBP0, TEX0.TBW, TEX0.PSM, min_rect) >= bp)
|
||||
break;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pxAssert(type == RenderTarget);
|
||||
// Let's try to find a perfect frame that contains valid data
|
||||
for (auto i = list.begin(); i != list.end(); ++i)
|
||||
for (auto i = list->begin(); i != list->end(); ++i)
|
||||
{
|
||||
Target* t = *i;
|
||||
|
||||
@@ -2671,7 +2712,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
{
|
||||
DevCon.Warning("Wanted %x psm %x bw %x, got %x psm %x bw %x, deleting", TEX0.TBP0, TEX0.PSM, TEX0.TBW, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list.erase(i);
|
||||
i = list->erase(i);
|
||||
delete t;
|
||||
continue;
|
||||
}
|
||||
@@ -2689,13 +2730,15 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
// 2nd try ! Try to find a frame at the requested bp -> bp + size is inside of (or equal to)
|
||||
if (!dst)
|
||||
{
|
||||
for (auto i = list.begin(); i != list.end(); ++i)
|
||||
for (auto i = list->begin(); i != list->end(); ++i)
|
||||
{
|
||||
Target* t = *i;
|
||||
const u32 end_block = GSLocalMemory::GetEndBlockAddress(bp, TEX0.TBW, TEX0.PSM, GSVector4i(0, size.y, size.x, size.y + 1));
|
||||
const u32 bp_adj = (end_block < t->m_TEX0.TBP0 && t->UnwrappedEndBlock() > GS_MAX_BLOCKS) ? (bp + GS_MAX_BLOCKS) : bp;
|
||||
const bool half_buffer_match = GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets && TEX0.TBW == t->m_TEX0.TBW && TEX0.PSM == t->m_TEX0.PSM &&
|
||||
bp == GSLocalMemory::GetStartBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, GSVector4i(0, size.y, size.x, size.y + 1));
|
||||
// Make sure the target is inside the texture
|
||||
if (t->m_TEX0.TBP0 <= bp && bp <= t->m_end_block && (half_buffer_match || t->Inside(bp, TEX0.TBW, TEX0.PSM, GSVector4i::loadh(size))))
|
||||
if (t->m_TEX0.TBP0 <= bp_adj && bp_adj <= t->UnwrappedEndBlock() && (half_buffer_match || t->Inside(bp_adj, TEX0.TBW, TEX0.PSM, GSVector4i::loadh(size))))
|
||||
{
|
||||
if (dst && (GSState::s_n - dst->m_last_draw) < (GSState::s_n - t->m_last_draw))
|
||||
continue;
|
||||
@@ -2707,7 +2750,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
{
|
||||
DevCon.Warning("2 Wanted %x psm %x bw %x, got %x psm %x bw %x, deleting", TEX0.TBP0, TEX0.PSM, TEX0.TBW, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list.erase(i);
|
||||
i = list->erase(i);
|
||||
delete t;
|
||||
continue;
|
||||
}
|
||||
@@ -2727,7 +2770,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
// 3rd try ! Try to find a frame that doesn't contain valid data (honestly I'm not sure we need to do it)
|
||||
if (!dst)
|
||||
{
|
||||
for (auto i = list.begin(); i != list.end(); ++i)
|
||||
for (auto i = list->begin(); i != list->end(); ++i)
|
||||
{
|
||||
Target* t = *i;
|
||||
if (bp == t->m_TEX0.TBP0 && TEX0.TBW == t->m_TEX0.TBW)
|
||||
@@ -2739,7 +2782,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
{
|
||||
DevCon.Warning("3 Wanted %x psm %x bw %x, got %x psm %x bw %x, deleting", TEX0.TBP0, TEX0.PSM, TEX0.TBW, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list.erase(i);
|
||||
i = list->erase(i);
|
||||
delete t;
|
||||
continue;
|
||||
}
|
||||
@@ -3084,67 +3127,28 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
}
|
||||
else if (!is_frame && !GSConfig.UserHacks_DisableDepthSupport)
|
||||
{
|
||||
const int rev_type = (type == DepthStencil) ? RenderTarget : DepthStencil;
|
||||
|
||||
// Depth stencil/RT can be an older RT/DS but only check recent RT/DS to avoid to pick
|
||||
// some bad data.
|
||||
auto& rev_list = m_dst[rev_type];
|
||||
Target* dst_match = nullptr;
|
||||
for (auto i = rev_list.begin(); i != rev_list.end(); ++i)
|
||||
if (!dst_match)
|
||||
{
|
||||
Target* t = *i;
|
||||
// Don't pull in targets without valid lower 24 bits unless the Z is 32bits and the alpha is valid, it makes no sense to convert them otherwise.
|
||||
// FIXME: Technically the difference in size is fine, but if the target gets reinterpreted, the hw renderer doesn't rearrange the target.
|
||||
// This does cause some extra uploads in some games (like Burnout), but without this, bad data gets displayed in games like Transformers.
|
||||
if (bp != t->m_TEX0.TBP0 || (!t->m_valid_rgb && (!(GSUtil::GetChannelMask(TEX0.PSM) & 0x8) || !(t->m_valid_alpha_low || t->m_valid_alpha_high))) ||
|
||||
(!is_shuffle && t->m_TEX0.TBW != TEX0.TBW && (possible_clear || ((~GSLocalMemory::m_psm[t->m_TEX0.PSM].fmsk | fbmask) == 0xffffffff))))
|
||||
const int rev_type = (type == DepthStencil) ? RenderTarget : DepthStencil;
|
||||
// Depth stencil/RT can be an older RT/DS but only check recent RT/DS to avoid to pick
|
||||
// some bad data.
|
||||
auto& rev_list = m_dst[rev_type];
|
||||
for (auto i = rev_list.begin(); i != rev_list.end(); ++i)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// If the format is completely different, but it's the same location, it's likely just overwriting it, so get rid.
|
||||
// Make sure it's not currently in use, that could be bad.
|
||||
if (!is_shuffle && (!ds || (ds != t)) &&
|
||||
t->m_TEX0.TBW != TEX0.TBW && TEX0.TBW != 1 && !preserve_rgb && min_rect.w > GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y)
|
||||
{
|
||||
if (src && src->m_target && src->m_from_target == t && src->m_target_direct)
|
||||
Target* t = *i;
|
||||
// Don't pull in targets without valid lower 24 bits unless the Z is 32bits and the alpha is valid, it makes no sense to convert them otherwise.
|
||||
// FIXME: Technically the difference in size is fine, but if the target gets reinterpreted, the hw renderer doesn't rearrange the target.
|
||||
// This does cause some extra uploads in some games (like Burnout), but without this, bad data gets displayed in games like Transformers.
|
||||
if (bp != t->m_TEX0.TBP0 || (!t->m_valid_rgb && (!(GSUtil::GetChannelMask(TEX0.PSM) & 0x8) || !(t->m_valid_alpha_low || t->m_valid_alpha_high))) ||
|
||||
(!is_shuffle && t->m_TEX0.TBW != TEX0.TBW && (possible_clear || ((~GSLocalMemory::m_psm[t->m_TEX0.PSM].fmsk | fbmask) == 0xffffffff))))
|
||||
{
|
||||
src->m_target_direct = false;
|
||||
src->m_shared_texture = false;
|
||||
t->m_texture = nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
GL_CACHE("TC: Deleting Z draw %d", GSState::s_n);
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = rev_list.erase(i);
|
||||
delete t;
|
||||
continue;
|
||||
}
|
||||
const GSLocalMemory::psm_t& t_psm_s = GSLocalMemory::m_psm[t->m_TEX0.PSM];
|
||||
if (t_psm_s.bpp != psm_s.bpp)
|
||||
{
|
||||
bool remove_target = possible_clear || (used && !is_shuffle);
|
||||
|
||||
// If we have a BW change, and it's not a multiple of 2 (for a shuffle), the game's going to get a jigsaw
|
||||
// puzzle of pages and can't be expecting to have legitimate data. Tokimeki Memorial 3 reuses a BW 17
|
||||
// buffer as BW 10, and if we don't discard the BW 17 buffer, the BW 10 buffer ends up twice the size.
|
||||
const u32 shuffle_bw = (psm_s.bpp > t_psm_s.bpp) ? (TEX0.TBW / 2u) : (TEX0.TBW * 2u);
|
||||
if (t->m_TEX0.TBW != TEX0.TBW && (t->m_TEX0.TBW != shuffle_bw && !is_shuffle))
|
||||
// If the format is completely different, but it's the same location, it's likely just overwriting it, so get rid.
|
||||
// Make sure it's not currently in use, that could be bad.
|
||||
if (!is_shuffle && (!ds || (ds != t)) &&
|
||||
t->m_TEX0.TBW != TEX0.TBW && TEX0.TBW != 1 && !preserve_rgb && min_rect.w > GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y)
|
||||
{
|
||||
// But we'll make sure the whole existing target's actually being drawn over to be safe.
|
||||
const u32 end_block = GSLocalMemory::GetUnwrappedEndBlockAddress(TEX0.TBP0, TEX0.TBW, TEX0.PSM, draw_rect);
|
||||
if (end_block >= t->UnwrappedEndBlock())
|
||||
{
|
||||
GL_CACHE("TC: Not converting %s at %x TBW %u with end block of %x when we're drawing through %x",
|
||||
to_string(rev_type), t->m_TEX0.TBP0, t->m_TEX0.TBW, t->UnwrappedEndBlock(), end_block);
|
||||
remove_target = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Probably an old target, get rid of it.
|
||||
if (remove_target)
|
||||
{
|
||||
// DT Racer hits this path and causes a crash when RT in RT is disabled,
|
||||
// so let's make sure source and target texture isn't linked/shared before deleting the target.
|
||||
if (src && src->m_target && src->m_from_target == t && src->m_target_direct)
|
||||
{
|
||||
src->m_target_direct = false;
|
||||
@@ -3152,26 +3156,68 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
t->m_texture = nullptr;
|
||||
}
|
||||
|
||||
GL_CACHE("TC: Deleting Z draw %d", GSState::s_n);
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = rev_list.erase(i);
|
||||
delete t;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const GSLocalMemory::psm_t& t_psm_s = GSLocalMemory::m_psm[t->m_TEX0.PSM];
|
||||
if (t_psm_s.bpp != psm_s.bpp)
|
||||
{
|
||||
bool remove_target = possible_clear || (used && !is_shuffle);
|
||||
|
||||
if (t->m_age == 0)
|
||||
{
|
||||
dst_match = t;
|
||||
break;
|
||||
}
|
||||
else if (t->m_age == 1 && (preserve_rgb || (preserve_alpha && (t->m_valid_alpha_low || t->m_valid_alpha_high))))
|
||||
{
|
||||
dst_match = t;
|
||||
// If we have a BW change, and it's not a multiple of 2 (for a shuffle), the game's going to get a jigsaw
|
||||
// puzzle of pages and can't be expecting to have legitimate data. Tokimeki Memorial 3 reuses a BW 17
|
||||
// buffer as BW 10, and if we don't discard the BW 17 buffer, the BW 10 buffer ends up twice the size.
|
||||
const u32 shuffle_bw = (psm_s.bpp > t_psm_s.bpp) ? (TEX0.TBW / 2u) : (TEX0.TBW * 2u);
|
||||
if (t->m_TEX0.TBW != TEX0.TBW && (t->m_TEX0.TBW != shuffle_bw && !is_shuffle))
|
||||
{
|
||||
// But we'll make sure the whole existing target's actually being drawn over to be safe.
|
||||
const u32 end_block = GSLocalMemory::GetUnwrappedEndBlockAddress(TEX0.TBP0, TEX0.TBW, TEX0.PSM, draw_rect);
|
||||
if (end_block >= t->UnwrappedEndBlock())
|
||||
{
|
||||
GL_CACHE("TC: Not converting %s at %x TBW %u with end block of %x when we're drawing through %x",
|
||||
to_string(rev_type), t->m_TEX0.TBP0, t->m_TEX0.TBW, t->UnwrappedEndBlock(), end_block);
|
||||
remove_target = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Probably an old target, get rid of it.
|
||||
if (remove_target)
|
||||
{
|
||||
// DT Racer hits this path and causes a crash when RT in RT is disabled,
|
||||
// so let's make sure source and target texture isn't linked/shared before deleting the target.
|
||||
if (src && src->m_target && src->m_from_target == t && src->m_target_direct)
|
||||
{
|
||||
src->m_target_direct = false;
|
||||
src->m_shared_texture = false;
|
||||
t->m_texture = nullptr;
|
||||
}
|
||||
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = rev_list.erase(i);
|
||||
delete t;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (t->m_age == 0)
|
||||
{
|
||||
dst_match = t;
|
||||
break;
|
||||
}
|
||||
else if (t->m_age == 1 && (preserve_rgb || (preserve_alpha && (t->m_valid_alpha_low || t->m_valid_alpha_high))))
|
||||
{
|
||||
dst_match = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
// We only want to use a matched target if it's actually being used.
|
||||
|
||||
if (dst_match)
|
||||
{
|
||||
// dst_match, we only want to use a matched target if it's actually being used.
|
||||
|
||||
calcRescale(dst_match);
|
||||
|
||||
// If we don't need A, and the existing target doesn't have valid alpha, don't bother converting it.
|
||||
@@ -3188,7 +3234,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
// Clear instead of invalidating if there is anything which isn't touched.
|
||||
clear |= (!preserve_target && fbmask != 0);
|
||||
GIFRegTEX0 new_TEX0;
|
||||
new_TEX0.TBP0 = TEX0.TBP0;
|
||||
new_TEX0.TBP0 = GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets ? dst_match->m_TEX0.TBP0 : TEX0.TBP0;
|
||||
new_TEX0.TBW = (!half_width) ? dst_match->m_TEX0.TBW : TEX0.TBW;
|
||||
new_TEX0.PSM = is_shuffle ? dst_match->m_TEX0.PSM : TEX0.PSM;
|
||||
|
||||
@@ -3203,8 +3249,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
dst->m_valid_alpha_low = dst_match->m_valid_alpha_low; //&& psm_s.trbpp != 24;
|
||||
dst->m_valid_alpha_high = dst_match->m_valid_alpha_high; //&& psm_s.trbpp != 24;
|
||||
dst->m_valid_rgb = dst_match->m_valid_rgb && (dst->m_TEX0.TBW == TEX0.TBW || min_rect.w <= GSLocalMemory::m_psm[dst_match->m_TEX0.PSM].pgs.y);
|
||||
dst->m_was_dst_matched = true;
|
||||
dst_match->m_was_dst_matched = true;
|
||||
dst->m_was_dst_matched = !dst_match->m_was_dst_matched;
|
||||
dst_match->m_valid_rgb = preserve_rgb;
|
||||
|
||||
if (GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp == 16 && GSLocalMemory::m_psm[dst_match->m_TEX0.PSM].bpp > 16)
|
||||
@@ -4306,23 +4351,27 @@ bool GSTextureCache::PrepareDownloadTexture(u32 width, u32 height, GSTexture::Fo
|
||||
}
|
||||
}
|
||||
}*/
|
||||
void GSTextureCache::InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 write_psm, u32 write_bw)
|
||||
void GSTextureCache::InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 write_psm, u32 write_bw, u32 fb_mask, bool ignore_exact)
|
||||
{
|
||||
const bool preserve_alpha = (GSLocalMemory::m_psm[write_psm].trbpp == 24);
|
||||
for (int type = 0; type < 2; type++)
|
||||
const bool preserve_alpha = (GSLocalMemory::m_psm[write_psm].trbpp == 24) || (fb_mask & 0xFF000000);
|
||||
for (int type = 0; type < (ignore_exact ? 1 : 2); type++)
|
||||
{
|
||||
auto& list = m_dst[type];
|
||||
for (auto i = list.begin(); i != list.end();)
|
||||
{
|
||||
Target* const t = *i;
|
||||
if (start_bp != t->m_TEX0.TBP0 && (t->m_TEX0.TBP0 > end_bp || t->UnwrappedEndBlock() < start_bp))
|
||||
|
||||
if ((ignore_exact && start_bp == t->m_TEX0.TBP0) || (start_bp != t->m_TEX0.TBP0 && (t->m_TEX0.TBP0 > end_bp || t->UnwrappedEndBlock() < start_bp)))
|
||||
{
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool compatible_fmt = GSUtil::HasCompatibleBits(t->m_TEX0.PSM, write_psm);
|
||||
const bool compatible_width = std::max(t->m_TEX0.TBW, 1U) == std::max(write_bw, 1U);
|
||||
|
||||
// If not fully contained but they are aligned and or clean, just dirty the area.
|
||||
if (type != DepthStencil && start_bp != t->m_TEX0.TBP0 && (t->m_TEX0.TBP0 < start_bp || t->UnwrappedEndBlock() > end_bp))
|
||||
if ((type != DepthStencil || !compatible_fmt || !compatible_width) && start_bp != t->m_TEX0.TBP0 && (t->m_TEX0.TBP0 < start_bp || t->UnwrappedEndBlock() > end_bp))
|
||||
{
|
||||
const u32 offset = (std::abs(static_cast<int>(start_bp - t->m_TEX0.TBP0)) >> 5) % std::max(1U, t->m_TEX0.TBW);
|
||||
const GSVector4i dirty_rect = t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size).rintersect(t->m_valid);
|
||||
@@ -4338,7 +4387,7 @@ void GSTextureCache::InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 wr
|
||||
{
|
||||
|
||||
RGBAMask mask;
|
||||
mask._u32 = GSUtil::GetChannelMask(write_psm);
|
||||
mask._u32 = GSUtil::GetChannelMask(write_psm, fb_mask);
|
||||
AddDirtyRectTarget(t, invalidate_r, t->m_TEX0.PSM, t->m_TEX0.TBW, mask, false);
|
||||
}
|
||||
|
||||
@@ -4368,7 +4417,8 @@ void GSTextureCache::InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 wr
|
||||
|
||||
t->m_valid_alpha_low &= preserve_alpha;
|
||||
t->m_valid_alpha_high &= preserve_alpha;
|
||||
t->m_valid_rgb = false;
|
||||
t->m_valid_rgb &= (fb_mask & 0x00FFFFFF) != 0;
|
||||
t->m_was_dst_matched = false;
|
||||
|
||||
// Don't keep partial depth buffers around.
|
||||
if ((!t->m_valid_alpha_low && !t->m_valid_alpha_high && !t->m_valid_rgb) || type == DepthStencil)
|
||||
@@ -4379,7 +4429,22 @@ void GSTextureCache::InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 wr
|
||||
Target* const rev_t = *j;
|
||||
if (rev_t->m_TEX0.TBP0 == t->m_TEX0.TBP0 && GSLocalMemory::m_psm[rev_t->m_TEX0.PSM].bpp == GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp)
|
||||
{
|
||||
rev_t->m_was_dst_matched = false;
|
||||
if (t->m_last_draw > rev_t->m_last_draw && GSUtil::GetChannelMask(t->m_TEX0.PSM) == GSUtil::GetChannelMask(rev_t->m_TEX0.PSM))
|
||||
{
|
||||
if (type == DepthStencil && GSUtil::GetChannelMask(t->m_TEX0.PSM) == 0x7)
|
||||
{
|
||||
rev_t->m_valid_rgb = false;
|
||||
rev_t->m_was_dst_matched = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
GL_CACHE("TC: InvalidateContainedTargets: Remove Target %s[%x, %s]", to_string(1 - type), rev_t->m_TEX0.TBP0, GSUtil::GetPSMName(rev_t->m_TEX0.PSM));
|
||||
rev_list.erase(j);
|
||||
delete rev_t;
|
||||
}
|
||||
}
|
||||
else
|
||||
rev_t->m_was_dst_matched = false;
|
||||
break;
|
||||
}
|
||||
++j;
|
||||
@@ -4390,6 +4455,16 @@ void GSTextureCache::InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 wr
|
||||
delete t;
|
||||
continue;
|
||||
}
|
||||
else if (ignore_exact && GSUtil::HasCompatibleBits(t->m_TEX0.PSM, write_psm))
|
||||
{
|
||||
RGBAMask mask;
|
||||
mask._u32 = GSUtil::GetChannelMask(write_psm, fb_mask);
|
||||
|
||||
AddDirtyRectTarget(t, t->m_valid, t->m_TEX0.PSM, t->m_TEX0.TBW, mask, false);
|
||||
t->m_valid_rgb |= !!(mask._u32 & 0x7);
|
||||
t->m_valid_alpha_low |= mask.c.a;
|
||||
t->m_valid_alpha_high |= mask.c.a;
|
||||
}
|
||||
|
||||
GL_CACHE("TC: InvalidateContainedTargets: Clear RGB valid on %s[%x, %s]", to_string(type), t->m_TEX0.TBP0, GSUtil::GetPSMName(t->m_TEX0.PSM));
|
||||
++i;
|
||||
@@ -5073,14 +5148,14 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
|
||||
// We use dx/dy == 0 and the TBW check as a safeguard to make sure these go through to local memory.
|
||||
// We can also recreate the target if it's previously been created in the height cache with a valid size.
|
||||
// Good test case for this is the Xenosaga I cutscene transitions, or Gradius V.
|
||||
if (src && !dst && ((dx == 0 && dy == 0 && ((static_cast<u32>(w) + 63) / 64) <= DBW) || HasTargetInHeightCache(DBP, DBW, DPSM, 10)))
|
||||
if (src && !dst && ((((dx == 0 && dy == 0) || (dx == sx && dy == sy && DBW == src->m_TEX0.TBW)) && ((static_cast<u32>(w) + 63) / 64) <= DBW) || HasTargetInHeightCache(DBP, DBW, DPSM, 10)))
|
||||
{
|
||||
GIFRegTEX0 new_TEX0 = {};
|
||||
new_TEX0.TBP0 = DBP;
|
||||
new_TEX0.TBW = DBW;
|
||||
new_TEX0.PSM = DPSM;
|
||||
|
||||
const GSVector2i target_size = GetTargetSize(DBP, DBW, DPSM, Common::AlignUpPow2(w, 64), h);
|
||||
const GSVector2i target_size = (dx == 0 && dy == 0) ? GetTargetSize(DBP, DBW, DPSM, Common::AlignUpPow2(w, 64), h) : GSVector2i(src->m_valid.z, src->m_valid.w);
|
||||
dst = LookupTarget(new_TEX0, target_size, src->m_scale, src->m_type);
|
||||
if (!dst)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()});
|
||||
|
||||
@@ -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 = 79;
|
||||
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