mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-27 16:02:37 +00:00
Compare commits
530
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b5a33f878 | ||
|
|
ea092a9472 | ||
|
|
18a1e0fadc | ||
|
|
437e69ef1b | ||
|
|
dd408ffd00 | ||
|
|
bce8924dd6 | ||
|
|
fe26c1de17 | ||
|
|
01df42a5f3 | ||
|
|
968cf3c03f | ||
|
|
586cbd4a0d | ||
|
|
940e42f130 | ||
|
|
3f80151a6f | ||
|
|
7e89b97daf | ||
|
|
d63fb62367 | ||
|
|
29e9ea6344 | ||
|
|
9a74ef15c3 | ||
|
|
a9ae04c152 | ||
|
|
8e35e1233b | ||
|
|
5610976720 | ||
|
|
1133272668 | ||
|
|
bbebb6419b | ||
|
|
b67cb81359 | ||
|
|
f3c82a317a | ||
|
|
f426d60ff1 | ||
|
|
b2fdad9e47 | ||
|
|
87971a28f5 | ||
|
|
96d62b3367 | ||
|
|
7516068662 | ||
|
|
6d1ba5870a | ||
|
|
00a16f3120 | ||
|
|
d3d7bd33f8 | ||
|
|
069f928edc | ||
|
|
0f3674f531 | ||
|
|
a21ffde34e | ||
|
|
1208d22547 | ||
|
|
3a27f8a76a | ||
|
|
dff2b19c56 | ||
|
|
a58db4f023 | ||
|
|
7bcd43dbee | ||
|
|
7c5473400e | ||
|
|
23c8a43e37 | ||
|
|
2d37634ee9 | ||
|
|
e026bfc9a2 | ||
|
|
5a31de7955 | ||
|
|
20e57994cf | ||
|
|
59ef88c959 | ||
|
|
1d1f00bdef | ||
|
|
47b26e497f | ||
|
|
733842cad4 | ||
|
|
73363f1272 | ||
|
|
0cd7682012 | ||
|
|
25a44371e3 | ||
|
|
f2455c875e | ||
|
|
2e315a3c62 | ||
|
|
d2fa86579b | ||
|
|
23e7df21b1 | ||
|
|
a305a6c1c6 | ||
|
|
635fb8882b | ||
|
|
fe6f49632c | ||
|
|
c3546577b6 | ||
|
|
c913951f7b | ||
|
|
3902b42096 | ||
|
|
ae573f941a | ||
|
|
20ea54cf0c | ||
|
|
0b4e78c0b2 | ||
|
|
57844a07e2 | ||
|
|
ba18551d6e | ||
|
|
c7b0d77970 | ||
|
|
1af19eabf0 | ||
|
|
32ea0869e5 | ||
|
|
e04007a7c6 | ||
|
|
1a3a15e4cf | ||
|
|
679adbfa5e | ||
|
|
d285efe3b5 | ||
|
|
a64c8d3205 | ||
|
|
82c52c3fab | ||
|
|
dd1e90fcd7 | ||
|
|
ef0155636a | ||
|
|
9d550fbffa | ||
|
|
fcef691e0a | ||
|
|
839c172612 | ||
|
|
9f49ac8249 | ||
|
|
fde550ef66 | ||
|
|
2b3993a595 | ||
|
|
de3e86791c | ||
|
|
6f66f77b09 | ||
|
|
d853aba955 | ||
|
|
cd62693792 | ||
|
|
cb4c8f3878 | ||
|
|
4d371d5f5f | ||
|
|
72963b01e1 | ||
|
|
80a18e82b7 | ||
|
|
574e687734 | ||
|
|
c725ec50ff | ||
|
|
c6f6e306e0 | ||
|
|
b847135321 | ||
|
|
ca3dc8792a | ||
|
|
ac3156df3a | ||
|
|
6aa6e390ee | ||
|
|
e4c10de8b6 | ||
|
|
b3b3aeef73 | ||
|
|
056dcee70c | ||
|
|
13c5bbe916 | ||
|
|
bbe3bd0304 | ||
|
|
f9b279c49d | ||
|
|
b61360a0c0 | ||
|
|
27ee6f02d6 | ||
|
|
2097ebb414 | ||
|
|
587df9145f | ||
|
|
2c5060e14f | ||
|
|
75aaa963d2 | ||
|
|
34ebfe40c3 | ||
|
|
2a54310876 | ||
|
|
8bccd325ec | ||
|
|
ad935311f0 | ||
|
|
19fc0274de | ||
|
|
6cc9e5de11 | ||
|
|
bf7ca29487 | ||
|
|
3cf426662e | ||
|
|
1fd07c2c83 | ||
|
|
f880c58ccc | ||
|
|
49e59e572e | ||
|
|
9d1fc8328f | ||
|
|
c9ff09c7c2 | ||
|
|
ceb2b664e0 | ||
|
|
b5b4c06179 | ||
|
|
d70f9c0b2b | ||
|
|
e847f82fab | ||
|
|
807a594c2d | ||
|
|
ced73c56cc | ||
|
|
9c6f6fa624 | ||
|
|
533176f9ff | ||
|
|
66c1663495 | ||
|
|
30c7715d3f | ||
|
|
093911a1dc | ||
|
|
d83f1f8235 | ||
|
|
50e78d717d | ||
|
|
6ca8201982 | ||
|
|
99a87bd65b | ||
|
|
f9f8e12c35 | ||
|
|
dcaf4205cb | ||
|
|
4735359466 | ||
|
|
211242452b | ||
|
|
6ae5cb7b64 | ||
|
|
997ad60c50 | ||
|
|
44f982bca7 | ||
|
|
0456f5a06b | ||
|
|
b346f58b73 | ||
|
|
ace877ce28 | ||
|
|
cf0cbee02d | ||
|
|
98e7eeb72a | ||
|
|
803c124136 | ||
|
|
d9d17737c3 | ||
|
|
46850f9574 | ||
|
|
314d37155a | ||
|
|
ddcdb654ad | ||
|
|
cdec5c515b | ||
|
|
1eb13dfa5e | ||
|
|
dda628ca81 | ||
|
|
13ad9f510d | ||
|
|
4c769dfbf9 | ||
|
|
ccd19b3e56 | ||
|
|
81d0ea5697 | ||
|
|
43de8bfaac | ||
|
|
86b539a9ea | ||
|
|
81a5dd612f | ||
|
|
9c00c7077b | ||
|
|
49ff962f69 | ||
|
|
c83aac966e | ||
|
|
9918dc2591 | ||
|
|
3ab372ccec | ||
|
|
843c0c59be | ||
|
|
d93812b0bd | ||
|
|
1c68c94c05 | ||
|
|
c66060be10 | ||
|
|
e7b269a9f6 | ||
|
|
d5b19850fb | ||
|
|
e7fe8192bb | ||
|
|
ebe0979563 | ||
|
|
a379304362 | ||
|
|
bec7c510c2 | ||
|
|
6a187ac67e | ||
|
|
9641b51925 | ||
|
|
b96a30ed88 | ||
|
|
2ec0e9e8f3 | ||
|
|
51afce3a97 | ||
|
|
dea80f34ca | ||
|
|
994271e2f6 | ||
|
|
59902d1fd8 | ||
|
|
b92f0a5458 | ||
|
|
251f6d6121 | ||
|
|
0d8c83a5cb | ||
|
|
3624d96c78 | ||
|
|
3213934d42 | ||
|
|
246212789f | ||
|
|
c7f7fd974b | ||
|
|
5cf2f5767a | ||
|
|
e8e580cf6c | ||
|
|
74a78f37d0 | ||
|
|
8d3ebb5a55 | ||
|
|
3628d2b4c1 | ||
|
|
d96d0eeb18 | ||
|
|
bd401a1407 | ||
|
|
070730be0a | ||
|
|
94e7d0a189 | ||
|
|
c28b71f8e0 | ||
|
|
27f015ecd1 | ||
|
|
8596627ced | ||
|
|
d4cca82a22 | ||
|
|
dd9ecd5f65 | ||
|
|
81b0c1302d | ||
|
|
b9b0b87545 | ||
|
|
040b2b9653 | ||
|
|
eccd6f6cac | ||
|
|
0ce19357f6 | ||
|
|
d762d24f73 | ||
|
|
eb917463ad | ||
|
|
c2189de88f | ||
|
|
2fe5c150bc | ||
|
|
5b8e3b51b0 | ||
|
|
f27a136430 | ||
|
|
fb4d97e8a9 | ||
|
|
5ffcd28ca4 | ||
|
|
b3c8f91a84 | ||
|
|
c78a5a9fdf | ||
|
|
858c18468b | ||
|
|
cd74809086 | ||
|
|
e21c5f37d0 | ||
|
|
2d5023fda2 | ||
|
|
98200fed19 | ||
|
|
c48a8bd6ae | ||
|
|
16ea36eb4b | ||
|
|
8f6f65f4c9 | ||
|
|
b81f1fc774 | ||
|
|
c85bd45604 | ||
|
|
5c86f894be | ||
|
|
86e3c90e32 | ||
|
|
93be8c201b | ||
|
|
894bc7de3c | ||
|
|
fc91fd2b1a | ||
|
|
a06a005124 | ||
|
|
e9425e101d | ||
|
|
04b404f3c9 | ||
|
|
80225015c5 | ||
|
|
0c4dfd565e | ||
|
|
0c8d7a1e49 | ||
|
|
a2150c70bb | ||
|
|
160cf5352c | ||
|
|
3b6b3c2040 | ||
|
|
63dd3917e2 | ||
|
|
2333c235f1 | ||
|
|
dd062ceeeb | ||
|
|
e591a66b67 | ||
|
|
b8a459e83d | ||
|
|
7684c343d6 | ||
|
|
e0c055fd74 | ||
|
|
d1eee7b3a9 | ||
|
|
1342c6f1e1 | ||
|
|
6453bafd1d | ||
|
|
beb8047083 | ||
|
|
1df06e230d | ||
|
|
39a34fa343 | ||
|
|
96c64d113e | ||
|
|
66ca225c76 | ||
|
|
0de4cfbf80 | ||
|
|
42980fe6df | ||
|
|
1592cf2b1b | ||
|
|
a9166f8546 | ||
|
|
b77978fd1e | ||
|
|
4330cb9462 | ||
|
|
af8aecfdde | ||
|
|
13533bd053 | ||
|
|
cd0f0a428a | ||
|
|
6758d67942 | ||
|
|
dc5b7c19a6 | ||
|
|
2a54306e8b | ||
|
|
7a6aaafe52 | ||
|
|
1469c53048 | ||
|
|
9992ab18fc | ||
|
|
18af08460f | ||
|
|
39ee9972b8 | ||
|
|
e500333bcd | ||
|
|
4498d75b9b | ||
|
|
1df53b169f | ||
|
|
ff61c1cecd | ||
|
|
60a01817ce | ||
|
|
1a87cfedb5 | ||
|
|
5bd447d2a2 | ||
|
|
a4fc58e908 | ||
|
|
4e8e335b7b | ||
|
|
947576363e | ||
|
|
9da390bc0a | ||
|
|
ef639610e7 | ||
|
|
6c3b562822 | ||
|
|
f1413569ce | ||
|
|
fcc9dff5b9 | ||
|
|
d9ae3ed64c | ||
|
|
618926b124 | ||
|
|
dca7ece2ca | ||
|
|
ccecebfeff | ||
|
|
1752f1382f | ||
|
|
53a59434d8 | ||
|
|
18b6c9059b | ||
|
|
715539804b | ||
|
|
b02eb28fd1 | ||
|
|
07f1e77d23 | ||
|
|
6980e0863d | ||
|
|
42bfeec2e9 | ||
|
|
795743ce22 | ||
|
|
ccddd6fb94 | ||
|
|
e559771a08 | ||
|
|
b870a77176 | ||
|
|
62eaead3a3 | ||
|
|
d7808d9cb4 | ||
|
|
8267c422b5 | ||
|
|
b46e141762 | ||
|
|
cfa140fb12 | ||
|
|
c52bf450f5 | ||
|
|
4cc2f00273 | ||
|
|
ade32e7ba7 | ||
|
|
cc5c0ec6b8 | ||
|
|
c167375fe1 | ||
|
|
65871a53a6 | ||
|
|
1ff580498c | ||
|
|
ae896d0bf6 | ||
|
|
e738535294 | ||
|
|
8072718462 | ||
|
|
7adade0df5 | ||
|
|
ab50c10d08 | ||
|
|
ffa69d48de | ||
|
|
e2c840cb23 | ||
|
|
fa891536df | ||
|
|
7012634f32 | ||
|
|
663b524fb3 | ||
|
|
0b3302af70 | ||
|
|
ffd07dbfce | ||
|
|
6eed46b024 | ||
|
|
e6ad87072d | ||
|
|
dd2fbd6c45 | ||
|
|
1779142805 | ||
|
|
186621c6ff | ||
|
|
11d4c0d2a2 | ||
|
|
aa69b5b184 | ||
|
|
cb378365e1 | ||
|
|
30fa9dddc5 | ||
|
|
4d8d3b6d25 | ||
|
|
523cc17ffe | ||
|
|
825e5f1162 | ||
|
|
3c016bead1 | ||
|
|
800a7719cf | ||
|
|
e3fb05c1a8 | ||
|
|
bc9c70d50f | ||
|
|
a8fe4e7a2d | ||
|
|
bed19e5d8c | ||
|
|
4adbf40861 | ||
|
|
8d01fb1c18 | ||
|
|
a610528075 | ||
|
|
3e25eb0998 | ||
|
|
0d92ce49da | ||
|
|
4179bb5b06 | ||
|
|
d581703ab4 | ||
|
|
9241fdc32a | ||
|
|
a2baaccc6e | ||
|
|
c130d74efc | ||
|
|
5e64efce4f | ||
|
|
17c2b4cc7d | ||
|
|
bad6726f5e | ||
|
|
26c4168160 | ||
|
|
49ea1680f8 | ||
|
|
79443aa104 | ||
|
|
342fae06a6 | ||
|
|
7022afa86e | ||
|
|
a44dd88806 | ||
|
|
d0a4c908a1 | ||
|
|
61dd8a3031 | ||
|
|
770bdb9571 | ||
|
|
719e20d470 | ||
|
|
fedd1a4335 | ||
|
|
ebd47081ca | ||
|
|
1546e93d67 | ||
|
|
94325b5ed6 | ||
|
|
86dbf89188 | ||
|
|
f119e4baca | ||
|
|
494467406a | ||
|
|
0966938dc8 | ||
|
|
bc5709e1bd | ||
|
|
767d0b7da9 | ||
|
|
d5410a93c8 | ||
|
|
129c64235f | ||
|
|
1190d5de7d | ||
|
|
8021150ea2 | ||
|
|
88c351f0fa | ||
|
|
e78bda60a0 | ||
|
|
22bdafecbb | ||
|
|
6f9ed6ffe5 | ||
|
|
fbdc1e8714 | ||
|
|
bac6bf65af | ||
|
|
5ad7d0ebc2 | ||
|
|
7f66a61801 | ||
|
|
2bd9f4124f | ||
|
|
705fc93363 | ||
|
|
77db9164b8 | ||
|
|
a42e3bb09e | ||
|
|
69156b2f0e | ||
|
|
56a15c2036 | ||
|
|
2db0b3cfa7 | ||
|
|
41bfba515c | ||
|
|
4710522e87 | ||
|
|
85cf965d54 | ||
|
|
87166eed94 | ||
|
|
6e9f162bd4 | ||
|
|
8063822243 | ||
|
|
c236ba713d | ||
|
|
c9e942a972 | ||
|
|
5383972e96 | ||
|
|
3ea2099245 | ||
|
|
49145c1902 | ||
|
|
d34e30d892 | ||
|
|
e1bcc298a0 | ||
|
|
33545ca6c4 | ||
|
|
562f8a0895 | ||
|
|
7e42513faf | ||
|
|
596ecdcca0 | ||
|
|
5d83ce82af | ||
|
|
4fb72e9db7 | ||
|
|
c4ca8f4bf8 | ||
|
|
a4b970d97d | ||
|
|
8cfc9cd7a8 | ||
|
|
e39162fc52 | ||
|
|
6bf69297c6 | ||
|
|
95d282854d | ||
|
|
c59a4565bc | ||
|
|
79e424182b | ||
|
|
fe867d03f6 | ||
|
|
ae4e695425 | ||
|
|
6407add888 | ||
|
|
840d742230 | ||
|
|
a798e12d7f | ||
|
|
d80e914b34 | ||
|
|
395c34ecab | ||
|
|
1e8317159e | ||
|
|
d0e08496be | ||
|
|
377fb8f50a | ||
|
|
4220a747cb | ||
|
|
e24f29d514 | ||
|
|
54aa8efc98 | ||
|
|
89fdb1214a | ||
|
|
01275fb211 | ||
|
|
5970c4ef95 | ||
|
|
24b40d4c03 | ||
|
|
146253afb6 | ||
|
|
c759236ccb | ||
|
|
ce39238210 | ||
|
|
330889e50b | ||
|
|
1be9568a02 | ||
|
|
8ce06ccdf6 | ||
|
|
1dbac4fd79 | ||
|
|
d80fbea51d | ||
|
|
e239210279 | ||
|
|
e7c8bcb3a4 | ||
|
|
0f3540b1ff | ||
|
|
5ea92d95a7 | ||
|
|
5a32e270ad | ||
|
|
e857e332ac | ||
|
|
44302cbf96 | ||
|
|
6b306f64d5 | ||
|
|
e1366eb91d | ||
|
|
2a1a8e72e3 | ||
|
|
191cdff231 | ||
|
|
7d8ee8b6b5 | ||
|
|
01a1036640 | ||
|
|
7850cbeaef | ||
|
|
af931d73c7 | ||
|
|
3a7cd93765 | ||
|
|
a34bcbd951 | ||
|
|
b0bd6470f3 | ||
|
|
93398d6d0f | ||
|
|
4e343408dc | ||
|
|
1375d650a7 | ||
|
|
d7f1bcaa19 | ||
|
|
2bb396f58f | ||
|
|
af1ab60786 | ||
|
|
430de9bd15 | ||
|
|
1c4171c9eb | ||
|
|
443bbcbf90 | ||
|
|
83ee040b4d | ||
|
|
980d89f739 | ||
|
|
f785974cc1 | ||
|
|
547b9b96cc | ||
|
|
989477efad | ||
|
|
6d273e4d9c | ||
|
|
d7fd4a2864 | ||
|
|
5e27a28ab0 | ||
|
|
67e550f2e7 | ||
|
|
aec9b97b0c | ||
|
|
e4a95b2c26 | ||
|
|
6e82c376f6 | ||
|
|
4f1bc505cc | ||
|
|
484433b180 | ||
|
|
70f6af407e | ||
|
|
7556a74f19 | ||
|
|
b4809e7170 | ||
|
|
0df20eb300 | ||
|
|
5bfdf4267e | ||
|
|
f3cdf217ea | ||
|
|
f453a752bb | ||
|
|
28a913894d | ||
|
|
0ffacf22e6 | ||
|
|
13e87a8e58 | ||
|
|
30d57bed06 | ||
|
|
965c30956d | ||
|
|
09fec6815f | ||
|
|
00bd19194a | ||
|
|
4a7bc55afa | ||
|
|
9c316e6988 | ||
|
|
a3c3cbb722 | ||
|
|
b42380ced6 | ||
|
|
bc2f077c37 | ||
|
|
498d63b620 | ||
|
|
566a45017e | ||
|
|
7a40dad90f | ||
|
|
a9e843b38e | ||
|
|
9a76719544 | ||
|
|
472c8213db | ||
|
|
a4f9c022ff | ||
|
|
97514884b1 | ||
|
|
4980efa9b7 | ||
|
|
34cf130e4d | ||
|
|
21aa7b66ce | ||
|
|
11b95bed20 |
No files matched your search
+47
-22
@@ -208,34 +208,66 @@ jobs:
|
||||
run: |
|
||||
archive="$RUNNER_TEMP/MoltenVK-macos.tar"
|
||||
package="$RUNNER_TEMP/MoltenVK"
|
||||
install_dir="_Build/macos/install"
|
||||
app_dir="$install_dir/KytyPS5.app"
|
||||
moltenvk="$install_dir/libMoltenVK.dylib"
|
||||
curl --fail --location --retry 3 \
|
||||
--output "$archive" \
|
||||
"https://github.com/KhronosGroup/MoltenVK/releases/download/$MOLTENVK_VERSION/MoltenVK-macos.tar"
|
||||
echo "$MOLTENVK_SHA256 $archive" | shasum -a 256 --check
|
||||
tar -xf "$archive" -C "$RUNNER_TEMP"
|
||||
test -d "$app_dir"
|
||||
install -m 755 \
|
||||
"$package/MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib" \
|
||||
_Build/macos/install/libMoltenVK.dylib
|
||||
install -m 644 "$package/LICENSE" _Build/macos/install/LICENSE.MoltenVK
|
||||
codesign --force --sign - --timestamp=none _Build/macos/install/libMoltenVK.dylib
|
||||
"$moltenvk"
|
||||
install -m 644 "$package/LICENSE" "$install_dir/LICENSE.MoltenVK"
|
||||
codesign --force --sign - --timestamp=none "$moltenvk"
|
||||
install -d -m 755 "$app_dir/Contents/Frameworks" "$app_dir/Contents/Resources"
|
||||
install -m 755 "$moltenvk" "$app_dir/Contents/Frameworks/libMoltenVK.dylib"
|
||||
install -m 644 "$package/LICENSE" "$app_dir/Contents/Resources/LICENSE.MoltenVK"
|
||||
codesign --force --sign - --timestamp=none "$app_dir"
|
||||
|
||||
- name: Verify artifacts
|
||||
shell: bash
|
||||
run: |
|
||||
file _Build/macos/install/launcher
|
||||
file _Build/macos/install/kyty_emulator
|
||||
file _Build/macos/install/libMoltenVK.dylib
|
||||
lipo _Build/macos/install/launcher -verify_arch x86_64
|
||||
lipo _Build/macos/install/kyty_emulator -verify_arch x86_64
|
||||
lipo _Build/macos/install/libMoltenVK.dylib -verify_arch x86_64
|
||||
codesign --verify --strict _Build/macos/install/kyty_emulator
|
||||
codesign --verify --strict _Build/macos/install/libMoltenVK.dylib
|
||||
install_dir="_Build/macos/install"
|
||||
app_dir="$install_dir/KytyPS5.app"
|
||||
test -d "$app_dir"
|
||||
for binary in \
|
||||
"$install_dir/kyty_emulator" \
|
||||
"$install_dir/libMoltenVK.dylib" \
|
||||
"$app_dir/Contents/MacOS/KytyPS5" \
|
||||
"$app_dir/Contents/MacOS/kyty_emulator" \
|
||||
"$app_dir/Contents/Frameworks/libMoltenVK.dylib"; do
|
||||
file "$binary"
|
||||
lipo "$binary" -verify_arch x86_64
|
||||
done
|
||||
for binary in \
|
||||
"$install_dir/kyty_emulator" \
|
||||
"$install_dir/libMoltenVK.dylib" \
|
||||
"$app_dir/Contents/MacOS/KytyPS5" \
|
||||
"$app_dir/Contents/MacOS/kyty_emulator" \
|
||||
"$app_dir/Contents/Frameworks/libMoltenVK.dylib"; do
|
||||
codesign --verify --strict "$binary"
|
||||
done
|
||||
codesign --verify --deep --strict --verbose=2 "$app_dir"
|
||||
|
||||
- name: Package macOS artifacts
|
||||
shell: bash
|
||||
run: |
|
||||
install_dir="_Build/macos/install"
|
||||
archive="$RUNNER_TEMP/KytyPS5-macOS-x86_64.zip"
|
||||
(
|
||||
cd "$install_dir"
|
||||
zip -qry "$archive" .
|
||||
)
|
||||
unzip -t "$archive"
|
||||
|
||||
- name: Upload macOS artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: KytyPS5-macOS-x86_64
|
||||
path: _Build/macos/install/**
|
||||
path: ${{ runner.temp }}/KytyPS5-macOS-x86_64.zip
|
||||
if-no-files-found: error
|
||||
|
||||
linux:
|
||||
@@ -424,16 +456,9 @@ jobs:
|
||||
if: needs.macos.result == 'success'
|
||||
shell: bash
|
||||
run: |
|
||||
macos_dir="artifacts/KytyPS5-macOS-x86_64"
|
||||
test -d "$macos_dir"
|
||||
chmod a+x \
|
||||
"$macos_dir/launcher" \
|
||||
"$macos_dir/kyty_emulator" \
|
||||
"$macos_dir/libMoltenVK.dylib"
|
||||
(
|
||||
cd "$macos_dir"
|
||||
zip -r "$GITHUB_WORKSPACE/$RELEASE_NAME-macOS-x86_64.zip" .
|
||||
)
|
||||
macos_archive="artifacts/KytyPS5-macOS-x86_64/KytyPS5-macOS-x86_64.zip"
|
||||
test -f "$macos_archive"
|
||||
cp "$macos_archive" "$RELEASE_NAME-macOS-x86_64.zip"
|
||||
|
||||
- name: Package Linux build
|
||||
if: needs.linux.result == 'success'
|
||||
|
||||
@@ -5,3 +5,4 @@ build/
|
||||
_Build/vscode-clang/
|
||||
_Build/
|
||||
cmake-build-debug/
|
||||
_PipelineCache/
|
||||
Vendored
+37
-3
@@ -10,7 +10,14 @@
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}\\_Build\\vscode-clang\\launcher",
|
||||
"environment": [],
|
||||
"externalConsole": false
|
||||
"externalConsole": false,
|
||||
"linux": {
|
||||
"type": "cppdbg",
|
||||
"program": "${workspaceFolder}/_Build/vscode-clang/launcher/launcher",
|
||||
"cwd": "${workspaceFolder}/_Build/vscode-clang/launcher",
|
||||
"MIMode": "gdb",
|
||||
"miDebuggerPath": "/usr/bin/gdb"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Debug kyty_emulator",
|
||||
@@ -25,13 +32,40 @@
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}\\_Build\\vscode-clang",
|
||||
"environment": [],
|
||||
"externalConsole": true
|
||||
"externalConsole": true,
|
||||
"linux": {
|
||||
"type": "cppdbg",
|
||||
"program": "${workspaceFolder}/_Build/vscode-clang/kyty_emulator",
|
||||
"cwd": "${workspaceFolder}/_Build/vscode-clang",
|
||||
"externalConsole": false,
|
||||
"MIMode": "gdb",
|
||||
"miDebuggerPath": "/usr/bin/gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Pass Kyty's internal signals to its handlers",
|
||||
"text": "handle SIG37 SIGSEGV SIGBUS SIGILL nostop noprint pass",
|
||||
"ignoreFailures": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Attach to running kyty_emulator",
|
||||
"type": "cppvsdbg",
|
||||
"request": "attach",
|
||||
"processId": "${command:pickProcess}"
|
||||
"processId": "${command:pickProcess}",
|
||||
"linux": {
|
||||
"type": "cppdbg",
|
||||
"MIMode": "gdb",
|
||||
"miDebuggerPath": "/usr/bin/gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Pass Kyty's internal signals to its handlers",
|
||||
"text": "handle SIG37 SIGSEGV SIGBUS SIGILL nostop noprint pass",
|
||||
"ignoreFailures": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+875
@@ -0,0 +1,875 @@
|
||||
/******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2026 Baldur Karlsson
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Documentation for the API is available at https://renderdoc.org/docs/in_application_api.html
|
||||
//
|
||||
|
||||
#if !defined(RENDERDOC_NO_STDINT)
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
|
||||
#define RENDERDOC_CC __cdecl
|
||||
#elif defined(__linux__) || defined(__FreeBSD__) || defined(__sun__) || defined(__OpenBSD__)
|
||||
#define RENDERDOC_CC
|
||||
#elif defined(__APPLE__)
|
||||
#define RENDERDOC_CC
|
||||
#else
|
||||
#error "Unknown platform"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constants not used directly in below API
|
||||
|
||||
// This is a GUID/magic value used for when applications pass a path where shader debug
|
||||
// information can be found to match up with a stripped shader.
|
||||
// the define can be used like so: const GUID RENDERDOC_ShaderDebugMagicValue =
|
||||
// RENDERDOC_ShaderDebugMagicValue_value
|
||||
#define RENDERDOC_ShaderDebugMagicValue_struct \
|
||||
{ \
|
||||
0xeab25520, 0x6670, 0x4865, 0x84, 0x29, 0x6c, 0x8, 0x51, 0x54, 0x00, 0xff \
|
||||
}
|
||||
|
||||
// as an alternative when you want a byte array (assuming x86 endianness):
|
||||
#define RENDERDOC_ShaderDebugMagicValue_bytearray \
|
||||
{ \
|
||||
0x20, 0x55, 0xb2, 0xea, 0x70, 0x66, 0x65, 0x48, 0x84, 0x29, 0x6c, 0x8, 0x51, 0x54, 0x00, 0xff \
|
||||
}
|
||||
|
||||
// truncated version when only a uint64_t is available (e.g. Vulkan tags):
|
||||
#define RENDERDOC_ShaderDebugMagicValue_truncated 0x48656670eab25520ULL
|
||||
|
||||
// this is a magic value for vulkan user tags to indicate which dispatchable API objects are which
|
||||
// for object annotations
|
||||
#define RENDERDOC_APIObjectAnnotationHelper 0xfbb3b337b664d0adULL
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// RenderDoc capture options
|
||||
//
|
||||
|
||||
typedef enum RENDERDOC_CaptureOption
|
||||
{
|
||||
// Allow the application to enable vsync
|
||||
//
|
||||
// Default - enabled
|
||||
//
|
||||
// 1 - The application can enable or disable vsync at will
|
||||
// 0 - vsync is force disabled
|
||||
eRENDERDOC_Option_AllowVSync = 0,
|
||||
|
||||
// Allow the application to enable fullscreen
|
||||
//
|
||||
// Default - enabled
|
||||
//
|
||||
// 1 - The application can enable or disable fullscreen at will
|
||||
// 0 - fullscreen is force disabled
|
||||
eRENDERDOC_Option_AllowFullscreen = 1,
|
||||
|
||||
// Record API debugging events and messages
|
||||
//
|
||||
// Default - disabled
|
||||
//
|
||||
// 1 - Enable built-in API debugging features and records the results into
|
||||
// the capture, which is matched up with events on replay
|
||||
// 0 - no API debugging is forcibly enabled
|
||||
eRENDERDOC_Option_APIValidation = 2,
|
||||
eRENDERDOC_Option_DebugDeviceMode = 2, // deprecated name of this enum
|
||||
|
||||
// Capture CPU callstacks for API events
|
||||
//
|
||||
// Default - disabled
|
||||
//
|
||||
// 1 - Enables capturing of callstacks
|
||||
// 0 - no callstacks are captured
|
||||
eRENDERDOC_Option_CaptureCallstacks = 3,
|
||||
|
||||
// When capturing CPU callstacks, only capture them from actions.
|
||||
// This option does nothing without the above option being enabled
|
||||
//
|
||||
// Default - disabled
|
||||
//
|
||||
// 1 - Only captures callstacks for actions.
|
||||
// Ignored if CaptureCallstacks is disabled
|
||||
// 0 - Callstacks, if enabled, are captured for every event.
|
||||
eRENDERDOC_Option_CaptureCallstacksOnlyDraws = 4,
|
||||
eRENDERDOC_Option_CaptureCallstacksOnlyActions = 4,
|
||||
|
||||
// Specify a delay in seconds to wait for a debugger to attach, after
|
||||
// creating or injecting into a process, before continuing to allow it to run.
|
||||
//
|
||||
// 0 indicates no delay, and the process will run immediately after injection
|
||||
//
|
||||
// Default - 0 seconds
|
||||
//
|
||||
eRENDERDOC_Option_DelayForDebugger = 5,
|
||||
|
||||
// Verify buffer access. This includes checking the memory returned by a Map() call to
|
||||
// detect any out-of-bounds modification, as well as initialising buffers with undefined contents
|
||||
// to a marker value to catch use of uninitialised memory.
|
||||
//
|
||||
// NOTE: This option is only valid for OpenGL and D3D11. Explicit APIs such as D3D12 and Vulkan do
|
||||
// not do the same kind of interception & checking and undefined contents are really undefined.
|
||||
//
|
||||
// Default - disabled
|
||||
//
|
||||
// 1 - Verify buffer access
|
||||
// 0 - No verification is performed, and overwriting bounds may cause crashes or corruption in
|
||||
// RenderDoc.
|
||||
eRENDERDOC_Option_VerifyBufferAccess = 6,
|
||||
|
||||
// The old name for eRENDERDOC_Option_VerifyBufferAccess was eRENDERDOC_Option_VerifyMapWrites.
|
||||
// This option now controls the filling of uninitialised buffers with 0xdddddddd which was
|
||||
// previously always enabled
|
||||
eRENDERDOC_Option_VerifyMapWrites = eRENDERDOC_Option_VerifyBufferAccess,
|
||||
|
||||
// Hooks any system API calls that create child processes, and injects
|
||||
// RenderDoc into them recursively with the same options.
|
||||
//
|
||||
// Default - disabled
|
||||
//
|
||||
// 1 - Hooks into spawned child processes
|
||||
// 0 - Child processes are not hooked by RenderDoc
|
||||
eRENDERDOC_Option_HookIntoChildren = 7,
|
||||
|
||||
// By default RenderDoc only includes resources in the final capture necessary
|
||||
// for that frame, this allows you to override that behaviour.
|
||||
//
|
||||
// Default - disabled
|
||||
//
|
||||
// 1 - all live resources at the time of capture are included in the capture
|
||||
// and available for inspection
|
||||
// 0 - only the resources referenced by the captured frame are included
|
||||
eRENDERDOC_Option_RefAllResources = 8,
|
||||
|
||||
// **NOTE**: As of RenderDoc v1.1 this option has been deprecated. Setting or
|
||||
// getting it will be ignored, to allow compatibility with older versions.
|
||||
// In v1.1 the option acts as if it's always enabled.
|
||||
//
|
||||
// By default RenderDoc skips saving initial states for resources where the
|
||||
// previous contents don't appear to be used, assuming that writes before
|
||||
// reads indicate previous contents aren't used.
|
||||
//
|
||||
// Default - disabled
|
||||
//
|
||||
// 1 - initial contents at the start of each captured frame are saved, even if
|
||||
// they are later overwritten or cleared before being used.
|
||||
// 0 - unless a read is detected, initial contents will not be saved and will
|
||||
// appear as black or empty data.
|
||||
eRENDERDOC_Option_SaveAllInitials = 9,
|
||||
|
||||
// In APIs that allow for the recording of command lists to be replayed later,
|
||||
// RenderDoc may choose to not capture command lists before a frame capture is
|
||||
// triggered, to reduce overheads. This means any command lists recorded once
|
||||
// and replayed many times will not be available and may cause a failure to
|
||||
// capture.
|
||||
//
|
||||
// NOTE: This is only true for APIs where multithreading is difficult or
|
||||
// discouraged. Newer APIs like Vulkan and D3D12 will ignore this option
|
||||
// and always capture all command lists since the API is heavily oriented
|
||||
// around it and the overheads have been reduced by API design.
|
||||
//
|
||||
// 1 - All command lists are captured from the start of the application
|
||||
// 0 - Command lists are only captured if their recording begins during
|
||||
// the period when a frame capture is in progress.
|
||||
eRENDERDOC_Option_CaptureAllCmdLists = 10,
|
||||
|
||||
// Mute API debugging output when the API validation mode option is enabled
|
||||
//
|
||||
// Default - enabled
|
||||
//
|
||||
// 1 - Mute any API debug messages from being displayed or passed through
|
||||
// 0 - API debugging is displayed as normal
|
||||
eRENDERDOC_Option_DebugOutputMute = 11,
|
||||
|
||||
// Option to allow vendor extensions to be used even when they may be
|
||||
// incompatible with RenderDoc and cause corrupted replays or crashes.
|
||||
//
|
||||
// Default - inactive
|
||||
//
|
||||
// No values are documented, this option should only be used when absolutely
|
||||
// necessary as directed by a RenderDoc developer.
|
||||
eRENDERDOC_Option_AllowUnsupportedVendorExtensions = 12,
|
||||
|
||||
// Define a soft memory limit which some APIs may aim to keep overhead under where
|
||||
// possible. Anything above this limit will where possible be saved directly to disk during
|
||||
// capture.
|
||||
// This will cause increased disk space use (which may cause a capture to fail if disk space is
|
||||
// exhausted) as well as slower capture times.
|
||||
//
|
||||
// Not all memory allocations may be deferred like this so it is not a guarantee of a memory
|
||||
// limit.
|
||||
//
|
||||
// Units are in MBs, suggested values would range from 200MB to 1000MB.
|
||||
//
|
||||
// Default - 0 Megabytes
|
||||
eRENDERDOC_Option_SoftMemoryLimit = 13,
|
||||
} RENDERDOC_CaptureOption;
|
||||
|
||||
// Sets an option that controls how RenderDoc behaves on capture.
|
||||
//
|
||||
// Returns 1 if the option and value are valid
|
||||
// Returns 0 if either is invalid and the option is unchanged
|
||||
typedef int(RENDERDOC_CC *pRENDERDOC_SetCaptureOptionU32)(RENDERDOC_CaptureOption opt, uint32_t val);
|
||||
typedef int(RENDERDOC_CC *pRENDERDOC_SetCaptureOptionF32)(RENDERDOC_CaptureOption opt, float val);
|
||||
|
||||
// Gets the current value of an option as a uint32_t
|
||||
//
|
||||
// If the option is invalid, 0xffffffff is returned
|
||||
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetCaptureOptionU32)(RENDERDOC_CaptureOption opt);
|
||||
|
||||
// Gets the current value of an option as a float
|
||||
//
|
||||
// If the option is invalid, -FLT_MAX is returned
|
||||
typedef float(RENDERDOC_CC *pRENDERDOC_GetCaptureOptionF32)(RENDERDOC_CaptureOption opt);
|
||||
|
||||
typedef enum RENDERDOC_InputButton
|
||||
{
|
||||
// '0' - '9' matches ASCII values
|
||||
eRENDERDOC_Key_0 = 0x30,
|
||||
eRENDERDOC_Key_1 = 0x31,
|
||||
eRENDERDOC_Key_2 = 0x32,
|
||||
eRENDERDOC_Key_3 = 0x33,
|
||||
eRENDERDOC_Key_4 = 0x34,
|
||||
eRENDERDOC_Key_5 = 0x35,
|
||||
eRENDERDOC_Key_6 = 0x36,
|
||||
eRENDERDOC_Key_7 = 0x37,
|
||||
eRENDERDOC_Key_8 = 0x38,
|
||||
eRENDERDOC_Key_9 = 0x39,
|
||||
|
||||
// 'A' - 'Z' matches ASCII values
|
||||
eRENDERDOC_Key_A = 0x41,
|
||||
eRENDERDOC_Key_B = 0x42,
|
||||
eRENDERDOC_Key_C = 0x43,
|
||||
eRENDERDOC_Key_D = 0x44,
|
||||
eRENDERDOC_Key_E = 0x45,
|
||||
eRENDERDOC_Key_F = 0x46,
|
||||
eRENDERDOC_Key_G = 0x47,
|
||||
eRENDERDOC_Key_H = 0x48,
|
||||
eRENDERDOC_Key_I = 0x49,
|
||||
eRENDERDOC_Key_J = 0x4A,
|
||||
eRENDERDOC_Key_K = 0x4B,
|
||||
eRENDERDOC_Key_L = 0x4C,
|
||||
eRENDERDOC_Key_M = 0x4D,
|
||||
eRENDERDOC_Key_N = 0x4E,
|
||||
eRENDERDOC_Key_O = 0x4F,
|
||||
eRENDERDOC_Key_P = 0x50,
|
||||
eRENDERDOC_Key_Q = 0x51,
|
||||
eRENDERDOC_Key_R = 0x52,
|
||||
eRENDERDOC_Key_S = 0x53,
|
||||
eRENDERDOC_Key_T = 0x54,
|
||||
eRENDERDOC_Key_U = 0x55,
|
||||
eRENDERDOC_Key_V = 0x56,
|
||||
eRENDERDOC_Key_W = 0x57,
|
||||
eRENDERDOC_Key_X = 0x58,
|
||||
eRENDERDOC_Key_Y = 0x59,
|
||||
eRENDERDOC_Key_Z = 0x5A,
|
||||
|
||||
// leave the rest of the ASCII range free
|
||||
// in case we want to use it later
|
||||
eRENDERDOC_Key_NonPrintable = 0x100,
|
||||
|
||||
eRENDERDOC_Key_Divide,
|
||||
eRENDERDOC_Key_Multiply,
|
||||
eRENDERDOC_Key_Subtract,
|
||||
eRENDERDOC_Key_Plus,
|
||||
|
||||
eRENDERDOC_Key_F1,
|
||||
eRENDERDOC_Key_F2,
|
||||
eRENDERDOC_Key_F3,
|
||||
eRENDERDOC_Key_F4,
|
||||
eRENDERDOC_Key_F5,
|
||||
eRENDERDOC_Key_F6,
|
||||
eRENDERDOC_Key_F7,
|
||||
eRENDERDOC_Key_F8,
|
||||
eRENDERDOC_Key_F9,
|
||||
eRENDERDOC_Key_F10,
|
||||
eRENDERDOC_Key_F11,
|
||||
eRENDERDOC_Key_F12,
|
||||
|
||||
eRENDERDOC_Key_Home,
|
||||
eRENDERDOC_Key_End,
|
||||
eRENDERDOC_Key_Insert,
|
||||
eRENDERDOC_Key_Delete,
|
||||
eRENDERDOC_Key_PageUp,
|
||||
eRENDERDOC_Key_PageDn,
|
||||
|
||||
eRENDERDOC_Key_Backspace,
|
||||
eRENDERDOC_Key_Tab,
|
||||
eRENDERDOC_Key_PrtScrn,
|
||||
eRENDERDOC_Key_Pause,
|
||||
|
||||
eRENDERDOC_Key_Max,
|
||||
} RENDERDOC_InputButton;
|
||||
|
||||
// Sets which key or keys can be used to toggle focus between multiple windows
|
||||
//
|
||||
// If keys is NULL or num is 0, toggle keys will be disabled
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_SetFocusToggleKeys)(RENDERDOC_InputButton *keys, int num);
|
||||
|
||||
// Sets which key or keys can be used to capture the next frame
|
||||
//
|
||||
// If keys is NULL or num is 0, captures keys will be disabled
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureKeys)(RENDERDOC_InputButton *keys, int num);
|
||||
|
||||
typedef enum RENDERDOC_OverlayBits
|
||||
{
|
||||
// This single bit controls whether the overlay is enabled or disabled globally
|
||||
eRENDERDOC_Overlay_Enabled = 0x1,
|
||||
|
||||
// Show the average framerate over several seconds as well as min/max
|
||||
eRENDERDOC_Overlay_FrameRate = 0x2,
|
||||
|
||||
// Show the current frame number
|
||||
eRENDERDOC_Overlay_FrameNumber = 0x4,
|
||||
|
||||
// Show a list of recent captures, and how many captures have been made
|
||||
eRENDERDOC_Overlay_CaptureList = 0x8,
|
||||
|
||||
// Default values for the overlay mask
|
||||
eRENDERDOC_Overlay_Default = (eRENDERDOC_Overlay_Enabled | eRENDERDOC_Overlay_FrameRate |
|
||||
eRENDERDOC_Overlay_FrameNumber | eRENDERDOC_Overlay_CaptureList),
|
||||
|
||||
// Enable all bits
|
||||
eRENDERDOC_Overlay_All = 0x7ffffff,
|
||||
|
||||
// Disable all bits
|
||||
eRENDERDOC_Overlay_None = 0,
|
||||
} RENDERDOC_OverlayBits;
|
||||
|
||||
// returns the overlay bits that have been set
|
||||
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetOverlayBits)(void);
|
||||
// sets the overlay bits with an and & or mask
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_MaskOverlayBits)(uint32_t And, uint32_t Or);
|
||||
|
||||
// this function will attempt to remove RenderDoc's hooks in the application.
|
||||
//
|
||||
// Note: that this can only work correctly if done immediately after
|
||||
// the module is loaded, before any API work happens. RenderDoc will remove its
|
||||
// injected hooks and shut down. Behaviour is undefined if this is called
|
||||
// after any API functions have been called, and there is still no guarantee of
|
||||
// success.
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_RemoveHooks)(void);
|
||||
|
||||
// DEPRECATED: compatibility for code compiled against pre-1.4.1 headers.
|
||||
typedef pRENDERDOC_RemoveHooks pRENDERDOC_Shutdown;
|
||||
|
||||
// This function will unload RenderDoc's crash handler.
|
||||
//
|
||||
// If you use your own crash handler and don't want RenderDoc's handler to
|
||||
// intercede, you can call this function to unload it and any unhandled
|
||||
// exceptions will pass to the next handler.
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_UnloadCrashHandler)(void);
|
||||
|
||||
// Sets the capture file path template
|
||||
//
|
||||
// pathtemplate is a UTF-8 string that gives a template for how captures will be named
|
||||
// and where they will be saved.
|
||||
//
|
||||
// Any extension is stripped off the path, and captures are saved in the directory
|
||||
// specified, and named with the filename and the frame number appended. If the
|
||||
// directory does not exist it will be created, including any parent directories.
|
||||
//
|
||||
// If pathtemplate is NULL, the template will remain unchanged
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// SetCaptureFilePathTemplate("my_captures/example");
|
||||
//
|
||||
// Capture #1 -> my_captures/example_frame123.rdc
|
||||
// Capture #2 -> my_captures/example_frame456.rdc
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureFilePathTemplate)(const char *pathtemplate);
|
||||
|
||||
// returns the current capture path template, see SetCaptureFileTemplate above, as a UTF-8 string
|
||||
typedef const char *(RENDERDOC_CC *pRENDERDOC_GetCaptureFilePathTemplate)(void);
|
||||
|
||||
// DEPRECATED: compatibility for code compiled against pre-1.1.2 headers.
|
||||
typedef pRENDERDOC_SetCaptureFilePathTemplate pRENDERDOC_SetLogFilePathTemplate;
|
||||
typedef pRENDERDOC_GetCaptureFilePathTemplate pRENDERDOC_GetLogFilePathTemplate;
|
||||
|
||||
// returns the number of captures that have been made
|
||||
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetNumCaptures)(void);
|
||||
|
||||
// This function returns the details of a capture, by index. New captures are added
|
||||
// to the end of the list.
|
||||
//
|
||||
// filename will be filled with the absolute path to the capture file, as a UTF-8 string
|
||||
// pathlength will be written with the length in bytes of the filename string
|
||||
// timestamp will be written with the time of the capture, in seconds since the Unix epoch
|
||||
//
|
||||
// Any of the parameters can be NULL and they'll be skipped.
|
||||
//
|
||||
// The function will return 1 if the capture index is valid, or 0 if the index is invalid
|
||||
// If the index is invalid, the values will be unchanged
|
||||
//
|
||||
// Note: when captures are deleted in the UI they will remain in this list, so the
|
||||
// capture path may not exist anymore.
|
||||
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetCapture)(uint32_t idx, char *filename,
|
||||
uint32_t *pathlength, uint64_t *timestamp);
|
||||
|
||||
// Sets the comments associated with a capture file. These comments are displayed in the
|
||||
// UI program when opening.
|
||||
//
|
||||
// filePath should be a path to the capture file to add comments to. If set to NULL or ""
|
||||
// the most recent capture file created made will be used instead.
|
||||
// comments should be a NULL-terminated UTF-8 string to add as comments.
|
||||
//
|
||||
// Any existing comments will be overwritten.
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureFileComments)(const char *filePath,
|
||||
const char *comments);
|
||||
|
||||
// returns 1 if the RenderDoc UI is connected to this application, 0 otherwise
|
||||
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_IsTargetControlConnected)(void);
|
||||
|
||||
// DEPRECATED: compatibility for code compiled against pre-1.1.1 headers.
|
||||
// This was renamed to IsTargetControlConnected in API 1.1.1, the old typedef is kept here for
|
||||
// backwards compatibility with old code, it is castable either way since it's ABI compatible
|
||||
// as the same function pointer type.
|
||||
typedef pRENDERDOC_IsTargetControlConnected pRENDERDOC_IsRemoteAccessConnected;
|
||||
|
||||
// This function will launch the Replay UI associated with the RenderDoc library injected
|
||||
// into the running application.
|
||||
//
|
||||
// if connectTargetControl is 1, the Replay UI will be launched with a command line parameter
|
||||
// to connect to this application
|
||||
// cmdline is the rest of the command line, as a UTF-8 string. E.g. a captures to open
|
||||
// if cmdline is NULL, the command line will be empty.
|
||||
//
|
||||
// returns the PID of the replay UI if successful, 0 if not successful.
|
||||
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_LaunchReplayUI)(uint32_t connectTargetControl,
|
||||
const char *cmdline);
|
||||
|
||||
// RenderDoc can return a higher version than requested if it's backwards compatible,
|
||||
// this function returns the actual version returned. If a parameter is NULL, it will be
|
||||
// ignored and the others will be filled out.
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_GetAPIVersion)(int *major, int *minor, int *patch);
|
||||
|
||||
// Requests that the replay UI show itself (if hidden or not the current top window). This can be
|
||||
// used in conjunction with IsTargetControlConnected and LaunchReplayUI to intelligently handle
|
||||
// showing the UI after making a capture.
|
||||
//
|
||||
// This will return 1 if the request was successfully passed on, though it's not guaranteed that
|
||||
// the UI will be on top in all cases depending on OS rules. It will return 0 if there is no current
|
||||
// target control connection to make such a request, or if there was another error
|
||||
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_ShowReplayUI)(void);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Capturing functions
|
||||
//
|
||||
|
||||
// A device pointer is a pointer to the API's root handle.
|
||||
//
|
||||
// This would be an ID3D11Device, HGLRC/GLXContext, ID3D12Device, etc
|
||||
typedef void *RENDERDOC_DevicePointer;
|
||||
|
||||
// A window handle is the OS's native window handle
|
||||
//
|
||||
// This would be an HWND, GLXDrawable, etc
|
||||
typedef void *RENDERDOC_WindowHandle;
|
||||
|
||||
// A helper macro for Vulkan, where the device handle cannot be used directly.
|
||||
//
|
||||
// Passing the VkInstance to this macro will return the RENDERDOC_DevicePointer to use.
|
||||
//
|
||||
// Specifically, the value needed is the dispatch table pointer, which sits as the first
|
||||
// pointer-sized object in the memory pointed to by the VkInstance. Thus we cast to a void** and
|
||||
// indirect once.
|
||||
#define RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(inst) (*((void **)(inst)))
|
||||
|
||||
// This sets the RenderDoc in-app overlay in the API/window pair as 'active' and it will
|
||||
// respond to keypresses. Neither parameter can be NULL
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_SetActiveWindow)(RENDERDOC_DevicePointer device,
|
||||
RENDERDOC_WindowHandle wndHandle);
|
||||
|
||||
// capture the next frame on whichever window and API is currently considered active
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_TriggerCapture)(void);
|
||||
|
||||
// capture the next N frames on whichever window and API is currently considered active
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_TriggerMultiFrameCapture)(uint32_t numFrames);
|
||||
|
||||
// When choosing either a device pointer or a window handle to capture, you can pass NULL.
|
||||
// Passing NULL specifies a 'wildcard' match against anything. This allows you to specify
|
||||
// any API rendering to a specific window, or a specific API instance rendering to any window,
|
||||
// or in the simplest case of one window and one API, you can just pass NULL for both.
|
||||
//
|
||||
// In either case, if there are two or more possible matching (device,window) pairs it
|
||||
// is undefined which one will be captured.
|
||||
//
|
||||
// Note: for headless rendering you can pass NULL for the window handle and either specify
|
||||
// a device pointer or leave it NULL as above.
|
||||
|
||||
// Immediately starts capturing API calls on the specified device pointer and window handle.
|
||||
//
|
||||
// If there is no matching thing to capture (e.g. no supported API has been initialised),
|
||||
// this will do nothing.
|
||||
//
|
||||
// The results are undefined (including crashes) if two captures are started overlapping,
|
||||
// even on separate devices and/oror windows.
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_StartFrameCapture)(RENDERDOC_DevicePointer device,
|
||||
RENDERDOC_WindowHandle wndHandle);
|
||||
|
||||
// Returns whether or not a frame capture is currently ongoing anywhere.
|
||||
//
|
||||
// This will return 1 if a capture is ongoing, and 0 if there is no capture running
|
||||
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_IsFrameCapturing)(void);
|
||||
|
||||
// Ends capturing immediately.
|
||||
//
|
||||
// This will return 1 if the capture succeeded, and 0 if there was an error capturing.
|
||||
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_EndFrameCapture)(RENDERDOC_DevicePointer device,
|
||||
RENDERDOC_WindowHandle wndHandle);
|
||||
|
||||
// Ends capturing immediately and discard any data stored without saving to disk.
|
||||
//
|
||||
// This will return 1 if the capture was discarded, and 0 if there was an error or no capture
|
||||
// was in progress
|
||||
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_DiscardFrameCapture)(RENDERDOC_DevicePointer device,
|
||||
RENDERDOC_WindowHandle wndHandle);
|
||||
|
||||
// Only valid to be called between a call to StartFrameCapture and EndFrameCapture. Gives a custom
|
||||
// title to the capture produced which will be displayed in the UI.
|
||||
//
|
||||
// If multiple captures are ongoing, this title will be applied to the first capture to end after
|
||||
// this call. The second capture to end will have no title, unless this function is called again.
|
||||
//
|
||||
// Calling this function has no effect if no capture is currently running, and if it is called
|
||||
// multiple times only the last title will be used.
|
||||
typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureTitle)(const char *title);
|
||||
|
||||
// Annotations API:
|
||||
//
|
||||
// These functions allow you to specify annotations either on a per-command level, or a per-object
|
||||
// level.
|
||||
//
|
||||
// Basic types of annotations are supported, as well as vector versions and references to API objects.
|
||||
//
|
||||
// The annotations are stored as keys, with the key being a dot-separated path allowing arbitrary
|
||||
// nesting and user organisation. The keys are sorted in human order so `foo.2.bar` will be displayed
|
||||
// before `foo.10.bar` to allow creation of arrays if desired.
|
||||
//
|
||||
// Deleting an annotation can be done by assigning an empty value to it.
|
||||
|
||||
// the type of an annotation value, or Empty to delete an annotation
|
||||
typedef enum RENDERDOC_AnnotationType
|
||||
{
|
||||
eRENDERDOC_Empty,
|
||||
eRENDERDOC_Bool,
|
||||
eRENDERDOC_Int32,
|
||||
eRENDERDOC_UInt32,
|
||||
eRENDERDOC_Int64,
|
||||
eRENDERDOC_UInt64,
|
||||
eRENDERDOC_Float,
|
||||
eRENDERDOC_Double,
|
||||
eRENDERDOC_String,
|
||||
eRENDERDOC_APIObject,
|
||||
eRENDERDOC_AnnotationMax = 0x7FFFFFFF,
|
||||
} RENDERDOC_AnnotationType;
|
||||
|
||||
// a union with vector annotation value data
|
||||
typedef union RENDERDOC_AnnotationVectorValue
|
||||
{
|
||||
bool boolean[4];
|
||||
int32_t int32[4];
|
||||
int64_t int64[4];
|
||||
uint32_t uint32[4];
|
||||
uint64_t uint64[4];
|
||||
float float32[4];
|
||||
double float64[4];
|
||||
} RENDERDOC_AnnotationVectorValue;
|
||||
|
||||
// a union with scalar annotation value data
|
||||
typedef union RENDERDOC_AnnotationValue
|
||||
{
|
||||
bool boolean;
|
||||
int32_t int32;
|
||||
int64_t int64;
|
||||
uint32_t uint32;
|
||||
uint64_t uint64;
|
||||
float float32;
|
||||
double float64;
|
||||
|
||||
RENDERDOC_AnnotationVectorValue vector;
|
||||
|
||||
const char *string;
|
||||
void *apiObject;
|
||||
} RENDERDOC_AnnotationValue;
|
||||
|
||||
// a struct for specifying a GL object, as we don't have pointers we can use so instead we specify a
|
||||
// pointer to this struct giving both the type and the name
|
||||
typedef struct RENDERDOC_GLResourceReference
|
||||
{
|
||||
// this is the same GLenum identifier as passed to glObjectLabel
|
||||
uint32_t identifier;
|
||||
uint32_t name;
|
||||
} GLResourceReference;
|
||||
|
||||
// simple C++ helpers to avoid the need for a temporary objects for value passing and GL object specification
|
||||
#ifdef __cplusplus
|
||||
struct RDGLObjectHelper
|
||||
{
|
||||
RENDERDOC_GLResourceReference gl;
|
||||
|
||||
RDGLObjectHelper(uint32_t identifier, uint32_t name)
|
||||
{
|
||||
gl.identifier = identifier;
|
||||
gl.name = name;
|
||||
}
|
||||
|
||||
operator RENDERDOC_GLResourceReference *() { return ≷ }
|
||||
};
|
||||
|
||||
struct RDAnnotationHelper
|
||||
{
|
||||
RENDERDOC_AnnotationValue val;
|
||||
|
||||
RDAnnotationHelper(bool b) { val.boolean = b; }
|
||||
RDAnnotationHelper(int32_t i) { val.int32 = i; }
|
||||
RDAnnotationHelper(int64_t i) { val.int64 = i; }
|
||||
RDAnnotationHelper(uint32_t i) { val.uint32 = i; }
|
||||
RDAnnotationHelper(uint64_t i) { val.uint64 = i; }
|
||||
RDAnnotationHelper(float f) { val.float32 = f; }
|
||||
RDAnnotationHelper(double d) { val.float64 = d; }
|
||||
RDAnnotationHelper(const char *s) { val.string = s; }
|
||||
|
||||
operator RENDERDOC_AnnotationValue *() { return &val; }
|
||||
};
|
||||
#endif
|
||||
|
||||
// The device is specified in the same way as other API calls that take a RENDERDOC_DevicePointer
|
||||
// to specify the device.
|
||||
//
|
||||
// The object or queue/commandbuffer will depend on the graphics API in question.
|
||||
//
|
||||
// Return value:
|
||||
// 0 - The annotation was applied successfully.
|
||||
// 1 - The device is unknown/invalid
|
||||
// 2 - The device is valid but the annotation is not supported for API-specific reasons, such as an
|
||||
// unrecognised or invalid object or queue/commandbuffer
|
||||
// 3 - The call is ill-formed or invalid e.g. empty is specified with a value pointer, or non-empty
|
||||
// is specified with a NULL value pointer
|
||||
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_SetObjectAnnotation)(RENDERDOC_DevicePointer device,
|
||||
void *object, const char *key,
|
||||
RENDERDOC_AnnotationType valueType,
|
||||
uint32_t valueVectorWidth,
|
||||
const RENDERDOC_AnnotationValue *value);
|
||||
|
||||
typedef uint32_t(RENDERDOC_CC *pRENDERDOC_SetCommandAnnotation)(
|
||||
RENDERDOC_DevicePointer device, void *queueOrCommandBuffer, const char *key,
|
||||
RENDERDOC_AnnotationType valueType, uint32_t valueVectorWidth,
|
||||
const RENDERDOC_AnnotationValue *value);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// RenderDoc API versions
|
||||
//
|
||||
|
||||
// RenderDoc uses semantic versioning (http://semver.org/).
|
||||
//
|
||||
// MAJOR version is incremented when incompatible API changes happen.
|
||||
// MINOR version is incremented when functionality is added in a backwards-compatible manner.
|
||||
// PATCH version is incremented when backwards-compatible bug fixes happen.
|
||||
//
|
||||
// Note that this means the API returned can be higher than the one you might have requested.
|
||||
// e.g. if you are running against a newer RenderDoc that supports 1.0.1, it will be returned
|
||||
// instead of 1.0.0. You can check this with the GetAPIVersion entry point
|
||||
typedef enum RENDERDOC_Version
|
||||
{
|
||||
eRENDERDOC_API_Version_1_0_0 = 10000, // RENDERDOC_API_1_0_0 = 1 00 00
|
||||
eRENDERDOC_API_Version_1_0_1 = 10001, // RENDERDOC_API_1_0_1 = 1 00 01
|
||||
eRENDERDOC_API_Version_1_0_2 = 10002, // RENDERDOC_API_1_0_2 = 1 00 02
|
||||
eRENDERDOC_API_Version_1_1_0 = 10100, // RENDERDOC_API_1_1_0 = 1 01 00
|
||||
eRENDERDOC_API_Version_1_1_1 = 10101, // RENDERDOC_API_1_1_1 = 1 01 01
|
||||
eRENDERDOC_API_Version_1_1_2 = 10102, // RENDERDOC_API_1_1_2 = 1 01 02
|
||||
eRENDERDOC_API_Version_1_2_0 = 10200, // RENDERDOC_API_1_2_0 = 1 02 00
|
||||
eRENDERDOC_API_Version_1_3_0 = 10300, // RENDERDOC_API_1_3_0 = 1 03 00
|
||||
eRENDERDOC_API_Version_1_4_0 = 10400, // RENDERDOC_API_1_4_0 = 1 04 00
|
||||
eRENDERDOC_API_Version_1_4_1 = 10401, // RENDERDOC_API_1_4_1 = 1 04 01
|
||||
eRENDERDOC_API_Version_1_4_2 = 10402, // RENDERDOC_API_1_4_2 = 1 04 02
|
||||
eRENDERDOC_API_Version_1_5_0 = 10500, // RENDERDOC_API_1_5_0 = 1 05 00
|
||||
eRENDERDOC_API_Version_1_6_0 = 10600, // RENDERDOC_API_1_6_0 = 1 06 00
|
||||
eRENDERDOC_API_Version_1_7_0 = 10700, // RENDERDOC_API_1_7_0 = 1 07 00
|
||||
} RENDERDOC_Version;
|
||||
|
||||
// API version changelog:
|
||||
//
|
||||
// 1.0.0 - initial release
|
||||
// 1.0.1 - Bugfix: IsFrameCapturing() was returning false for captures that were triggered
|
||||
// by keypress or TriggerCapture, instead of Start/EndFrameCapture.
|
||||
// 1.0.2 - Refactor: Renamed eRENDERDOC_Option_DebugDeviceMode to eRENDERDOC_Option_APIValidation
|
||||
// 1.1.0 - Add feature: TriggerMultiFrameCapture(). Backwards compatible with 1.0.x since the new
|
||||
// function pointer is added to the end of the struct, the original layout is identical
|
||||
// 1.1.1 - Refactor: Renamed remote access to target control (to better disambiguate from remote
|
||||
// replay/remote server concept in replay UI)
|
||||
// 1.1.2 - Refactor: Renamed "log file" in function names to just capture, to clarify that these
|
||||
// are captures and not debug logging files. This is the first API version in the v1.0
|
||||
// branch.
|
||||
// 1.2.0 - Added feature: SetCaptureFileComments() to add comments to a capture file that will be
|
||||
// displayed in the UI program on load.
|
||||
// 1.3.0 - Added feature: New capture option eRENDERDOC_Option_AllowUnsupportedVendorExtensions
|
||||
// which allows users to opt-in to allowing unsupported vendor extensions to function.
|
||||
// Should be used at the user's own risk.
|
||||
// Refactor: Renamed eRENDERDOC_Option_VerifyMapWrites to
|
||||
// eRENDERDOC_Option_VerifyBufferAccess, which now also controls initialisation to
|
||||
// 0xdddddddd of uninitialised buffer contents.
|
||||
// 1.4.0 - Added feature: DiscardFrameCapture() to discard a frame capture in progress and stop
|
||||
// capturing without saving anything to disk.
|
||||
// 1.4.1 - Refactor: Renamed Shutdown to RemoveHooks to better clarify what is happening
|
||||
// 1.4.2 - Refactor: Renamed 'draws' to 'actions' in callstack capture option.
|
||||
// 1.5.0 - Added feature: ShowReplayUI() to request that the replay UI show itself if connected
|
||||
// 1.6.0 - Added feature: SetCaptureTitle() which can be used to set a title for a
|
||||
// capture made with StartFrameCapture() or EndFrameCapture()
|
||||
// 1.7.0 - Added feature: SetObjectAnnotation() / SetCommandAnnotation() for adding rich
|
||||
// annotations to objects and command streams
|
||||
|
||||
typedef struct RENDERDOC_API_1_7_0
|
||||
{
|
||||
pRENDERDOC_GetAPIVersion GetAPIVersion;
|
||||
|
||||
pRENDERDOC_SetCaptureOptionU32 SetCaptureOptionU32;
|
||||
pRENDERDOC_SetCaptureOptionF32 SetCaptureOptionF32;
|
||||
|
||||
pRENDERDOC_GetCaptureOptionU32 GetCaptureOptionU32;
|
||||
pRENDERDOC_GetCaptureOptionF32 GetCaptureOptionF32;
|
||||
|
||||
pRENDERDOC_SetFocusToggleKeys SetFocusToggleKeys;
|
||||
pRENDERDOC_SetCaptureKeys SetCaptureKeys;
|
||||
|
||||
pRENDERDOC_GetOverlayBits GetOverlayBits;
|
||||
pRENDERDOC_MaskOverlayBits MaskOverlayBits;
|
||||
|
||||
// Shutdown was renamed to RemoveHooks in 1.4.1.
|
||||
// These unions allow old code to continue compiling without changes
|
||||
union
|
||||
{
|
||||
pRENDERDOC_Shutdown Shutdown;
|
||||
pRENDERDOC_RemoveHooks RemoveHooks;
|
||||
};
|
||||
pRENDERDOC_UnloadCrashHandler UnloadCrashHandler;
|
||||
|
||||
// Get/SetLogFilePathTemplate was renamed to Get/SetCaptureFilePathTemplate in 1.1.2.
|
||||
// These unions allow old code to continue compiling without changes
|
||||
union
|
||||
{
|
||||
// deprecated name
|
||||
pRENDERDOC_SetLogFilePathTemplate SetLogFilePathTemplate;
|
||||
// current name
|
||||
pRENDERDOC_SetCaptureFilePathTemplate SetCaptureFilePathTemplate;
|
||||
};
|
||||
union
|
||||
{
|
||||
// deprecated name
|
||||
pRENDERDOC_GetLogFilePathTemplate GetLogFilePathTemplate;
|
||||
// current name
|
||||
pRENDERDOC_GetCaptureFilePathTemplate GetCaptureFilePathTemplate;
|
||||
};
|
||||
|
||||
pRENDERDOC_GetNumCaptures GetNumCaptures;
|
||||
pRENDERDOC_GetCapture GetCapture;
|
||||
|
||||
pRENDERDOC_TriggerCapture TriggerCapture;
|
||||
|
||||
// IsRemoteAccessConnected was renamed to IsTargetControlConnected in 1.1.1.
|
||||
// This union allows old code to continue compiling without changes
|
||||
union
|
||||
{
|
||||
// deprecated name
|
||||
pRENDERDOC_IsRemoteAccessConnected IsRemoteAccessConnected;
|
||||
// current name
|
||||
pRENDERDOC_IsTargetControlConnected IsTargetControlConnected;
|
||||
};
|
||||
pRENDERDOC_LaunchReplayUI LaunchReplayUI;
|
||||
|
||||
pRENDERDOC_SetActiveWindow SetActiveWindow;
|
||||
|
||||
pRENDERDOC_StartFrameCapture StartFrameCapture;
|
||||
pRENDERDOC_IsFrameCapturing IsFrameCapturing;
|
||||
pRENDERDOC_EndFrameCapture EndFrameCapture;
|
||||
|
||||
// new function in 1.1.0
|
||||
pRENDERDOC_TriggerMultiFrameCapture TriggerMultiFrameCapture;
|
||||
|
||||
// new function in 1.2.0
|
||||
pRENDERDOC_SetCaptureFileComments SetCaptureFileComments;
|
||||
|
||||
// new function in 1.4.0
|
||||
pRENDERDOC_DiscardFrameCapture DiscardFrameCapture;
|
||||
|
||||
// new function in 1.5.0
|
||||
pRENDERDOC_ShowReplayUI ShowReplayUI;
|
||||
|
||||
// new function in 1.6.0
|
||||
pRENDERDOC_SetCaptureTitle SetCaptureTitle;
|
||||
|
||||
// new functions in 1.7.0
|
||||
pRENDERDOC_SetObjectAnnotation SetObjectAnnotation;
|
||||
pRENDERDOC_SetCommandAnnotation SetCommandAnnotation;
|
||||
} RENDERDOC_API_1_7_0;
|
||||
|
||||
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_0_0;
|
||||
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_0_1;
|
||||
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_0_2;
|
||||
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_1_0;
|
||||
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_1_1;
|
||||
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_1_2;
|
||||
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_2_0;
|
||||
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_3_0;
|
||||
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_4_0;
|
||||
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_4_1;
|
||||
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_4_2;
|
||||
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_5_0;
|
||||
typedef RENDERDOC_API_1_7_0 RENDERDOC_API_1_6_0;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// RenderDoc API entry point
|
||||
//
|
||||
// This entry point can be obtained via GetProcAddress/dlsym if RenderDoc is available.
|
||||
//
|
||||
// The name is the same as the typedef - "RENDERDOC_GetAPI"
|
||||
//
|
||||
// This function is not thread safe, and should not be called on multiple threads at once.
|
||||
// Ideally, call this once as early as possible in your application's startup, before doing
|
||||
// any API work, since some configuration functionality etc has to be done also before
|
||||
// initialising any APIs.
|
||||
//
|
||||
// Parameters:
|
||||
// version is a single value from the RENDERDOC_Version above.
|
||||
//
|
||||
// outAPIPointers will be filled out with a pointer to the corresponding struct of function
|
||||
// pointers.
|
||||
//
|
||||
// Returns:
|
||||
// 1 - if the outAPIPointers has been filled with a pointer to the API struct requested
|
||||
// 0 - if the requested version is not supported or the arguments are invalid.
|
||||
//
|
||||
typedef int(RENDERDOC_CC *pRENDERDOC_GetAPI)(RENDERDOC_Version version, void **outAPIPointers);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
Vendored
+1
-1
Submodule 3rdparty/tracy updated: 05cceee0df...30997d5ca6.
+79
-30
@@ -126,7 +126,7 @@ if (KYTY_LINKER STREQUAL LD AND NOT LINUX)
|
||||
set(KYTY_LD_OPTIONS "-Wl,--image-base=0x100000000000")
|
||||
endif()
|
||||
|
||||
project(Kyty${KYTY_PROJECT_NAME}${CMAKE_BUILD_TYPE}${KYTY_COMPILER} VERSION 0.2.2)
|
||||
project(Kyty${KYTY_PROJECT_NAME}${CMAKE_BUILD_TYPE}${KYTY_COMPILER} VERSION 0.3.0)
|
||||
|
||||
include_directories(
|
||||
${KYTY_THIRD_PARTY_DIR}/gtest/include
|
||||
@@ -203,14 +203,21 @@ file(GLOB kyty_emulator_src CONFIGURE_DEPENDS
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/*.h"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/*.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/*.h"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/cfg/*.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/cfg/*.h"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/decompiler/*.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/decompiler/*.h"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/emitter/*.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/emitter/*.h"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/frontend/cfg/*.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/frontend/cfg/*.h"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/frontend/decode/*.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/frontend/decode/*.h"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/frontend/translate/*.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/frontend/translate/*.h"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/backend/spirv/*.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/backend/spirv/*.h"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/*.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/*.h"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/opcodes/*.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/opcodes/*.h"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/opcodes/*.inc"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/*.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/*.h"
|
||||
"${KYTY_SOURCE_DIR}/graphics/presentation/*.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/presentation/*.h"
|
||||
"${KYTY_SOURCE_DIR}/graphics/presentation/window/*.cpp"
|
||||
@@ -247,7 +254,7 @@ foreach(shader_name IN LISTS gpu_tiler_shader_names)
|
||||
add_custom_command(
|
||||
OUTPUT "${shader_header}"
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${gpu_tiler_generated_dir}"
|
||||
COMMAND "${KYTY_GLSLANG_VALIDATOR}" -V --target-env vulkan1.0 -Os
|
||||
COMMAND "${KYTY_GLSLANG_VALIDATOR}" -V --target-env vulkan1.3 -Os
|
||||
"-I${gpu_tiler_shader_dir}" -o "${shader_spv}" "${shader_source}"
|
||||
COMMAND ${CMAKE_COMMAND} -DINPUT=${shader_spv} -DOUTPUT=${shader_header}
|
||||
-DSYMBOL=${shader_symbol} -P "${KYTY_SOURCE_DIR}/embed_spirv.cmake"
|
||||
@@ -256,6 +263,22 @@ foreach(shader_name IN LISTS gpu_tiler_shader_names)
|
||||
)
|
||||
list(APPEND gpu_tiler_shader_headers "${shader_header}")
|
||||
endforeach()
|
||||
|
||||
set(fault_buffer_shader_source "${gpu_tiler_shader_dir}/fault_buffer_process.comp")
|
||||
set(fault_buffer_shader_spv "${gpu_tiler_generated_dir}/fault_buffer_process.spv")
|
||||
set(fault_buffer_shader_header "${gpu_tiler_generated_dir}/fault_buffer_process_spv.h")
|
||||
add_custom_command(
|
||||
OUTPUT "${fault_buffer_shader_header}"
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${gpu_tiler_generated_dir}"
|
||||
COMMAND "${KYTY_GLSLANG_VALIDATOR}" -V --target-env vulkan1.3 -Os
|
||||
-DCACHING_PAGEBITS=14 -DMAX_PAGE_FAULTS=1024
|
||||
-o "${fault_buffer_shader_spv}" "${fault_buffer_shader_source}"
|
||||
COMMAND ${CMAKE_COMMAND} -DINPUT=${fault_buffer_shader_spv} -DOUTPUT=${fault_buffer_shader_header}
|
||||
-DSYMBOL=FAULT_BUFFER_PROCESS_SPV -P "${KYTY_SOURCE_DIR}/embed_spirv.cmake"
|
||||
DEPENDS "${fault_buffer_shader_source}"
|
||||
VERBATIM
|
||||
)
|
||||
list(APPEND kyty_emulator_src "${fault_buffer_shader_header}")
|
||||
list(APPEND kyty_emulator_src ${gpu_tiler_shader_headers})
|
||||
|
||||
set(gpu_blit_generated_dir "${PROJECT_BINARY_DIR}/gpu_blit_shaders")
|
||||
@@ -271,7 +294,7 @@ foreach(shader_source IN LISTS gpu_blit_shader_sources)
|
||||
add_custom_command(
|
||||
OUTPUT "${shader_header}"
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${gpu_blit_generated_dir}"
|
||||
COMMAND "${KYTY_GLSLANG_VALIDATOR}" -V --target-env vulkan1.0 -Os
|
||||
COMMAND "${KYTY_GLSLANG_VALIDATOR}" -V --target-env vulkan1.3 -Os
|
||||
"-I${gpu_tiler_shader_dir}" -o "${shader_spv}" "${shader_source}"
|
||||
COMMAND ${CMAKE_COMMAND} -DINPUT=${shader_spv} -DOUTPUT=${shader_header}
|
||||
-DSYMBOL=${shader_symbol} -P "${KYTY_SOURCE_DIR}/embed_spirv.cmake"
|
||||
@@ -319,9 +342,11 @@ set(inc_headers
|
||||
${KYTY_THIRD_PARTY_DIR}/SDL2/include
|
||||
${KYTY_THIRD_PARTY_DIR}/VulkanMemoryAllocator/include
|
||||
${KYTY_THIRD_PARTY_DIR}/SPIRV-Tools/include
|
||||
${KYTY_THIRD_PARTY_DIR}/SPIRV-Headers/include
|
||||
${KYTY_THIRD_PARTY_DIR}/fmt/include
|
||||
${KYTY_THIRD_PARTY_DIR}/xxhash
|
||||
${KYTY_THIRD_PARTY_DIR}/cpuinfo/include
|
||||
${KYTY_THIRD_PARTY_DIR}/renderdoc
|
||||
${KYTY_THIRD_PARTY_DIR}/stb
|
||||
)
|
||||
|
||||
@@ -367,9 +392,8 @@ add_kyty_full_emulator_test(shader_cfg_tests "${KYTY_TESTS_DIR}/shaderCfgTests.c
|
||||
add_executable(scalar_provenance_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_TESTS_DIR}/ScalarProvenanceTests.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/host_gpu/hostMemory.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/ReadLaneElimination.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/ScalarProvenance.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/SrtWalker.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/ReadLaneElimination.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/SrtWalker.cpp"
|
||||
)
|
||||
target_link_libraries(scalar_provenance_tests fmt::fmt)
|
||||
target_include_directories(scalar_provenance_tests PRIVATE ${inc_headers})
|
||||
@@ -378,6 +402,7 @@ add_executable(page_manager_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_TESTS_DIR}/PageManagerTests.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/host_gpu/pageManager.cpp"
|
||||
)
|
||||
target_link_libraries(page_manager_tests common)
|
||||
target_include_directories(page_manager_tests PRIVATE ${inc_headers})
|
||||
|
||||
add_executable(bit_array_tests EXCLUDE_FROM_ALL
|
||||
@@ -387,6 +412,7 @@ target_include_directories(bit_array_tests PRIVATE ${inc_headers})
|
||||
|
||||
add_executable(ime_dialog_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_TESTS_DIR}/ImeDialogTests.cpp"
|
||||
"${KYTY_SOURCE_DIR}/libs/imeCommon.cpp"
|
||||
"${KYTY_SOURCE_DIR}/libs/imeDialog.cpp"
|
||||
)
|
||||
target_include_directories(ime_dialog_tests PRIVATE ${inc_headers})
|
||||
@@ -406,29 +432,26 @@ add_executable(shader_vertex_metadata_tests EXCLUDE_FROM_ALL
|
||||
)
|
||||
target_include_directories(shader_vertex_metadata_tests PRIVATE ${inc_headers})
|
||||
|
||||
add_executable(shader_stage_runtime_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_TESTS_DIR}/ShaderStageRuntimeTests.cpp"
|
||||
add_executable(resource_materialization_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_TESTS_DIR}/ResourceMaterializationTests.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/guest_gpu/gpu_format.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/host_gpu/hostMemory.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/shaderStageRuntime.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/ResourceMaterialization.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/ScalarProvenance.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/SrtWalker.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/SrtWalker.cpp"
|
||||
)
|
||||
target_link_libraries(shader_stage_runtime_tests fmt::fmt)
|
||||
target_include_directories(shader_stage_runtime_tests PRIVATE ${inc_headers})
|
||||
target_link_libraries(resource_materialization_tests fmt::fmt)
|
||||
target_include_directories(resource_materialization_tests PRIVATE ${inc_headers})
|
||||
|
||||
add_executable(resource_tracking_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_TESTS_DIR}/ResourceTrackingTests.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/guest_gpu/gpu_format.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/host_gpu/hostMemory.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/ScalarProvenance.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/SrtWalker.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/SrtPatcher.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/ResourceTracking.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/ResourceMaterialization.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/ShaderInfoCollection.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/BindingLayout.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/shaderPixelParameter.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/SrtWalker.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/ShaderInfoCollection.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/BindingLayout.cpp"
|
||||
)
|
||||
target_link_libraries(resource_tracking_tests fmt::fmt)
|
||||
target_include_directories(resource_tracking_tests PRIVATE ${inc_headers})
|
||||
@@ -449,6 +472,9 @@ add_executable(event_queue_lifetime_tests EXCLUDE_FROM_ALL
|
||||
target_link_libraries(event_queue_lifetime_tests common fmt::fmt)
|
||||
target_include_directories(event_queue_lifetime_tests PRIVATE ${inc_headers})
|
||||
|
||||
add_kyty_full_emulator_test(kernel_file_system_tests
|
||||
"${KYTY_TESTS_DIR}/KernelFileSystemTests.cpp")
|
||||
|
||||
add_executable(sync_on_address_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_TESTS_DIR}/SyncOnAddressTests.cpp"
|
||||
"${KYTY_SOURCE_DIR}/kernel/syncOnAddress.cpp"
|
||||
@@ -474,7 +500,7 @@ set(gpu_test_ms_depth_header
|
||||
add_custom_command(
|
||||
OUTPUT "${gpu_test_ms_depth_header}"
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${gpu_test_generated_dir}"
|
||||
COMMAND "${KYTY_GLSLANG_VALIDATOR}" -V --target-env vulkan1.0 -Os
|
||||
COMMAND "${KYTY_GLSLANG_VALIDATOR}" -V --target-env vulkan1.3 -Os
|
||||
-o "${gpu_test_ms_depth_spv}" "${gpu_test_ms_depth_source}"
|
||||
COMMAND ${CMAKE_COMMAND} -DINPUT=${gpu_test_ms_depth_spv}
|
||||
-DOUTPUT=${gpu_test_ms_depth_header} -DSYMBOL=GPU_TEST_MS_DEPTH_SPV
|
||||
@@ -498,6 +524,16 @@ if(NOT KYTY_CLANG_CL)
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_test(NAME emulator_user_name_cli
|
||||
COMMAND $<TARGET_FILE:kyty_emulator> --help --user-name 1234567890123456)
|
||||
add_test(NAME emulator_user_name_too_long
|
||||
COMMAND $<TARGET_FILE:kyty_emulator> --help --user-name 12345678901234567)
|
||||
set_tests_properties(emulator_user_name_too_long PROPERTIES WILL_FAIL TRUE)
|
||||
add_test(NAME emulator_present_mode_cli
|
||||
COMMAND $<TARGET_FILE:kyty_emulator> --help --present-mode Immediate)
|
||||
add_test(NAME emulator_present_mode_invalid
|
||||
COMMAND $<TARGET_FILE:kyty_emulator> --help --present-mode Invalid)
|
||||
set_tests_properties(emulator_present_mode_invalid PROPERTIES WILL_FAIL TRUE)
|
||||
add_test(NAME ime_dialog COMMAND $<TARGET_FILE:ime_dialog_tests>)
|
||||
add_test(NAME shader_cfg COMMAND $<TARGET_FILE:shader_cfg_tests>)
|
||||
add_test(NAME scalar_provenance COMMAND $<TARGET_FILE:scalar_provenance_tests>)
|
||||
@@ -506,12 +542,19 @@ if(BUILD_TESTING)
|
||||
add_test(NAME page_manager COMMAND $<TARGET_FILE:page_manager_tests>)
|
||||
add_test(NAME bit_array COMMAND $<TARGET_FILE:bit_array_tests>)
|
||||
add_test(NAME shader_vertex_metadata COMMAND $<TARGET_FILE:shader_vertex_metadata_tests>)
|
||||
add_test(NAME shader_stage_runtime COMMAND $<TARGET_FILE:shader_stage_runtime_tests>)
|
||||
add_test(NAME resource_materialization COMMAND $<TARGET_FILE:resource_materialization_tests>)
|
||||
add_test(NAME resource_tracking COMMAND $<TARGET_FILE:resource_tracking_tests>)
|
||||
add_test(NAME event_queue_lifetime COMMAND $<TARGET_FILE:event_queue_lifetime_tests>)
|
||||
add_test(NAME kernel_file_system COMMAND $<TARGET_FILE:kernel_file_system_tests>)
|
||||
add_test(NAME sync_on_address COMMAND $<TARGET_FILE:sync_on_address_tests>)
|
||||
add_test(NAME audio_out2_port COMMAND $<TARGET_FILE:audio_out2_port_tests>)
|
||||
add_test(NAME shader_recompiler_compute COMMAND $<TARGET_FILE:shader_recompiler_compute_tests>)
|
||||
add_test(NAME shader_recompiler_alignbyte
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --alignbyte-only)
|
||||
add_test(NAME graphics_pipeline_rasterization
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --polygon-mode-only)
|
||||
add_test(NAME graphics_draw_offsets
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --draw-offset-only)
|
||||
add_test(NAME virtual_memory_allocation
|
||||
COMMAND $<TARGET_FILE:virtual_memory_allocation_tests>)
|
||||
add_test(NAME guest_red_zone_patcher
|
||||
@@ -529,12 +572,16 @@ if(BUILD_TESTING)
|
||||
|
||||
add_test(NAME texture_cache_layered_image
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --layered-image-only)
|
||||
add_test(NAME host_image_allocation
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --host-image-allocation-only)
|
||||
add_test(NAME texture_cache_image_views
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --image-view-cache-only)
|
||||
add_test(NAME texture_cache_storage_sampled
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --storage-sampled-only)
|
||||
add_test(NAME texture_cache_depth_readback
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --depth-readback-only)
|
||||
add_test(NAME compute_meta_clear_classification
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --compute-meta-clear-only)
|
||||
add_test(NAME buffer_cache_dirty_gc
|
||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --buffer-cache-gc-only)
|
||||
|
||||
@@ -557,12 +604,14 @@ if(BUILD_TESTING)
|
||||
page_manager_tests
|
||||
bit_array_tests
|
||||
shader_vertex_metadata_tests
|
||||
shader_stage_runtime_tests
|
||||
resource_materialization_tests
|
||||
resource_tracking_tests
|
||||
event_queue_lifetime_tests
|
||||
kernel_file_system_tests
|
||||
sync_on_address_tests
|
||||
shader_recompiler_compute_tests
|
||||
virtual_memory_allocation_tests
|
||||
audio_out2_port_tests
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ graphical glitches, low compatibility, and poor performance.
|
||||
<tr>
|
||||
<td align="center">
|
||||
<strong>Neptunia ReVerse</strong><br>
|
||||
<img src="docs/screenshots/ps5-04.png" width="300" alt="Minecraft Legends running in KytyPS5">
|
||||
<img src="docs/screenshots/ps5-04.png" width="300" alt="Neptunia ReVerse running in KytyPS5">
|
||||
</td>
|
||||
<td align="center">
|
||||
<strong>SILENT HILL: The Short Message</strong><br>
|
||||
@@ -65,11 +65,11 @@ graphical glitches, low compatibility, and poor performance.
|
||||
<tr>
|
||||
<td align="center">
|
||||
<strong>Hellboy</strong><br>
|
||||
<img src="docs/screenshots/ps5-02.png" width="300" alt="Disgaea 6 running in KytyPS5">
|
||||
<img src="docs/screenshots/ps5-02.png" width="300" alt="Hellboy running in KytyPS5">
|
||||
</td>
|
||||
<td align="center">
|
||||
<strong>Paleo Pines</strong><br>
|
||||
<img src="docs/screenshots/ps5-06.png" width="300" alt="Dreaming Sarah running in KytyPS5">
|
||||
<img src="docs/screenshots/ps5-06.png" width="300" alt="Paleo Pines running in KytyPS5">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -90,8 +90,14 @@ starting a large change.
|
||||
|
||||
Set up the clang-format hook after cloning:
|
||||
|
||||
```powershell
|
||||
python -m pip install pre-commit
|
||||
Install `pre-commit` using the method appropriate for your platform:
|
||||
|
||||
- **Arch Linux / CachyOS:** `sudo pacman -S pre-commit`
|
||||
- **Other Linux / macOS / Windows:** `python -m pip install pre-commit`
|
||||
|
||||
Then install the Git hook:
|
||||
|
||||
```bash
|
||||
python -m pre_commit install --install-hooks
|
||||
```
|
||||
|
||||
@@ -220,17 +226,23 @@ cmake --install _Build/macos --prefix _Build/macos/install
|
||||
```
|
||||
|
||||
The build re-signs `kyty_emulator` with the JIT entitlements it needs to execute translated
|
||||
guest code; no manual signing step is required.
|
||||
guest code; no manual signing step is required. When the launcher is built, the install
|
||||
also produces `_Build/macos/install/KytyPS5.app` — double-click to launch the GUI.
|
||||
A flat `kyty_emulator` is kept for CLI usage.
|
||||
|
||||
Vulkan comes from MoltenVK. Download `MoltenVK-macos.tar` from the
|
||||
[MoltenVK releases](https://github.com/KhronosGroup/MoltenVK/releases), then copy
|
||||
`MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib` next to `kyty_emulator` and ad-hoc sign it:
|
||||
`MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib` next to the flat `kyty_emulator`
|
||||
(and, for the bundle, into `KytyPS5.app/Contents/Frameworks/`) and ad-hoc sign it:
|
||||
|
||||
```bash
|
||||
codesign --force --sign - _Build/macos/install/libMoltenVK.dylib
|
||||
# For the bundle (if present):
|
||||
codesign --force --sign - _Build/macos/install/KytyPS5.app/Contents/Frameworks/libMoltenVK.dylib
|
||||
codesign --force --sign - _Build/macos/install/KytyPS5.app
|
||||
```
|
||||
|
||||
Release archives already include a signed `libMoltenVK.dylib`.
|
||||
Release archives already include a signed `libMoltenVK.dylib` (both flat and inside the bundle).
|
||||
|
||||
### Regression tests
|
||||
|
||||
@@ -274,6 +286,10 @@ To use the graphical launcher:
|
||||
./_Build/linux/install/launcher
|
||||
```
|
||||
|
||||
```bash
|
||||
open _Build/macos/install/KytyPS5.app # or double-click in Finder
|
||||
```
|
||||
|
||||
On first launch, add one or more game folders in the global settings. The launcher searches those
|
||||
folders recursively for game directories containing `eboot.bin`. Select a detected game and run it
|
||||
from the game list.
|
||||
@@ -288,12 +304,17 @@ The emulator can also be started directly with a legally obtained game directory
|
||||
./_Build/linux/install/kyty_emulator --game "/games/ExampleGame"
|
||||
```
|
||||
|
||||
On macOS, point SDL at the MoltenVK library explicitly; the hardened runtime prevents it from
|
||||
being picked up from the executable's directory:
|
||||
On macOS, the adjacent flat or app-bundled `libMoltenVK.dylib` is found automatically; no
|
||||
environment variable is required:
|
||||
|
||||
```bash
|
||||
cd _Build/macos/install
|
||||
SDL_VULKAN_LIBRARY="$PWD/libMoltenVK.dylib" ./kyty_emulator --game "/games/ExampleGame"
|
||||
./_Build/macos/install/kyty_emulator --game "/games/ExampleGame"
|
||||
```
|
||||
|
||||
To override the Vulkan loader, set `SDL_VULKAN_LIBRARY`:
|
||||
|
||||
```bash
|
||||
SDL_VULKAN_LIBRARY=/path/to/libMoltenVK.dylib ./kyty_emulator --game "/games/ExampleGame"
|
||||
```
|
||||
|
||||
Run `kyty_emulator --help` to see the available graphics, logging, validation, profiling, and
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 2.2 MiB |
@@ -0,0 +1,25 @@
|
||||
// SPDX-FileCopyrightText: 2014 Jannik Vogel <email@jannikvogel.de>
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr T AlignUp(T value, std::size_t size) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
auto mod{static_cast<T>(value % size)};
|
||||
value -= mod;
|
||||
return static_cast<T>(mod == T{0} ? value : value + size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr T AlignDown(T value, std::size_t size) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
return static_cast<T>(value - value % size);
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
+4
-19
@@ -1,6 +1,5 @@
|
||||
#include "common/assert.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/subsystems.h"
|
||||
#include "kytyGitVersion.h"
|
||||
@@ -12,30 +11,15 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS && KYTY_BUILD == KYTY_BUILD_DEBUG && \
|
||||
KYTY_COMPILER == KYTY_COMPILER_CLANG
|
||||
constexpr int PRINT_STACK_FROM = 4;
|
||||
#else
|
||||
constexpr int PRINT_STACK_FROM = 2;
|
||||
#endif
|
||||
|
||||
static std::string BuildFatalReport(const char* title, std::string_view text, const char* file,
|
||||
int line) {
|
||||
DebugStack stack;
|
||||
DebugStack::Trace(&stack);
|
||||
|
||||
std::string report = "--- Build ---\n" KYTY_BUILD_LABEL "\n--- Stack Trace ---\n";
|
||||
for (int i = PRINT_STACK_FROM; i < stack.depth; i++) {
|
||||
report += fmt::format("[{}] {:016x}\n", i - PRINT_STACK_FROM,
|
||||
static_cast<uint64_t>(stack.GetAddr(i)));
|
||||
}
|
||||
report += fmt::format("{}\n{} in {}:{}\n", title, text, file, line);
|
||||
return report;
|
||||
return fmt::format("--- Build ---\n{}\n{}\n{} in {}:{}\n", KYTY_BUILD_LABEL, title, text, file,
|
||||
line);
|
||||
}
|
||||
|
||||
static int DbgReport(const char* title, std::string_view text, const char* file, int line) {
|
||||
Log::WriteFatal(BuildFatalReport(title, text, file, line));
|
||||
SubsystemsListSingleton::Instance()->ShutdownAll();
|
||||
Subsystems::EmergencyShutdownActive();
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -59,6 +43,7 @@ int DbgExitHandler(const char* file, int line, fmt::text_style style, std::strin
|
||||
}
|
||||
|
||||
void DbgExit(int status) {
|
||||
Subsystems::EmergencyShutdownActive();
|
||||
std::fflush(nullptr);
|
||||
std::_Exit(status);
|
||||
}
|
||||
|
||||
@@ -29,11 +29,7 @@ void DbgExit(int status);
|
||||
|
||||
} // namespace Common
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS || KYTY_PLATFORM == KYTY_PLATFORM_LINUX
|
||||
#define EXIT_HALT() (Common::DbgExit(321), 1)
|
||||
#else
|
||||
#define EXIT_HALT() (std::_Exit(321), 1)
|
||||
#endif
|
||||
|
||||
#ifndef KYTY_FINAL
|
||||
#define EXIT_IF(x) \
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
#include "common/commonSubsystem.h"
|
||||
|
||||
#include "common/byteBuffer.h" // IWYU pragma: associated
|
||||
#include "common/common.h" // IWYU pragma: associated
|
||||
#include "common/debug.h" // IWYU pragma: associated
|
||||
#include "common/hash.h" // IWYU pragma: associated
|
||||
#include "common/magicEnum.h" // IWYU pragma: associated
|
||||
#include "common/singleton.h" // IWYU pragma: associated
|
||||
#include "common/virtualMemory.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
KYTY_SUBSYSTEM_INIT(Common) {
|
||||
VirtualMemory::Init();
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Common) {}
|
||||
|
||||
KYTY_SUBSYSTEM_DESTROY(Common) {}
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,12 +0,0 @@
|
||||
#ifndef KYTY_COMMON_COMMON_SUBSYSTEM_H_
|
||||
#define KYTY_COMMON_COMMON_SUBSYSTEM_H_
|
||||
|
||||
#include "common/subsystems.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
KYTY_SUBSYSTEM_DEFINE(Common);
|
||||
|
||||
} // namespace Common
|
||||
|
||||
#endif /* KYTY_COMMON_COMMON_SUBSYSTEM_H_ */
|
||||
@@ -1,20 +1,7 @@
|
||||
#include "common/debug.h"
|
||||
|
||||
#include "common/platform/sysDbg.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
void DebugStack::Print(int from, [[maybe_unused]] bool with_name) const {
|
||||
for (int i = from; i < depth; i++) {
|
||||
printf("[%d] %016" PRIx64 "\n", i - from, static_cast<uint64_t>(GetAddr(i)));
|
||||
}
|
||||
}
|
||||
|
||||
void DebugStack::Trace(DebugStack* stack) {
|
||||
stack->depth = DEBUG_MAX_STACK_DEPTH;
|
||||
SysStackWalk(stack->stack.data(), &stack->depth);
|
||||
}
|
||||
|
||||
std::string Debug::GetCompiler() {
|
||||
#if KYTY_COMPILER == KYTY_COMPILER_CLANG
|
||||
return "clang";
|
||||
|
||||
+1
-26
@@ -2,42 +2,17 @@
|
||||
#define KYTY_COMMON_DEBUG_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/stringUtils.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
namespace Common {
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS && KYTY_BUILD == KYTY_BUILD_DEBUG && \
|
||||
KYTY_COMPILER == KYTY_COMPILER_CLANG
|
||||
constexpr int DEBUG_MAX_STACK_DEPTH = 20;
|
||||
#else
|
||||
constexpr int DEBUG_MAX_STACK_DEPTH = 15;
|
||||
#endif
|
||||
|
||||
namespace Debug {
|
||||
std::string GetCompiler();
|
||||
std::string GetLinker();
|
||||
std::string GetBitness();
|
||||
} // namespace Debug
|
||||
|
||||
struct DebugStack {
|
||||
int depth {0};
|
||||
std::array<void*, DEBUG_MAX_STACK_DEPTH> stack {};
|
||||
|
||||
[[nodiscard]] uintptr_t GetAddr(int i) const { return reinterpret_cast<uintptr_t>(stack[i]); }
|
||||
|
||||
void Print(int from, bool with_name = true) const;
|
||||
|
||||
void CopyTo(DebugStack* s) const {
|
||||
std::memcpy(s->stack.data(), stack.data(), sizeof(void*) * depth);
|
||||
s->depth = depth;
|
||||
}
|
||||
|
||||
static void Trace(DebugStack* stack);
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
#endif /* KYTY_COMMON_DEBUG_H_ */
|
||||
@@ -9,18 +9,20 @@ namespace Config {
|
||||
|
||||
static std::unique_ptr<ConfigOptions> g_config;
|
||||
|
||||
KYTY_SUBSYSTEM_INIT(Config) {
|
||||
void Initialize() {
|
||||
EXIT_IF(g_config != nullptr);
|
||||
|
||||
g_config = std::make_unique<ConfigOptions>();
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Config) {}
|
||||
|
||||
KYTY_SUBSYSTEM_DESTROY(Config) {}
|
||||
void Shutdown() {
|
||||
g_config.reset();
|
||||
}
|
||||
|
||||
void Load(const ConfigOptions& cfg) {
|
||||
EXIT_IF(g_config == nullptr);
|
||||
EXIT_IF(cfg.user_name.empty() || cfg.user_name.size() > MAX_USER_NAME_LENGTH);
|
||||
EXIT_IF(!IsConfiguredUserIdValid(cfg.user_id));
|
||||
|
||||
*g_config = cfg;
|
||||
}
|
||||
@@ -33,10 +35,30 @@ uint32_t GetScreenHeight() {
|
||||
return g_config->screen_height;
|
||||
}
|
||||
|
||||
const std::string& GetUserName() {
|
||||
return g_config->user_name;
|
||||
}
|
||||
|
||||
int32_t GetUserId() {
|
||||
return g_config->user_id;
|
||||
}
|
||||
|
||||
PresentMode GetPresentMode() {
|
||||
return g_config->present_mode;
|
||||
}
|
||||
|
||||
int32_t GetGpuIndex() {
|
||||
return g_config->gpu_index;
|
||||
}
|
||||
|
||||
bool FullscreenEnabled() {
|
||||
return g_config->fullscreen_enabled;
|
||||
}
|
||||
|
||||
bool VrEnabled() {
|
||||
return g_config->vr_enabled;
|
||||
}
|
||||
|
||||
uint32_t GetVblankFrequency() {
|
||||
return std::clamp(g_config->vblank_frequency, 30u, 360u);
|
||||
}
|
||||
@@ -57,7 +79,7 @@ ShaderOptimizationType GetShaderOptimizationType() {
|
||||
return g_config->shader_optimization_type;
|
||||
}
|
||||
|
||||
ShaderLogDirection GetShaderLogDirection() {
|
||||
LogDirection GetShaderLogDirection() {
|
||||
return g_config->shader_log_direction;
|
||||
}
|
||||
|
||||
@@ -77,7 +99,7 @@ bool GraphicsDebugDumpEnabled() {
|
||||
return g_config->graphics_debug_dump_enabled;
|
||||
}
|
||||
|
||||
OutputDirection GetPrintfDirection() {
|
||||
LogDirection GetPrintfDirection() {
|
||||
return g_config->printf_direction;
|
||||
}
|
||||
|
||||
@@ -85,14 +107,18 @@ std::filesystem::path GetPrintfOutputFile() {
|
||||
return g_config->printf_output_file;
|
||||
}
|
||||
|
||||
ProfilerDirection GetProfilerDirection() {
|
||||
return g_config->profiler_direction;
|
||||
bool ProfilerEnabled() {
|
||||
return g_config->profiler_enabled;
|
||||
}
|
||||
|
||||
bool SpirvDebugPrintfEnabled() {
|
||||
return g_config->spirv_debug_printf_enabled;
|
||||
}
|
||||
|
||||
bool GpuAssistedValidationEnabled() {
|
||||
return g_config->gpu_assisted_validation_enabled && g_config->vulkan_validation_enabled;
|
||||
}
|
||||
|
||||
bool RenderDocEnabled() {
|
||||
return g_config->renderdoc_enabled;
|
||||
}
|
||||
@@ -101,6 +127,10 @@ bool ReadbackLinearImagesEnabled() {
|
||||
return g_config->readback_linear_images;
|
||||
}
|
||||
|
||||
bool PlayGoHackEnabled() {
|
||||
return g_config->playgo_hack_enabled;
|
||||
}
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
bool RedZoneProtectionEnabled() {
|
||||
return g_config->red_zone_protection_enabled;
|
||||
|
||||
+40
-12
@@ -2,49 +2,69 @@
|
||||
#define KYTY_COMMON_EMULATOR_CONFIG_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/subsystems.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Config {
|
||||
|
||||
KYTY_SUBSYSTEM_DEFINE(Config);
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
|
||||
struct Lifecycle {
|
||||
static constexpr const char* name = "Config";
|
||||
static constexpr auto initialize = Config::Initialize;
|
||||
static constexpr auto shutdown = Config::Shutdown;
|
||||
};
|
||||
|
||||
enum class ShaderOptimizationType { None, Size, Performance };
|
||||
|
||||
enum class ShaderLogDirection { Silent, Console, File };
|
||||
enum class LogDirection { Silent, Console, File };
|
||||
|
||||
enum class ProfilerDirection { None, Network };
|
||||
|
||||
enum class OutputDirection { Silent, Console, File };
|
||||
enum class PresentMode { Fifo, Mailbox, Immediate };
|
||||
|
||||
using Keymap = std::vector<std::string>;
|
||||
|
||||
constexpr uint32_t DEFAULT_CONSOLE_LANGUAGE = 1;
|
||||
constexpr uint32_t MAX_CONSOLE_LANGUAGE = 29;
|
||||
constexpr std::size_t MAX_USER_NAME_LENGTH = 16;
|
||||
constexpr int32_t DEFAULT_USER_ID = 1000;
|
||||
|
||||
constexpr bool IsConfiguredUserIdValid(int32_t user_id) {
|
||||
constexpr int32_t USER_ID_EVERYONE = 0xfe;
|
||||
constexpr int32_t USER_ID_SYSTEM = 0xff;
|
||||
return user_id >= 0 && user_id != USER_ID_EVERYONE && user_id != USER_ID_SYSTEM;
|
||||
}
|
||||
|
||||
struct ConfigOptions {
|
||||
uint32_t screen_width = 1280;
|
||||
uint32_t screen_height = 720;
|
||||
std::string user_name = "Kyty";
|
||||
int32_t user_id = DEFAULT_USER_ID;
|
||||
PresentMode present_mode = PresentMode::Mailbox;
|
||||
int32_t gpu_index = -1;
|
||||
bool fullscreen_enabled = false;
|
||||
bool vr_enabled = false;
|
||||
uint32_t vblank_frequency = 60;
|
||||
uint32_t console_language = DEFAULT_CONSOLE_LANGUAGE;
|
||||
bool vulkan_validation_enabled = false;
|
||||
bool shader_validation_enabled = false;
|
||||
ShaderOptimizationType shader_optimization_type = ShaderOptimizationType::None;
|
||||
ShaderLogDirection shader_log_direction = ShaderLogDirection::Silent;
|
||||
LogDirection shader_log_direction = LogDirection::Silent;
|
||||
std::filesystem::path shader_log_folder = "_Shaders";
|
||||
bool command_buffer_dump_enabled = false;
|
||||
std::filesystem::path command_buffer_dump_folder = "_Buffers";
|
||||
bool graphics_debug_dump_enabled = false;
|
||||
OutputDirection printf_direction = OutputDirection::Console;
|
||||
LogDirection printf_direction = LogDirection::Silent;
|
||||
std::filesystem::path printf_output_file = "_kyty.txt";
|
||||
ProfilerDirection profiler_direction = ProfilerDirection::None;
|
||||
bool profiler_enabled = false;
|
||||
bool spirv_debug_printf_enabled = false;
|
||||
bool gpu_assisted_validation_enabled = false;
|
||||
bool renderdoc_enabled = false;
|
||||
bool readback_linear_images = false;
|
||||
bool playgo_hack_enabled = false;
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
bool red_zone_protection_enabled = false;
|
||||
#endif
|
||||
@@ -55,14 +75,19 @@ void Load(const ConfigOptions& cfg);
|
||||
|
||||
uint32_t GetScreenWidth();
|
||||
uint32_t GetScreenHeight();
|
||||
const std::string& GetUserName();
|
||||
int32_t GetUserId();
|
||||
PresentMode GetPresentMode();
|
||||
int32_t GetGpuIndex();
|
||||
bool FullscreenEnabled();
|
||||
bool VrEnabled();
|
||||
uint32_t GetVblankFrequency();
|
||||
uint32_t GetConsoleLanguage();
|
||||
bool VulkanValidationEnabled();
|
||||
|
||||
bool ShaderValidationEnabled();
|
||||
ShaderOptimizationType GetShaderOptimizationType();
|
||||
ShaderLogDirection GetShaderLogDirection();
|
||||
LogDirection GetShaderLogDirection();
|
||||
std::filesystem::path GetShaderLogFolder();
|
||||
|
||||
bool CommandBufferDumpEnabled();
|
||||
@@ -70,15 +95,18 @@ std::filesystem::path GetCommandBufferDumpFolder();
|
||||
|
||||
bool GraphicsDebugDumpEnabled();
|
||||
|
||||
OutputDirection GetPrintfDirection();
|
||||
LogDirection GetPrintfDirection();
|
||||
std::filesystem::path GetPrintfOutputFile();
|
||||
|
||||
ProfilerDirection GetProfilerDirection();
|
||||
bool ProfilerEnabled();
|
||||
|
||||
bool SpirvDebugPrintfEnabled();
|
||||
|
||||
bool GpuAssistedValidationEnabled();
|
||||
|
||||
bool RenderDocEnabled();
|
||||
bool ReadbackLinearImagesEnabled();
|
||||
bool PlayGoHackEnabled();
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
bool RedZoneProtectionEnabled();
|
||||
#endif
|
||||
|
||||
+4
-7
@@ -25,9 +25,6 @@
|
||||
#ifdef CopyFile
|
||||
#undef CopyFile
|
||||
#endif
|
||||
#ifdef MoveFile
|
||||
#undef MoveFile
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
@@ -72,7 +69,7 @@ File::File(): m_p(std::make_unique<FilePrivate>()) {
|
||||
}
|
||||
|
||||
File::~File() {
|
||||
EXIT_IF(m_p->f != nullptr);
|
||||
Close();
|
||||
}
|
||||
|
||||
File::File(const std::filesystem::path& name): m_p(std::make_unique<FilePrivate>()) {
|
||||
@@ -544,14 +541,14 @@ bool File::CopyFile(const std::filesystem::path& src,
|
||||
return SysFileCopyFile(src, dst);
|
||||
}
|
||||
|
||||
bool File::MoveFile(const std::filesystem::path& src,
|
||||
const std::filesystem::path& dst) // @suppress("Member declaration not found")
|
||||
bool File::RenameFile(const std::filesystem::path& src,
|
||||
const std::filesystem::path& dst) // @suppress("Member declaration not found")
|
||||
{
|
||||
if (IsFileExisting(dst)) {
|
||||
DeleteFile(dst); // @suppress("Invalid arguments")
|
||||
}
|
||||
|
||||
return SysFileMoveFile(src, dst);
|
||||
return SysFileRenameFile(src, dst);
|
||||
}
|
||||
|
||||
void File::RemoveReadonly(const std::filesystem::path& name) {
|
||||
|
||||
+1
-4
@@ -20,9 +20,6 @@
|
||||
#ifdef CopyFile
|
||||
#undef CopyFile
|
||||
#endif
|
||||
#ifdef MoveFile
|
||||
#undef MoveFile
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
@@ -107,7 +104,7 @@ public:
|
||||
static std::vector<FindInfo> FindFiles(const std::filesystem::path& path);
|
||||
static std::vector<DirEntry> GetDirEntries(const std::filesystem::path& path);
|
||||
static bool CopyFile(const std::filesystem::path& src, const std::filesystem::path& dst);
|
||||
static bool MoveFile(const std::filesystem::path& src, const std::filesystem::path& dst);
|
||||
static bool RenameFile(const std::filesystem::path& src, const std::filesystem::path& dst);
|
||||
static void RemoveReadonly(const std::filesystem::path& name);
|
||||
|
||||
KYTY_CLASS_NO_COPY(File);
|
||||
|
||||
@@ -2,18 +2,20 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
#include <windows.h> // IWYU pragma: keep
|
||||
#elif defined(__APPLE__)
|
||||
#else
|
||||
#include <algorithm>
|
||||
#include <csignal>
|
||||
#include <cstdlib>
|
||||
#include <initializer_list>
|
||||
#include <unistd.h>
|
||||
#if defined(__APPLE__)
|
||||
#include <sys/ucontext.h>
|
||||
#else
|
||||
#include <csignal>
|
||||
#include <initializer_list>
|
||||
#include <ucontext.h> // IWYU pragma: keep
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// IWYU pragma: no_include <errhandlingapi.h>
|
||||
@@ -28,54 +30,59 @@ namespace Common::HostException {
|
||||
|
||||
static std::atomic<Handler> g_handler {nullptr};
|
||||
static std::atomic_uint32_t g_install_state {0};
|
||||
static thread_local bool g_in_exception_filter = false;
|
||||
|
||||
static_assert(decltype(g_handler)::is_always_lock_free);
|
||||
static_assert(decltype(g_install_state)::is_always_lock_free);
|
||||
|
||||
[[noreturn]] static void FailFast(const char* reason) noexcept {
|
||||
std::fputs("HostException fail-fast: ", stderr);
|
||||
std::fputs(reason != nullptr ? reason : "unspecified", stderr);
|
||||
std::fputc('\n', stderr);
|
||||
std::fflush(stderr);
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
TerminateProcess(GetCurrentProcess(), static_cast<UINT>(EXCEPTION_NONCONTINUABLE_EXCEPTION));
|
||||
#endif
|
||||
std::_Exit(321);
|
||||
}
|
||||
|
||||
class FilterScope final {
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_LINUX
|
||||
|
||||
// macOS uses the same POSIX platform setting and needs a signal stack too.
|
||||
class ThreadSignalStack {
|
||||
public:
|
||||
FilterScope() noexcept {
|
||||
if (g_in_exception_filter) {
|
||||
FailFast("nested exception while resolving a host fault");
|
||||
ThreadSignalStack() {
|
||||
const auto page_size = static_cast<size_t>(::getpagesize());
|
||||
const auto stack_size =
|
||||
(std::max<size_t>(64 * 1024, MINSIGSTKSZ) + page_size - 1) & ~(page_size - 1);
|
||||
if (::posix_memalign(&m_memory, page_size, stack_size) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
stack_t stack {};
|
||||
stack.ss_sp = m_memory;
|
||||
stack.ss_size = stack_size;
|
||||
if (::sigaltstack(&stack, &m_previous) != 0) {
|
||||
std::free(m_memory);
|
||||
m_memory = nullptr;
|
||||
}
|
||||
g_in_exception_filter = true;
|
||||
}
|
||||
|
||||
~FilterScope() { g_in_exception_filter = false; }
|
||||
~ThreadSignalStack() {
|
||||
if (m_memory != nullptr && ::sigaltstack(&m_previous, nullptr) == 0) {
|
||||
std::free(m_memory);
|
||||
}
|
||||
}
|
||||
|
||||
KYTY_CLASS_NO_COPY(FilterScope);
|
||||
[[nodiscard]] bool IsInitialized() const { return m_memory != nullptr; }
|
||||
|
||||
KYTY_CLASS_NO_COPY(ThreadSignalStack)
|
||||
|
||||
private:
|
||||
void* m_memory = nullptr;
|
||||
stack_t m_previous {};
|
||||
};
|
||||
|
||||
static Handler LoadInstalledHandler() noexcept {
|
||||
if (g_install_state.load(std::memory_order_acquire) == 0) {
|
||||
FailFast("host exception handler is not installed");
|
||||
}
|
||||
|
||||
const auto handler = g_handler.load(std::memory_order_acquire);
|
||||
if (handler == nullptr) {
|
||||
FailFast("host exception callback is null");
|
||||
}
|
||||
return handler;
|
||||
bool InitializeThreadSignalStack() {
|
||||
// Keep fault handling off guest stacks, which GPU tracking can make read-only.
|
||||
thread_local ThreadSignalStack signal_stack;
|
||||
return signal_stack.IsInitialized();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
|
||||
static LONG WINAPI ExceptionFilter(PEXCEPTION_POINTERS exception) {
|
||||
FilterScope filter_scope;
|
||||
|
||||
static LONG WINAPI ExceptionFilter(PEXCEPTION_POINTERS exception) noexcept {
|
||||
auto* exception_record = exception->ExceptionRecord;
|
||||
|
||||
if (exception_record->ExceptionCode == DBG_PRINTEXCEPTION_C ||
|
||||
@@ -105,12 +112,6 @@ static LONG WINAPI ExceptionFilter(PEXCEPTION_POINTERS exception) {
|
||||
} else if (exception_record->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION) {
|
||||
info.type = ExceptionType::IllegalInstruction;
|
||||
} else {
|
||||
printf("Unhandled win exception: code=0x%08" PRIx32 ", addr=0x%016" PRIx64
|
||||
", rip=0x%016" PRIx64 ", rsp=0x%016" PRIx64 ", rbp=0x%016" PRIx64 "\n",
|
||||
static_cast<uint32_t>(exception_record->ExceptionCode),
|
||||
reinterpret_cast<uint64_t>(exception_record->ExceptionAddress),
|
||||
exception->ContextRecord->Rip, exception->ContextRecord->Rsp,
|
||||
exception->ContextRecord->Rbp);
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
@@ -131,28 +132,21 @@ static LONG WINAPI ExceptionFilter(PEXCEPTION_POINTERS exception) {
|
||||
info.r14 = exception->ContextRecord->R14;
|
||||
info.r15 = exception->ContextRecord->R15;
|
||||
|
||||
const auto handler = LoadInstalledHandler();
|
||||
|
||||
return handler(info) ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH;
|
||||
const auto handler = g_handler.load(std::memory_order_acquire);
|
||||
if (handler != nullptr && handler(info)) {
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
static std::atomic<Handler> g_handler {nullptr};
|
||||
static std::atomic_uint32_t g_install_state {0};
|
||||
static thread_local bool g_in_exception_filter = false;
|
||||
|
||||
static_assert(decltype(g_handler)::is_always_lock_free);
|
||||
static_assert(decltype(g_install_state)::is_always_lock_free);
|
||||
|
||||
[[noreturn]] static void FailFast(const char* reason) noexcept {
|
||||
std::fputs("HostException fail-fast: ", stderr);
|
||||
std::fputs(reason != nullptr ? reason : "unspecified", stderr);
|
||||
std::fputc('\n', stderr);
|
||||
std::fflush(stderr);
|
||||
std::_Exit(321);
|
||||
}
|
||||
|
||||
// Translate the x86-64 page-fault error code (mcontext __es.__err) into an access type.
|
||||
// bit 1 (0x2) = write, bit 4 (0x10) = instruction fetch, otherwise a read.
|
||||
static AccessViolationType DecodeAccess(uint64_t err) {
|
||||
@@ -170,11 +164,6 @@ static AccessViolationType DecodeAccess(uint64_t err) {
|
||||
// re-executing the faulting instruction against the now-fixed protection. An unresolved
|
||||
// fault restores the default disposition so the retry terminates the process.
|
||||
static void SignalHandler(int sig, siginfo_t* si, void* uctx) {
|
||||
if (g_in_exception_filter) {
|
||||
FailFast("nested exception while resolving a host fault");
|
||||
}
|
||||
g_in_exception_filter = true;
|
||||
|
||||
auto* uc = static_cast<ucontext_t*>(uctx);
|
||||
const auto* mc = uc->uc_mcontext;
|
||||
const auto& ss = mc->__ss;
|
||||
@@ -210,14 +199,7 @@ static void SignalHandler(int sig, siginfo_t* si, void* uctx) {
|
||||
info.r15 = ss.__r15;
|
||||
|
||||
const auto handler = g_handler.load(std::memory_order_acquire);
|
||||
if (handler == nullptr) {
|
||||
FailFast("host exception callback is null");
|
||||
}
|
||||
|
||||
const bool resolved = handler(info);
|
||||
g_in_exception_filter = false;
|
||||
|
||||
if (resolved) {
|
||||
if (handler != nullptr && handler(info)) {
|
||||
return; // retry the faulting instruction against the fixed mapping
|
||||
}
|
||||
|
||||
@@ -244,8 +226,6 @@ static void ChainToDefault(int signal_number) noexcept {
|
||||
}
|
||||
|
||||
static void SignalHandler(int signal_number, siginfo_t* signal_info, void* native_context) {
|
||||
FilterScope filter_scope;
|
||||
|
||||
auto* context = static_cast<ucontext_t*>(native_context);
|
||||
auto* gregs = context->uc_mcontext.gregs;
|
||||
|
||||
@@ -289,9 +269,8 @@ static void SignalHandler(int signal_number, siginfo_t* signal_info, void* nativ
|
||||
info.r14 = static_cast<uint64_t>(gregs[REG_R14]);
|
||||
info.r15 = static_cast<uint64_t>(gregs[REG_R15]);
|
||||
|
||||
const auto handler = LoadInstalledHandler();
|
||||
|
||||
if (handler(info)) {
|
||||
const auto handler = g_handler.load(std::memory_order_acquire);
|
||||
if (handler != nullptr && handler(info)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -313,7 +292,7 @@ bool InstallHandler(Handler handler) {
|
||||
g_handler.store(handler, std::memory_order_release);
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
if (AddVectoredExceptionHandler(1, ExceptionFilter) == nullptr) {
|
||||
if (AddVectoredExceptionHandler(0, ExceptionFilter) == nullptr) {
|
||||
g_handler.store(nullptr, std::memory_order_release);
|
||||
g_install_state.store(0, std::memory_order_release);
|
||||
printf("AddVectoredExceptionHandler() failed\n");
|
||||
@@ -322,7 +301,7 @@ bool InstallHandler(Handler handler) {
|
||||
#elif defined(__APPLE__)
|
||||
struct sigaction sa {};
|
||||
sa.sa_sigaction = SignalHandler;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
// The guest signal-dispatch path (KernelRaiseException) interrupts threads with
|
||||
// SIGUSR1; block it while a fault is being resolved so a stop-the-world request
|
||||
@@ -343,8 +322,7 @@ bool InstallHandler(Handler handler) {
|
||||
struct sigaction action {};
|
||||
action.sa_sigaction = SignalHandler;
|
||||
sigemptyset(&action.sa_mask);
|
||||
// Fault resolution needs the normal thread stack.
|
||||
action.sa_flags = SA_SIGINFO | SA_RESTART;
|
||||
action.sa_flags = SA_SIGINFO | SA_RESTART | SA_ONSTACK;
|
||||
|
||||
for (const int signal_number: {SIGSEGV, SIGBUS, SIGILL}) {
|
||||
if (::sigaction(signal_number, &action, nullptr) != 0) {
|
||||
|
||||
@@ -39,6 +39,10 @@ using Handler = bool (*)(const ExceptionInfo&);
|
||||
|
||||
bool InstallHandler(Handler handler);
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_LINUX
|
||||
bool InitializeThreadSignalStack();
|
||||
#endif
|
||||
|
||||
} // namespace Common::HostException
|
||||
|
||||
#endif /* KYTY_COMMON_HOST_EXCEPTION_H_ */
|
||||
@@ -149,25 +149,19 @@ void WriteFatal(fmt::text_style style, std::string_view text) {
|
||||
Flush();
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_INIT(Log) {
|
||||
void Initialize() {
|
||||
g_initialized = true;
|
||||
switch (Config::GetPrintfDirection()) {
|
||||
case Config::OutputDirection::Silent: g_direction = Direction::Silent; break;
|
||||
case Config::OutputDirection::Console: g_direction = Direction::Console; break;
|
||||
case Config::OutputDirection::File: g_direction = Direction::File; break;
|
||||
case Config::LogDirection::Silent: g_direction = Direction::Silent; break;
|
||||
case Config::LogDirection::Console: g_direction = Direction::Console; break;
|
||||
case Config::LogDirection::File: g_direction = Direction::File; break;
|
||||
}
|
||||
g_output_file =
|
||||
(g_direction == Direction::File ? Config::GetPrintfOutputFile() : std::filesystem::path {});
|
||||
SetupLogger();
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Log) {
|
||||
Flush();
|
||||
std::lock_guard lock(g_logger_mutex);
|
||||
g_logger.reset();
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_DESTROY(Log) {
|
||||
void Shutdown() {
|
||||
Flush();
|
||||
std::lock_guard lock(g_logger_mutex);
|
||||
g_logger.reset();
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#define KYTY_COMMON_LOGGING_LOG_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/subsystems.h"
|
||||
|
||||
#include <fmt/color.h>
|
||||
#include <fmt/printf.h>
|
||||
@@ -10,7 +9,15 @@
|
||||
|
||||
namespace Log {
|
||||
|
||||
KYTY_SUBSYSTEM_DEFINE(Log);
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
|
||||
struct Lifecycle {
|
||||
static constexpr const char* name = "Log";
|
||||
static constexpr auto initialize = Log::Initialize;
|
||||
static constexpr auto shutdown = Log::Shutdown;
|
||||
static constexpr auto emergency_shutdown = Log::Shutdown;
|
||||
};
|
||||
|
||||
enum class Direction { Silent, Console, File };
|
||||
|
||||
|
||||
@@ -30,8 +30,6 @@ struct sys_dbg_stack_info_t {
|
||||
#endif
|
||||
};
|
||||
|
||||
void SysStackWalk(void** stack, int* depth);
|
||||
void SysStackUsage(sys_dbg_stack_info_t& s); // NOLINT(google-runtime-references)
|
||||
void SysStackUsagePrint(sys_dbg_stack_info_t& stack); // NOLINT(google-runtime-references)
|
||||
void SysStackUsage(sys_dbg_stack_info_t& s); // NOLINT(google-runtime-references)
|
||||
|
||||
#endif /* KYTY_COMMON_PLATFORM_SYSDBG_H_ */
|
||||
@@ -80,7 +80,7 @@ void SysFileFindFiles(const std::filesystem::path& path, std::vector<sys_file_fi
|
||||
// NOLINTNEXTLINE(google-runtime-references)
|
||||
void SysFileGetDents(const std::filesystem::path& path, std::vector<sys_dir_entry_t>& out);
|
||||
bool SysFileCopyFile(const std::filesystem::path& src, const std::filesystem::path& dst);
|
||||
bool SysFileMoveFile(const std::filesystem::path& src, const std::filesystem::path& dst);
|
||||
bool SysFileRenameFile(const std::filesystem::path& src, const std::filesystem::path& dst);
|
||||
void SysFileRemoveReadonly(const std::filesystem::path& name);
|
||||
|
||||
#endif /* KYTY_COMMON_PLATFORM_SYSFILEIO_H_ */
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <execinfo.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
@@ -17,56 +16,6 @@
|
||||
#include <libgen.h> // POSIX basename() lives here on macOS, not in <cstring>
|
||||
#endif
|
||||
|
||||
// Avoid unwinding a guest-owned stack.
|
||||
static bool OnOwnStack() {
|
||||
const char* probe = reinterpret_cast<const char*>(&probe);
|
||||
|
||||
pthread_attr_t attr {};
|
||||
#if defined(__APPLE__)
|
||||
const auto* top = static_cast<const char*>(pthread_get_stackaddr_np(pthread_self()));
|
||||
const auto size = pthread_get_stacksize_np(pthread_self());
|
||||
(void)attr;
|
||||
return top != nullptr && size != 0 && probe < top && probe >= top - size;
|
||||
#else
|
||||
if (pthread_getattr_np(pthread_self(), &attr) != 0) {
|
||||
return false;
|
||||
}
|
||||
void* base = nullptr;
|
||||
size_t size = 0;
|
||||
const bool ok = pthread_attr_getstack(&attr, &base, &size) == 0 && base != nullptr && size != 0;
|
||||
pthread_attr_destroy(&attr);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
const auto* low = static_cast<const char*>(base);
|
||||
return probe >= low && probe < low + size;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SysStackWalk(void** stack, int* depth) {
|
||||
if (stack == nullptr || depth == nullptr || *depth <= 0) {
|
||||
if (depth != nullptr) {
|
||||
*depth = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!OnOwnStack()) {
|
||||
*depth = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const int n = ::backtrace(stack, *depth);
|
||||
*depth = (n < 0 ? 0 : n);
|
||||
}
|
||||
|
||||
void SysStackUsagePrint(sys_dbg_stack_info_t& stack) {
|
||||
printf("stack: (0x%" PRIx64 ", %" PRIu64 ")\n", static_cast<uint64_t>(stack.commited_addr),
|
||||
static_cast<uint64_t>(stack.commited_size));
|
||||
printf("code: (0x%" PRIx64 ", %" PRIu64 ")\n", static_cast<uint64_t>(stack.code_addr),
|
||||
static_cast<uint64_t>(stack.code_size));
|
||||
}
|
||||
|
||||
void SysStackUsage(sys_dbg_stack_info_t& s) {
|
||||
pid_t pid = getpid();
|
||||
|
||||
|
||||
@@ -693,7 +693,7 @@ bool SysFileCopyFile(const std::filesystem::path& src, const std::filesystem::pa
|
||||
!error;
|
||||
}
|
||||
|
||||
bool SysFileMoveFile(const std::filesystem::path& src, const std::filesystem::path& dst) {
|
||||
bool SysFileRenameFile(const std::filesystem::path& src, const std::filesystem::path& dst) {
|
||||
auto real_src = get_internal_name(src);
|
||||
auto real_dst = get_internal_name(dst);
|
||||
|
||||
|
||||
@@ -31,8 +31,7 @@
|
||||
namespace Common {
|
||||
|
||||
static pthread_mutex_t g_virtual_mutex {};
|
||||
static std::map<uintptr_t, size_t>* g_allocs = nullptr;
|
||||
static std::map<uintptr_t, int>* g_protects = nullptr;
|
||||
static std::map<uintptr_t, size_t>* g_allocs = nullptr;
|
||||
|
||||
void SysVirtualInit() {
|
||||
pthread_mutexattr_t attr {};
|
||||
@@ -46,8 +45,7 @@ void SysVirtualInit() {
|
||||
pthread_mutex_init(&g_virtual_mutex, &attr);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
|
||||
g_allocs = new std::map<uintptr_t, size_t>;
|
||||
g_protects = new std::map<uintptr_t, int>;
|
||||
g_allocs = new std::map<uintptr_t, size_t>;
|
||||
}
|
||||
|
||||
static int get_protection_flag(VirtualMemory::Mode mode) {
|
||||
@@ -68,21 +66,6 @@ static int get_protection_flag(VirtualMemory::Mode mode) {
|
||||
return protect;
|
||||
}
|
||||
|
||||
static VirtualMemory::Mode get_protection_flag(int mode) {
|
||||
switch (mode) {
|
||||
case PROT_NONE: return VirtualMemory::Mode::NoAccess;
|
||||
case PROT_READ: return VirtualMemory::Mode::Read;
|
||||
case PROT_WRITE: return VirtualMemory::Mode::Write;
|
||||
case PROT_READ | PROT_WRITE: return VirtualMemory::Mode::ReadWrite; // NOLINT
|
||||
case PROT_EXEC: return VirtualMemory::Mode::Execute;
|
||||
case PROT_EXEC | PROT_WRITE: return VirtualMemory::Mode::ExecuteWrite; // NOLINT
|
||||
case PROT_EXEC | PROT_READ: return VirtualMemory::Mode::ExecuteRead; // NOLINT
|
||||
case PROT_EXEC | PROT_WRITE | PROT_READ:
|
||||
return VirtualMemory::Mode::ExecuteReadWrite; // NOLINT
|
||||
default: return VirtualMemory::Mode::NoAccess;
|
||||
}
|
||||
}
|
||||
|
||||
// Keep automatic mappings inside the guest and GPU-addressable low window.
|
||||
#ifdef KYTY_FIXED_NOREPLACE
|
||||
static constexpr uintptr_t LOW_ARENA_LIMIT = 0x000000FC00000000ULL; // libc mspace window ceiling
|
||||
@@ -161,11 +144,6 @@ uint64_t SysVirtualAlloc(uint64_t address, uint64_t size, VirtualMemory::Mode mo
|
||||
if (ptr != MAP_FAILED) {
|
||||
pthread_mutex_lock(&g_virtual_mutex);
|
||||
record_alloc(ret_addr, size);
|
||||
uintptr_t page_start = ret_addr >> 12u;
|
||||
uintptr_t page_end = (ret_addr + size - 1) >> 12u;
|
||||
for (uintptr_t page = page_start; page <= page_end; page++) {
|
||||
(*g_protects)[page] = protect;
|
||||
}
|
||||
pthread_mutex_unlock(&g_virtual_mutex);
|
||||
}
|
||||
|
||||
@@ -251,11 +229,6 @@ uint64_t SysVirtualAllocAligned(uint64_t address, uint64_t size, VirtualMemory::
|
||||
|
||||
pthread_mutex_lock(&g_virtual_mutex);
|
||||
record_alloc(ret_addr, size);
|
||||
uintptr_t page_start = ret_addr >> 12u;
|
||||
uintptr_t page_end = (ret_addr + size - 1) >> 12u;
|
||||
for (uintptr_t page = page_start; page <= page_end; page++) {
|
||||
(*g_protects)[page] = protect;
|
||||
}
|
||||
pthread_mutex_unlock(&g_virtual_mutex);
|
||||
|
||||
return ret_addr;
|
||||
@@ -337,11 +310,6 @@ bool SysVirtualAllocFixed(uint64_t address, uint64_t size, VirtualMemory::Mode m
|
||||
if (ptr != MAP_FAILED) {
|
||||
pthread_mutex_lock(&g_virtual_mutex);
|
||||
record_alloc(ret_addr, size);
|
||||
uintptr_t page_start = ret_addr >> 12u;
|
||||
uintptr_t page_end = (ret_addr + size - 1) >> 12u;
|
||||
for (uintptr_t page = page_start; page <= page_end; page++) {
|
||||
(*g_protects)[page] = protect;
|
||||
}
|
||||
pthread_mutex_unlock(&g_virtual_mutex);
|
||||
|
||||
return true;
|
||||
@@ -516,18 +484,7 @@ bool SysVirtualFree(uint64_t address) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (munmap(reinterpret_cast<void*>(addr), size) == 0) {
|
||||
uintptr_t page_start = addr >> 12u;
|
||||
uintptr_t page_end = (addr + size - 1) >> 12u;
|
||||
pthread_mutex_lock(&g_virtual_mutex);
|
||||
for (uintptr_t page = page_start; page <= page_end; page++) {
|
||||
g_protects->erase(page);
|
||||
}
|
||||
pthread_mutex_unlock(&g_virtual_mutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return munmap(reinterpret_cast<void*>(addr), size) == 0;
|
||||
}
|
||||
|
||||
bool SysVirtualFreeRange(uint64_t address, uint64_t size) {
|
||||
@@ -582,40 +539,16 @@ bool SysVirtualFreeRange(uint64_t address, uint64_t size) {
|
||||
if (end < alloc_end) {
|
||||
(*g_allocs)[end] = alloc_end - end;
|
||||
}
|
||||
for (uintptr_t page = addr >> 12u; page <= (end - 1u) >> 12u; page++) {
|
||||
g_protects->erase(page);
|
||||
}
|
||||
pthread_mutex_unlock(&g_virtual_mutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SysVirtualProtect(uint64_t address, uint64_t size, VirtualMemory::Mode mode,
|
||||
VirtualMemory::Mode* old_mode) {
|
||||
auto addr = static_cast<uintptr_t>(address);
|
||||
|
||||
pthread_mutex_lock(&g_virtual_mutex);
|
||||
if (old_mode != nullptr) {
|
||||
if (auto s = g_protects->find(addr >> 12u); s != g_protects->end()) {
|
||||
*old_mode = get_protection_flag(s->second);
|
||||
} else {
|
||||
*old_mode = VirtualMemory::Mode::NoAccess;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&g_virtual_mutex);
|
||||
|
||||
uintptr_t page_start = addr >> 12u;
|
||||
uintptr_t page_end = (addr + size - 1) >> 12u;
|
||||
if (mprotect(reinterpret_cast<void*>(page_start << 12u), (page_end - page_start + 1) << 12u,
|
||||
get_protection_flag(mode)) == 0) {
|
||||
pthread_mutex_lock(&g_virtual_mutex);
|
||||
for (uintptr_t page = page_start; page <= page_end; page++) {
|
||||
(*g_protects)[page] = get_protection_flag(mode);
|
||||
}
|
||||
pthread_mutex_unlock(&g_virtual_mutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
bool SysVirtualProtect(uint64_t address, uint64_t size, VirtualMemory::Mode mode) {
|
||||
const auto addr = static_cast<uintptr_t>(address);
|
||||
const auto page_start = addr >> 12u;
|
||||
const auto page_end = (addr + size - 1) >> 12u;
|
||||
return mprotect(reinterpret_cast<void*>(page_start << 12u), (page_end - page_start + 1) << 12u,
|
||||
get_protection_flag(mode)) == 0;
|
||||
}
|
||||
|
||||
bool SysVirtualFlushInstructionCache(uint64_t /*address*/, uint64_t /*size*/) {
|
||||
|
||||
@@ -19,8 +19,7 @@ bool SysVirtualReserveFixed(uint64_t address, uint64_t size);
|
||||
bool SysVirtualDecommit(uint64_t address, uint64_t size);
|
||||
bool SysVirtualFree(uint64_t address);
|
||||
bool SysVirtualFreeRange(uint64_t address, uint64_t size);
|
||||
bool SysVirtualProtect(uint64_t address, uint64_t size, VirtualMemory::Mode mode,
|
||||
VirtualMemory::Mode* old_mode = nullptr);
|
||||
bool SysVirtualProtect(uint64_t address, uint64_t size, VirtualMemory::Mode mode);
|
||||
bool SysVirtualFlushInstructionCache(uint64_t address, uint64_t size);
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -15,66 +15,6 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/platform/sysDbg.h"
|
||||
|
||||
struct FrameS {
|
||||
struct FrameS* next;
|
||||
void* ret_addr;
|
||||
};
|
||||
|
||||
int WalkStack(int z_stack_depth, void** z_stack_trace) {
|
||||
CONTEXT context;
|
||||
// KNONVOLATILE_CONTEXT_POINTERS NvContext;
|
||||
PRUNTIME_FUNCTION runtime_function = nullptr;
|
||||
PVOID handler_data = nullptr;
|
||||
ULONG64 establisher_frame = 0;
|
||||
ULONG64 image_base = 0;
|
||||
|
||||
RtlCaptureContext(&context);
|
||||
|
||||
int frame = 0;
|
||||
|
||||
while (true) {
|
||||
if (frame >= z_stack_depth) {
|
||||
break;
|
||||
}
|
||||
|
||||
z_stack_trace[frame] = reinterpret_cast<void*>(context.Rip);
|
||||
|
||||
frame++;
|
||||
|
||||
runtime_function = RtlLookupFunctionEntry(context.Rip, &image_base, nullptr);
|
||||
|
||||
if (runtime_function == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
// RtlZeroMemory(&NvContext, sizeof(KNONVOLATILE_CONTEXT_POINTERS));
|
||||
RtlVirtualUnwind(0, image_base, context.Rip, runtime_function, &context, &handler_data,
|
||||
&establisher_frame, nullptr /*&NvContext*/);
|
||||
|
||||
if (context.Rip == 0u) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
void SysStackWalk(void** stack, int* depth) {
|
||||
int n = WalkStack(*depth, stack);
|
||||
*depth = n;
|
||||
// unwind_info_t info = {stack, 0, *depth};
|
||||
// _Unwind_Backtrace(&trace_fcn, &info);
|
||||
// *depth = info.depth;
|
||||
}
|
||||
|
||||
void SysStackUsagePrint(sys_dbg_stack_info_t& stack) {
|
||||
printf("stack: (0x%" PRIx64 ", %" PRIu64 ") + (0x%" PRIx64 ", %" PRIu64 ") + (0x%" PRIx64
|
||||
", %" PRIu64 ")\n",
|
||||
static_cast<uint64_t>(stack.reserved_addr), static_cast<uint64_t>(stack.reserved_size),
|
||||
static_cast<uint64_t>(stack.guard_addr), static_cast<uint64_t>(stack.guard_size),
|
||||
static_cast<uint64_t>(stack.commited_addr), static_cast<uint64_t>(stack.commited_size));
|
||||
}
|
||||
|
||||
void SysStackUsage(sys_dbg_stack_info_t& s) {
|
||||
MEMORY_BASIC_INFORMATION mbi {};
|
||||
[[maybe_unused]] size_t ss = VirtualQuery(&mbi, &mbi, sizeof(mbi));
|
||||
|
||||
@@ -565,7 +565,7 @@ bool SysFileCopyFile(const std::filesystem::path& src, const std::filesystem::pa
|
||||
return CopyFileW(src_wide.c_str(), dst_wide.c_str(), FALSE) != 0;
|
||||
}
|
||||
|
||||
bool SysFileMoveFile(const std::filesystem::path& src, const std::filesystem::path& dst) {
|
||||
bool SysFileRenameFile(const std::filesystem::path& src, const std::filesystem::path& dst) {
|
||||
auto src_wide = src.wstring();
|
||||
auto dst_wide = dst.wstring();
|
||||
return MoveFileW(src_wide.c_str(), dst_wide.c_str()) != 0;
|
||||
|
||||
@@ -45,18 +45,6 @@ static DWORD GetProtectionFlag(VirtualMemory::Mode mode) {
|
||||
return protect;
|
||||
}
|
||||
|
||||
static VirtualMemory::Mode GetProtectionFlag(DWORD mode) {
|
||||
switch (mode) {
|
||||
case PAGE_NOACCESS: return VirtualMemory::Mode::NoAccess;
|
||||
case PAGE_READONLY: return VirtualMemory::Mode::Read;
|
||||
case PAGE_READWRITE: return VirtualMemory::Mode::ReadWrite;
|
||||
case PAGE_EXECUTE: return VirtualMemory::Mode::Execute;
|
||||
case PAGE_EXECUTE_READ: return VirtualMemory::Mode::ExecuteRead;
|
||||
case PAGE_EXECUTE_READWRITE: return VirtualMemory::Mode::ExecuteReadWrite;
|
||||
default: return VirtualMemory::Mode::NoAccess;
|
||||
}
|
||||
}
|
||||
|
||||
void SysVirtualInit() {}
|
||||
|
||||
uint64_t SysVirtualAlloc(uint64_t address, uint64_t size, VirtualMemory::Mode mode) {
|
||||
@@ -328,17 +316,13 @@ bool SysVirtualFreeRange(uint64_t address, uint64_t size) {
|
||||
return current - address == size && SysVirtualFree(address);
|
||||
}
|
||||
|
||||
bool SysVirtualProtect(uint64_t address, uint64_t size, VirtualMemory::Mode mode,
|
||||
VirtualMemory::Mode* old_mode) {
|
||||
bool SysVirtualProtect(uint64_t address, uint64_t size, VirtualMemory::Mode mode) {
|
||||
DWORD old_protect = 0;
|
||||
if (VirtualProtect(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size,
|
||||
GetProtectionFlag(mode), &old_protect) == 0) {
|
||||
printf("VirtualProtect() failed: 0x%08" PRIx32 "\n", static_cast<uint32_t>(GetLastError()));
|
||||
return false;
|
||||
}
|
||||
if (old_mode != nullptr) {
|
||||
*old_mode = GetProtectionFlag(old_protect);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+12
-27
@@ -1,7 +1,6 @@
|
||||
#include "common/profiler.h"
|
||||
|
||||
#include "common/emulatorConfig.h"
|
||||
#include "common/subsystems.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <common/TracyProtocol.hpp>
|
||||
@@ -55,35 +54,21 @@ void SetThreadName(const char* name) {
|
||||
}
|
||||
}
|
||||
|
||||
void Close() {
|
||||
void Initialize() {
|
||||
if (Config::ProfilerEnabled() && !tracy::ProfilerAvailable()) {
|
||||
tracy::StartupProfiler();
|
||||
TracySetProgramName("KytyPS5");
|
||||
::printf("Tracy profiler enabled: client %d.%d.%d, protocol %u, "
|
||||
"broadcast %u, connect to 127.0.0.1:8086\n",
|
||||
tracy::Version::Major, tracy::Version::Minor, tracy::Version::Patch,
|
||||
tracy::ProtocolVersion, tracy::BroadcastVersion);
|
||||
}
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
if (tracy::ProfilerAvailable()) {
|
||||
tracy::ShutdownProfiler();
|
||||
}
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_INIT(Profiler) {
|
||||
switch (Config::GetProfilerDirection()) {
|
||||
case Config::ProfilerDirection::Network:
|
||||
if (!tracy::ProfilerAvailable()) {
|
||||
tracy::StartupProfiler();
|
||||
TracySetProgramName("KytyPS5");
|
||||
::printf("Tracy profiler enabled: client %d.%d.%d, protocol %u, "
|
||||
"broadcast %u, connect to 127.0.0.1:8086\n",
|
||||
tracy::Version::Major, tracy::Version::Minor, tracy::Version::Patch,
|
||||
tracy::ProtocolVersion, tracy::BroadcastVersion);
|
||||
}
|
||||
break;
|
||||
case Config::ProfilerDirection::None:
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Profiler) {
|
||||
Close();
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_DESTROY(Profiler) {
|
||||
Close();
|
||||
}
|
||||
|
||||
} // namespace Profiler
|
||||
@@ -2,7 +2,6 @@
|
||||
#define KYTY_COMMON_PROFILER_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/subsystems.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
@@ -42,7 +41,15 @@ private:
|
||||
void EndBlock();
|
||||
void SetThreadName(const char* name);
|
||||
|
||||
KYTY_SUBSYSTEM_DEFINE(Profiler);
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
|
||||
struct Lifecycle {
|
||||
static constexpr const char* name = "Profiler";
|
||||
static constexpr auto initialize = Profiler::Initialize;
|
||||
static constexpr auto shutdown = Profiler::Shutdown;
|
||||
static constexpr auto emergency_shutdown = Profiler::Shutdown;
|
||||
};
|
||||
|
||||
} // namespace Profiler
|
||||
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
#ifndef EMULATOR_SRC_COMMON_SLOTVECTOR_H_
|
||||
#define EMULATOR_SRC_COMMON_SLOTVECTOR_H_
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/abi.h"
|
||||
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace Common {
|
||||
|
||||
struct SlotId {
|
||||
static constexpr uint32_t INVALID_INDEX = std::numeric_limits<uint32_t>::max();
|
||||
|
||||
constexpr SlotId() noexcept = default;
|
||||
constexpr SlotId(uint32_t value) noexcept: index(value), generation(1) {}
|
||||
constexpr SlotId(uint32_t value, uint32_t slot_generation) noexcept
|
||||
: index(value), generation(slot_generation) {}
|
||||
|
||||
[[nodiscard]] constexpr explicit operator bool() const noexcept { return index != INVALID_INDEX; }
|
||||
constexpr auto operator<=>(const SlotId&) const noexcept = default;
|
||||
|
||||
uint32_t index = INVALID_INDEX;
|
||||
uint32_t generation = 0;
|
||||
};
|
||||
|
||||
// Stable-address slot storage for cache resources that are intentionally non-movable.
|
||||
template <typename T>
|
||||
class SlotVector {
|
||||
public:
|
||||
SlotVector() = default;
|
||||
KYTY_CLASS_NO_COPY(SlotVector);
|
||||
|
||||
[[nodiscard]] T& operator[](SlotId id) noexcept {
|
||||
EXIT_IF(!is_allocated(id));
|
||||
return *m_values[id.index].value;
|
||||
}
|
||||
|
||||
[[nodiscard]] const T& operator[](SlotId id) const noexcept {
|
||||
EXIT_IF(!is_allocated(id));
|
||||
return *m_values[id.index].value;
|
||||
}
|
||||
|
||||
[[nodiscard]] T* try_get(SlotId id) noexcept {
|
||||
return is_allocated(id) ? &*m_values[id.index].value : nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] const T* try_get(SlotId id) const noexcept {
|
||||
return is_allocated(id) ? &*m_values[id.index].value : nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool is_allocated(SlotId id) const noexcept {
|
||||
return id && id.index < m_values.size() &&
|
||||
m_values[id.index].generation == id.generation && m_values[id.index].value.has_value();
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
[[nodiscard]] SlotId insert(Args&&... args) {
|
||||
uint32_t index = 0;
|
||||
if (m_free_list.empty()) {
|
||||
index = static_cast<uint32_t>(m_values.size());
|
||||
m_values.emplace_back();
|
||||
m_values.back().value.emplace(std::forward<Args>(args)...);
|
||||
} else {
|
||||
index = m_free_list.back();
|
||||
m_free_list.pop_back();
|
||||
EXIT_IF(m_values[index].value.has_value());
|
||||
m_values[index].value.emplace(std::forward<Args>(args)...);
|
||||
}
|
||||
++m_size;
|
||||
return SlotId {index, m_values[index].generation};
|
||||
}
|
||||
|
||||
void erase(SlotId id) noexcept {
|
||||
EXIT_IF(!is_allocated(id));
|
||||
auto& slot = m_values[id.index];
|
||||
slot.value.reset();
|
||||
if (++slot.generation == 0) {
|
||||
slot.generation = 1;
|
||||
}
|
||||
m_free_list.push_back(id.index);
|
||||
--m_size;
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t size() const noexcept { return m_size; }
|
||||
[[nodiscard]] size_t capacity() const noexcept { return m_values.size(); }
|
||||
|
||||
template <typename F>
|
||||
void ForEach(F&& fn) {
|
||||
for (uint32_t index = 0; index < m_values.size(); ++index) {
|
||||
if (m_values[index].value) {
|
||||
fn(SlotId {index, m_values[index].generation}, *m_values[index].value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void ForEach(F&& fn) const {
|
||||
for (uint32_t index = 0; index < m_values.size(); ++index) {
|
||||
if (m_values[index].value) {
|
||||
fn(SlotId {index, m_values[index].generation}, *m_values[index].value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct Slot {
|
||||
std::optional<T> value;
|
||||
uint32_t generation = 1;
|
||||
};
|
||||
|
||||
std::deque<Slot> m_values;
|
||||
std::vector<uint32_t> m_free_list;
|
||||
size_t m_size = 0;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
template <>
|
||||
struct std::hash<Common::SlotId> {
|
||||
[[nodiscard]] size_t operator()(Common::SlotId id) const noexcept {
|
||||
return std::hash<uint64_t> {}((static_cast<uint64_t>(id.generation) << 32u) | id.index);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // EMULATOR_SRC_COMMON_SLOTVECTOR_H_
|
||||
@@ -316,10 +316,12 @@ inline std::string SafeCsv(std::string_view text) {
|
||||
return std::string(text);
|
||||
}
|
||||
|
||||
inline std::string Utf16ToUtf8(const char16_t* utf16) {
|
||||
std::u16string input(utf16);
|
||||
inline std::string Utf16ToUtf8(std::u16string_view utf16) {
|
||||
if (utf16.empty()) {
|
||||
return {};
|
||||
}
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
|
||||
return convert.to_bytes(input);
|
||||
return convert.to_bytes(utf16.data(), utf16.data() + utf16.size());
|
||||
}
|
||||
|
||||
inline ByteBuffer HexToBin(std::string_view text) {
|
||||
|
||||
+25
-214
@@ -1,232 +1,43 @@
|
||||
#include "common/subsystems.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace Common {
|
||||
|
||||
class SubsystemPrivate {
|
||||
public:
|
||||
SubsystemPrivate() = default;
|
||||
virtual ~SubsystemPrivate() = default;
|
||||
static Subsystems* g_active_subsystems = nullptr;
|
||||
|
||||
KYTY_CLASS_NO_COPY(SubsystemPrivate);
|
||||
Subsystems::Subsystems(bool print): m_print(print) {
|
||||
m_active.reserve(16);
|
||||
g_active_subsystems = this;
|
||||
}
|
||||
|
||||
bool failed {false};
|
||||
std::string fail_msg;
|
||||
};
|
||||
|
||||
class SubsystemsListPrivate {
|
||||
public:
|
||||
explicit SubsystemsListPrivate(SubsystemsList& p): parent(p) {}
|
||||
|
||||
virtual ~SubsystemsListPrivate() = default;
|
||||
|
||||
void SetArgs(int argc, char** argv) {
|
||||
this->m_argc = argc;
|
||||
this->m_argv = argv;
|
||||
Subsystems::~Subsystems() {
|
||||
Destroy();
|
||||
if (g_active_subsystems == this) {
|
||||
g_active_subsystems = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Add(Subsystem* s, std::initializer_list<Subsystem*> deps) {
|
||||
EXIT_IF(!s);
|
||||
|
||||
const char* name = s->Id();
|
||||
|
||||
EXIT_IF(FindByName(name) != nullptr);
|
||||
|
||||
auto nl = std::make_unique<SubsListStruct>();
|
||||
|
||||
nl->s = s;
|
||||
nl->name = name;
|
||||
nl->deps = nullptr;
|
||||
nl->next = std::move(list);
|
||||
nl->prev_init = nullptr;
|
||||
|
||||
auto* node = nl.get();
|
||||
list = std::move(nl);
|
||||
|
||||
for (auto* dep: deps) {
|
||||
const char* str = dep->Id();
|
||||
|
||||
auto l = std::make_unique<DepsListStruct>();
|
||||
l->dep_name = str;
|
||||
l->next = std::move(node->deps);
|
||||
|
||||
node->deps = std::move(l);
|
||||
void Subsystems::Destroy() {
|
||||
for (auto it = m_active.rbegin(); it != m_active.rend(); ++it) {
|
||||
if (it->shutdown != nullptr) {
|
||||
it->shutdown();
|
||||
}
|
||||
|
||||
node->initialized = false;
|
||||
}
|
||||
m_active.clear();
|
||||
}
|
||||
|
||||
bool InitAll(bool print_msg) {
|
||||
while (SubsListStruct* n = FindNextToInitialize()) {
|
||||
n->s->Init(&parent);
|
||||
|
||||
if (n->s->m_p->failed) {
|
||||
fail_msg = n->s->m_p->fail_msg.c_str();
|
||||
fail_name = n->name;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (print_msg) {
|
||||
printf("Initialized: %s\n", n->name);
|
||||
}
|
||||
|
||||
n->initialized = true;
|
||||
|
||||
SubsListStruct* last = last_init;
|
||||
last_init = n;
|
||||
n->prev_init = last;
|
||||
void Subsystems::EmergencyShutdown() {
|
||||
for (auto it = m_active.rbegin(); it != m_active.rend(); ++it) {
|
||||
if (it->emergency_shutdown != nullptr) {
|
||||
it->emergency_shutdown();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
m_active.clear();
|
||||
}
|
||||
|
||||
void DestroyAll(bool print_msg) {
|
||||
for (SubsListStruct* n = last_init; n != nullptr; n = n->prev_init) {
|
||||
n->s->Destroy(&parent);
|
||||
n->initialized = false;
|
||||
|
||||
if (print_msg) {
|
||||
printf("Destroyed: %s\n", n->name);
|
||||
}
|
||||
}
|
||||
|
||||
last_init = nullptr;
|
||||
void Subsystems::EmergencyShutdownActive() {
|
||||
if (g_active_subsystems != nullptr) {
|
||||
g_active_subsystems->EmergencyShutdown();
|
||||
}
|
||||
|
||||
void ShutdownAll() {
|
||||
for (SubsListStruct* n = last_init; n != nullptr; n = n->prev_init) {
|
||||
n->s->UnexpectedShutdown(&parent);
|
||||
n->initialized = false;
|
||||
}
|
||||
|
||||
last_init = nullptr;
|
||||
}
|
||||
|
||||
struct DepsListStruct {
|
||||
const char* dep_name;
|
||||
std::unique_ptr<DepsListStruct> next;
|
||||
};
|
||||
|
||||
struct SubsListStruct {
|
||||
Subsystem* s;
|
||||
const char* name;
|
||||
std::unique_ptr<DepsListStruct> deps;
|
||||
std::unique_ptr<SubsListStruct> next;
|
||||
SubsListStruct* prev_init;
|
||||
bool initialized;
|
||||
};
|
||||
|
||||
[[nodiscard]] SubsListStruct* FindByName(const char* name) const {
|
||||
for (SubsListStruct* n = list.get(); n != nullptr; n = n->next.get()) {
|
||||
if (std::strcmp(n->name, name) == 0) {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] SubsListStruct* FindNextToInitialize() const {
|
||||
for (SubsListStruct* n = list.get(); n != nullptr; n = n->next.get()) {
|
||||
if (n->initialized) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DepsListStruct* d = n->deps.get();
|
||||
for (; d != nullptr; d = d->next.get()) {
|
||||
SubsListStruct* s = FindByName(d->dep_name);
|
||||
if ((s == nullptr) || !s->initialized) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (d == nullptr) {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
KYTY_CLASS_NO_COPY(SubsystemsListPrivate);
|
||||
|
||||
std::unique_ptr<SubsListStruct> list;
|
||||
SubsListStruct* last_init = nullptr;
|
||||
int m_argc = 0;
|
||||
char** m_argv = nullptr;
|
||||
const char* fail_msg = nullptr;
|
||||
const char* fail_name = nullptr;
|
||||
SubsystemsList& parent;
|
||||
};
|
||||
|
||||
SubsystemsList::SubsystemsList(): m_p(std::make_unique<SubsystemsListPrivate>(*this)) {}
|
||||
|
||||
SubsystemsList::~SubsystemsList() = default;
|
||||
|
||||
void SubsystemsList::Add(Subsystem* s, std::initializer_list<Subsystem*> deps) {
|
||||
m_p->Add(s, deps);
|
||||
}
|
||||
|
||||
bool SubsystemsList::InitAll(bool print_msg) {
|
||||
return m_p->InitAll(print_msg);
|
||||
}
|
||||
|
||||
void SubsystemsList::DestroyAll(bool print_msg) {
|
||||
m_p->DestroyAll(print_msg);
|
||||
}
|
||||
|
||||
int* SubsystemsList::GetArgc() {
|
||||
return &m_p->m_argc;
|
||||
}
|
||||
|
||||
char** SubsystemsList::GetArgv() {
|
||||
return m_p->m_argv;
|
||||
}
|
||||
|
||||
Subsystem::Subsystem(): m_p(std::make_unique<SubsystemPrivate>()) {}
|
||||
|
||||
Subsystem::~Subsystem() = default;
|
||||
|
||||
void Subsystem::Fail(const char* format, ...) {
|
||||
va_list args {};
|
||||
va_start(args, format);
|
||||
|
||||
va_list args_copy {};
|
||||
va_copy(args_copy, args);
|
||||
int len = std::vsnprintf(nullptr, 0, format, args_copy);
|
||||
va_end(args_copy);
|
||||
|
||||
if (len > 0) {
|
||||
std::string msg(static_cast<size_t>(len) + 1, '\0');
|
||||
std::vsnprintf(msg.data(), msg.size(), format, args);
|
||||
m_p->fail_msg = msg.c_str();
|
||||
m_p->failed = true;
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
const char* SubsystemsList::GetFailName() const {
|
||||
return m_p->fail_name;
|
||||
}
|
||||
|
||||
const char* SubsystemsList::GetFailMsg() const {
|
||||
return m_p->fail_msg;
|
||||
}
|
||||
|
||||
void SubsystemsList::SetArgs(int argc, char* argv[]) {
|
||||
m_p->SetArgs(argc, argv);
|
||||
}
|
||||
|
||||
void SubsystemsList::ShutdownAll() {
|
||||
m_p->ShutdownAll();
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
+41
-70
@@ -2,90 +2,61 @@
|
||||
#define KYTY_COMMON_SUBSYSTEMS_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/singleton.h"
|
||||
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
|
||||
namespace Common {
|
||||
|
||||
class Subsystem;
|
||||
class SubsystemsListPrivate;
|
||||
class SubsystemPrivate;
|
||||
|
||||
class SubsystemsList {
|
||||
class Subsystems {
|
||||
public:
|
||||
SubsystemsList();
|
||||
virtual ~SubsystemsList();
|
||||
void SetArgs(int argc, char* argv[]);
|
||||
using Callback = void (*)();
|
||||
|
||||
// void Add(Subsystem* s, const char* name, ...);
|
||||
void Add(Subsystem* s, std::initializer_list<Subsystem*> deps);
|
||||
explicit Subsystems(bool print = false);
|
||||
~Subsystems();
|
||||
|
||||
bool InitAll(bool print_msg = false);
|
||||
void DestroyAll(bool print_msg = false);
|
||||
template <typename Lifecycle>
|
||||
void Initialize() {
|
||||
Lifecycle::initialize();
|
||||
m_active.push_back({ShutdownCallback<Lifecycle>(), EmergencyCallback<Lifecycle>()});
|
||||
if (m_print) {
|
||||
std::printf("Initialized: %s\n", Lifecycle::name);
|
||||
}
|
||||
}
|
||||
|
||||
int* GetArgc();
|
||||
char** GetArgv();
|
||||
void Destroy();
|
||||
void EmergencyShutdown();
|
||||
|
||||
[[nodiscard]] const char* GetFailName() const;
|
||||
[[nodiscard]] const char* GetFailMsg() const;
|
||||
static void EmergencyShutdownActive();
|
||||
|
||||
void ShutdownAll();
|
||||
|
||||
static SubsystemsList* Instance() { return Common::Singleton<SubsystemsList>::Instance(); }
|
||||
|
||||
KYTY_CLASS_NO_COPY(SubsystemsList);
|
||||
KYTY_CLASS_NO_COPY(Subsystems);
|
||||
|
||||
private:
|
||||
std::unique_ptr<SubsystemsListPrivate> m_p;
|
||||
template <typename Lifecycle>
|
||||
static consteval Callback ShutdownCallback() {
|
||||
if constexpr (requires { Lifecycle::shutdown; }) {
|
||||
return Lifecycle::shutdown;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Lifecycle>
|
||||
static consteval Callback EmergencyCallback() {
|
||||
if constexpr (requires { Lifecycle::emergency_shutdown; }) {
|
||||
return Lifecycle::emergency_shutdown;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct Entry {
|
||||
Callback shutdown;
|
||||
Callback emergency_shutdown;
|
||||
};
|
||||
|
||||
std::vector<Entry> m_active;
|
||||
bool m_print;
|
||||
};
|
||||
|
||||
using SubsystemsListSingleton = Common::Singleton<SubsystemsList>;
|
||||
|
||||
class Subsystem {
|
||||
public:
|
||||
Subsystem();
|
||||
virtual ~Subsystem();
|
||||
|
||||
virtual const char* Id() = 0;
|
||||
virtual void Init(SubsystemsList* parent) = 0;
|
||||
virtual void Destroy(SubsystemsList* parent) = 0;
|
||||
virtual void UnexpectedShutdown(SubsystemsList* parent) = 0;
|
||||
|
||||
friend class SubsystemsListPrivate;
|
||||
|
||||
KYTY_CLASS_NO_COPY(Subsystem);
|
||||
|
||||
protected:
|
||||
void Fail(const char* format, ...) KYTY_FORMAT_PRINTF(2, 3);
|
||||
|
||||
private:
|
||||
std::unique_ptr<SubsystemPrivate> m_p;
|
||||
};
|
||||
|
||||
#define KYTY_SUBSYSTEM_DEFINE(s) \
|
||||
class s##Subsystem: public Common::Subsystem { \
|
||||
public: \
|
||||
static Subsystem* Instance() { return Common::Singleton<s##Subsystem>::Instance(); } \
|
||||
const char* Id() { return #s; } \
|
||||
void Init(Common::SubsystemsList* parent); \
|
||||
void Destroy(Common::SubsystemsList* parent); \
|
||||
void UnexpectedShutdown(Common::SubsystemsList* parent); \
|
||||
}; \
|
||||
typedef Common::Singleton<s##Subsystem> s##SubsystemSingleton;
|
||||
|
||||
#define KYTY_SUBSYSTEM_INIT(s) \
|
||||
void s##Subsystem::Init([[maybe_unused]] Common::SubsystemsList* parent)
|
||||
#define KYTY_SUBSYSTEM_DESTROY(s) \
|
||||
void s##Subsystem::Destroy([[maybe_unused]] Common::SubsystemsList* parent)
|
||||
#define KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(s) \
|
||||
void s##Subsystem::UnexpectedShutdown([[maybe_unused]] Common::SubsystemsList* parent)
|
||||
|
||||
// #define KYTY_SUBSYSTEM_ADD(list, s, ...) list.push_back(s##SubsystemSingleton::Instance(), #s,
|
||||
// __VA_ARGS__); #define KYTY_SUBSYSTEM_ADD2(list, ...) list.Add2(s##SubsystemSingleton::Instance(),
|
||||
// __VA_ARGS__);
|
||||
|
||||
} // namespace Common
|
||||
|
||||
#endif /* KYTY_COMMON_SUBSYSTEMS_H_ */
|
||||
+9
-52
@@ -1,7 +1,6 @@
|
||||
#include "common/threads.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
@@ -9,7 +8,6 @@
|
||||
#include <chrono> // IWYU pragma: keep
|
||||
#include <condition_variable> // IWYU pragma: keep
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS && KYTY_COMPILER == KYTY_COMPILER_CLANG
|
||||
#define KYTY_WIN_CS
|
||||
@@ -198,37 +196,7 @@ struct CondVarPrivate {
|
||||
#endif
|
||||
};
|
||||
|
||||
static std::recursive_mutex g_cond_waiters_mutex;
|
||||
static std::vector<std::pair<int, CondVarPrivate*>> g_cond_waiters;
|
||||
static wait_poll_func_t g_cond_wait_poll_callback = nullptr;
|
||||
|
||||
static void WakeCondVar(CondVarPrivate* cond_var) {
|
||||
#ifdef KYTY_WIN_CS
|
||||
static auto func = ResolveWakeAllConditionVariable();
|
||||
EXIT_NOT_IMPLEMENTED(func == nullptr);
|
||||
func(&cond_var->m_cv);
|
||||
#else
|
||||
cond_var->m_cv.notify_all();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void RegisterCondWaiter(CondVarPrivate* cond_var) {
|
||||
std::lock_guard lock(g_cond_waiters_mutex);
|
||||
g_cond_waiters.emplace_back(Thread::GetThreadIdUnique(), cond_var);
|
||||
}
|
||||
|
||||
static void UnregisterCondWaiter(CondVarPrivate* cond_var) {
|
||||
const auto thread_id = Thread::GetThreadIdUnique();
|
||||
std::lock_guard lock(g_cond_waiters_mutex);
|
||||
|
||||
const auto it = std::find_if(g_cond_waiters.begin(), g_cond_waiters.end(),
|
||||
[thread_id, cond_var](const auto& waiter) {
|
||||
return waiter.first == thread_id && waiter.second == cond_var;
|
||||
});
|
||||
if (it != g_cond_waiters.end()) {
|
||||
g_cond_waiters.erase(it);
|
||||
}
|
||||
}
|
||||
static wait_poll_func_t g_cond_wait_poll_callback = nullptr;
|
||||
|
||||
struct ThreadPrivate {
|
||||
ThreadPrivate(thread_func_t f, void* a): func(f), arg(a), m_thread(&Run, this) {}
|
||||
@@ -252,15 +220,11 @@ static thread_id_t g_main_thread;
|
||||
static int g_main_thread_int;
|
||||
static std::atomic<int> g_thread_counter = 0;
|
||||
|
||||
KYTY_SUBSYSTEM_INIT(Threads) {
|
||||
void InitializeThreads() {
|
||||
g_main_thread = std::this_thread::get_id();
|
||||
g_main_thread_int = Thread::GetThreadIdUnique();
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Threads) {}
|
||||
|
||||
KYTY_SUBSYSTEM_DESTROY(Threads) {}
|
||||
|
||||
Thread::Thread(thread_func_t func, void* arg)
|
||||
: m_thread(std::make_unique<ThreadPrivate>(func, arg)) {
|
||||
while (!m_thread->started) {
|
||||
@@ -370,7 +334,6 @@ CondVar::~CondVar() {
|
||||
}
|
||||
|
||||
void CondVar::Wait(Mutex* mutex) {
|
||||
RegisterCondWaiter(m_cond_var.get());
|
||||
#ifndef KYTY_WIN_CS
|
||||
std::unique_lock<std::recursive_mutex> cpp_lock(mutex->m_mutex->m_mutex, std::adopt_lock_t());
|
||||
#endif
|
||||
@@ -411,7 +374,6 @@ void CondVar::Wait(Mutex* mutex) {
|
||||
}
|
||||
cpp_lock.release();
|
||||
#endif
|
||||
UnregisterCondWaiter(m_cond_var.get());
|
||||
}
|
||||
|
||||
void CondVar::SetWaitPollCallback(wait_poll_func_t callback) {
|
||||
@@ -420,7 +382,6 @@ void CondVar::SetWaitPollCallback(wait_poll_func_t callback) {
|
||||
|
||||
bool CondVar::WaitFor(Mutex* mutex, uint32_t micros) {
|
||||
bool ok = false;
|
||||
RegisterCondWaiter(m_cond_var.get());
|
||||
#ifndef KYTY_WIN_CS
|
||||
std::unique_lock<std::recursive_mutex> cpp_lock(mutex->m_mutex->m_mutex, std::adopt_lock_t());
|
||||
#endif
|
||||
@@ -435,7 +396,6 @@ bool CondVar::WaitFor(Mutex* mutex, uint32_t micros) {
|
||||
std::cv_status::no_timeout);
|
||||
cpp_lock.release();
|
||||
#endif
|
||||
UnregisterCondWaiter(m_cond_var.get());
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -450,16 +410,13 @@ void CondVar::Signal() {
|
||||
}
|
||||
|
||||
void CondVar::SignalAll() {
|
||||
WakeCondVar(m_cond_var.get());
|
||||
}
|
||||
|
||||
void CondVar::SignalThread(int thread_id) {
|
||||
std::lock_guard lock(g_cond_waiters_mutex);
|
||||
for (const auto& waiter: g_cond_waiters) {
|
||||
if (waiter.first == thread_id) {
|
||||
WakeCondVar(waiter.second);
|
||||
}
|
||||
}
|
||||
#ifdef KYTY_WIN_CS
|
||||
static auto func = ResolveWakeAllConditionVariable();
|
||||
EXIT_NOT_IMPLEMENTED(func == nullptr);
|
||||
func(&m_cond_var->m_cv);
|
||||
#else
|
||||
m_cond_var->m_cv.notify_all();
|
||||
#endif
|
||||
}
|
||||
|
||||
int Thread::GetThreadIdUnique() {
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
#define KYTY_COMMON_THREADS_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/subsystems.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace Common {
|
||||
|
||||
KYTY_SUBSYSTEM_DEFINE(Threads);
|
||||
void InitializeThreads();
|
||||
|
||||
using thread_func_t = void (*)(void*);
|
||||
using wait_poll_func_t = void (*)();
|
||||
@@ -78,7 +77,6 @@ public:
|
||||
void Signal();
|
||||
void SignalAll();
|
||||
|
||||
static void SignalThread(int thread_id);
|
||||
static void SetWaitPollCallback(wait_poll_func_t callback);
|
||||
|
||||
KYTY_CLASS_NO_COPY(CondVar);
|
||||
|
||||
@@ -50,8 +50,8 @@ bool FreeRange(uint64_t address, uint64_t size) {
|
||||
return SysVirtualFreeRange(address, size);
|
||||
}
|
||||
|
||||
bool Protect(uint64_t address, uint64_t size, Mode mode, Mode* old_mode) {
|
||||
return SysVirtualProtect(address, size, mode, old_mode);
|
||||
bool Protect(uint64_t address, uint64_t size, Mode mode) {
|
||||
return SysVirtualProtect(address, size, mode);
|
||||
}
|
||||
|
||||
bool FlushInstructionCache(uint64_t address, uint64_t size) {
|
||||
|
||||
@@ -35,7 +35,7 @@ bool ReserveFixed(uint64_t address, uint64_t size);
|
||||
bool Decommit(uint64_t address, uint64_t size);
|
||||
bool Free(uint64_t address);
|
||||
bool FreeRange(uint64_t address, uint64_t size);
|
||||
bool Protect(uint64_t address, uint64_t size, Mode mode, Mode* old_mode = nullptr);
|
||||
bool Protect(uint64_t address, uint64_t size, Mode mode);
|
||||
bool FlushInstructionCache(uint64_t address, uint64_t size);
|
||||
|
||||
} // namespace VirtualMemory
|
||||
|
||||
+43
-38
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/commonSubsystem.h"
|
||||
#include "common/emulatorConfig.h"
|
||||
#include "common/file.h"
|
||||
#include "common/logging/log.h"
|
||||
@@ -16,6 +15,7 @@
|
||||
#include "kernel/fileSystem.h"
|
||||
#include "kernel/memory.h"
|
||||
#include "kernel/pthread.h"
|
||||
#include "kytyGitVersion.h"
|
||||
#include "libs/agc.h"
|
||||
#include "libs/audio.h"
|
||||
#include "libs/controller.h"
|
||||
@@ -27,13 +27,31 @@
|
||||
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <thread>
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
static void PrintSystemInfo() {
|
||||
Common::SystemInfo info = Common::GetSystemInfo();
|
||||
const Common::SystemInfo info = Common::GetSystemInfo();
|
||||
|
||||
LOGF("ProcessorName = %s\n", info.ProcessorName.c_str());
|
||||
#if defined(__APPLE__)
|
||||
static constexpr auto platform_name = "macOS";
|
||||
#elif KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
static constexpr auto platform_name = "Windows";
|
||||
#elif KYTY_PLATFORM == KYTY_PLATFORM_LINUX
|
||||
static constexpr auto platform_name = "Linux";
|
||||
#else
|
||||
static constexpr auto platform_name = "Unknown";
|
||||
#endif
|
||||
|
||||
LOGF("Build\n"
|
||||
" version: %s\n\n"
|
||||
"Host\n"
|
||||
" os: %s\n"
|
||||
" cpu: %s\n"
|
||||
" threads: %u\n\n",
|
||||
KYTY_BUILD_LABEL, platform_name, info.ProcessorName.c_str(),
|
||||
std::thread::hardware_concurrency());
|
||||
}
|
||||
|
||||
static void KytyClose() {
|
||||
@@ -43,7 +61,7 @@ static void KytyClose() {
|
||||
|
||||
LOGF("done!\n");
|
||||
|
||||
Common::SubsystemsListSingleton::Instance()->ShutdownAll();
|
||||
Common::Subsystems::EmergencyShutdownActive();
|
||||
}
|
||||
|
||||
static void MountOrCreateDir(const std::filesystem::path& dir, const std::string& point) {
|
||||
@@ -105,30 +123,13 @@ static void ClearDebugTextureFolder() {
|
||||
}
|
||||
}
|
||||
|
||||
static void Init(const Config::ConfigOptions& cfg, const std::filesystem::path& param_json) {
|
||||
static void Init(const Config::ConfigOptions& cfg, const std::filesystem::path& param_json,
|
||||
Common::Subsystems& subsystems) {
|
||||
EXIT_IF(!Common::Thread::IsMainThread());
|
||||
|
||||
auto* slist = Common::SubsystemsList::Instance();
|
||||
|
||||
auto* audio = Libs::Audio::AudioSubsystem::Instance();
|
||||
auto* config = Config::ConfigSubsystem::Instance();
|
||||
auto* controller = Libs::Controller::ControllerSubsystem::Instance();
|
||||
auto* core = Common::CommonSubsystem::Instance();
|
||||
auto* file_system = Libs::LibKernel::FileSystem::FileSystemSubsystem::Instance();
|
||||
auto* graphics = Libs::Graphics::GraphicsSubsystem::Instance();
|
||||
auto* log = Log::LogSubsystem::Instance();
|
||||
auto* memory = Libs::LibKernel::Memory::MemorySubsystem::Instance();
|
||||
auto* network = Libs::Network::NetworkSubsystem::Instance();
|
||||
auto* profiler = Profiler::ProfilerSubsystem::Instance();
|
||||
auto* pthread = Libs::LibKernel::PthreadSubsystem::Instance();
|
||||
auto* timer = Loader::Timer::TimerSubsystem::Instance();
|
||||
|
||||
slist->Add(config, {core});
|
||||
slist->InitAll(true);
|
||||
|
||||
subsystems.Initialize<Config::Lifecycle>();
|
||||
Config::Load(cfg);
|
||||
slist->Add(log, {core, config});
|
||||
slist->InitAll(true);
|
||||
subsystems.Initialize<Log::Lifecycle>();
|
||||
|
||||
if (Common::File::IsFileExisting(param_json)) {
|
||||
Loader::SystemContentLoadParamSfo(param_json);
|
||||
@@ -138,17 +139,16 @@ static void Init(const Config::ConfigOptions& cfg, const std::filesystem::path&
|
||||
}
|
||||
}
|
||||
|
||||
slist->Add(audio, {core, log, pthread, memory});
|
||||
slist->Add(controller, {core, log, config});
|
||||
slist->Add(file_system, {core, log, pthread});
|
||||
slist->Add(graphics, {core, log, pthread, memory, config, profiler, controller});
|
||||
slist->Add(memory, {core, log});
|
||||
slist->Add(network, {core, log, pthread});
|
||||
slist->Add(profiler, {core, config});
|
||||
slist->Add(pthread, {core, log, timer});
|
||||
slist->Add(timer, {core, log});
|
||||
|
||||
slist->InitAll(true);
|
||||
// Initialization order is explicit; destruction is automatic and reversed.
|
||||
subsystems.Initialize<Loader::Timer::Lifecycle>();
|
||||
subsystems.Initialize<Libs::LibKernel::PthreadLifecycle>();
|
||||
subsystems.Initialize<Profiler::Lifecycle>();
|
||||
subsystems.Initialize<Libs::Network::Lifecycle>();
|
||||
subsystems.Initialize<Libs::LibKernel::Memory::Lifecycle>();
|
||||
subsystems.Initialize<Libs::LibKernel::FileSystem::Lifecycle>();
|
||||
subsystems.Initialize<Libs::Controller::Lifecycle>();
|
||||
subsystems.Initialize<Libs::Audio::Lifecycle>();
|
||||
subsystems.Initialize<Libs::Graphics::Lifecycle>();
|
||||
}
|
||||
|
||||
static void LoadElf(const std::filesystem::path& elf, bool dbg_print_reloc = false,
|
||||
@@ -189,8 +189,9 @@ void Run(const RunOptions& options) {
|
||||
EXIT("ELF is required\n");
|
||||
}
|
||||
|
||||
const auto param_json = options.app0_dir / "sce_sys" / "param.json";
|
||||
Init(options.config, param_json);
|
||||
const auto param_json = options.app0_dir / "sce_sys" / "param.json";
|
||||
Common::Subsystems subsystems(true);
|
||||
Init(options.config, param_json, subsystems);
|
||||
|
||||
ClearDebugTextureFolder();
|
||||
|
||||
@@ -199,6 +200,10 @@ void Run(const RunOptions& options) {
|
||||
int ok = atexit(KytyClose);
|
||||
EXIT_NOT_IMPLEMENTED(ok != 0);
|
||||
|
||||
// Guest threads are still running, so skip KytyClose() and only flush emergency state.
|
||||
ok = at_quick_exit(Common::Subsystems::EmergencyShutdownActive);
|
||||
EXIT_NOT_IMPLEMENTED(ok != 0);
|
||||
|
||||
Libs::LibKernel::FileSystem::Mount(options.app0_dir, "/app0");
|
||||
Libs::LibKernel::FileSystem::Mount(options.app0_dir, "/hostapp");
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
set(KYTY_GIT_VERSION "unknown")
|
||||
set(KYTY_GIT_HASH "unknown")
|
||||
set(KYTY_GIT_REVISION "unknown")
|
||||
if(GIT_EXECUTABLE)
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" describe --tags --always --dirty
|
||||
@@ -14,16 +15,18 @@ if(GIT_EXECUTABLE)
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" rev-parse --short=7 HEAD
|
||||
COMMAND "${GIT_EXECUTABLE}" rev-parse HEAD
|
||||
WORKING_DIRECTORY "${GIT_WORKING_DIRECTORY}"
|
||||
OUTPUT_VARIABLE KYTY_GIT_HASH
|
||||
OUTPUT_VARIABLE KYTY_GIT_REVISION
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
RESULT_VARIABLE GIT_HASH_RESULT
|
||||
ERROR_QUIET
|
||||
)
|
||||
if(NOT GIT_HASH_RESULT EQUAL 0)
|
||||
set(KYTY_GIT_HASH "unknown")
|
||||
set(KYTY_GIT_REVISION "unknown")
|
||||
else()
|
||||
string(SUBSTRING "${KYTY_GIT_REVISION}" 0 7 KYTY_GIT_HASH)
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD --
|
||||
WORKING_DIRECTORY "${GIT_WORKING_DIRECTORY}"
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
@@ -30,10 +31,9 @@ private:
|
||||
friend class CommandProcessor;
|
||||
|
||||
struct BufferCursor {
|
||||
uint32_t* next_packet = nullptr;
|
||||
uint32_t remaining_dw = 0;
|
||||
uint32_t total_dw = 0;
|
||||
uint32_t deferred_advance_dw = 0;
|
||||
std::span<const uint32_t> commands;
|
||||
uint32_t offset_dw = 0;
|
||||
uint32_t deferred_advance_dw = 0;
|
||||
};
|
||||
|
||||
std::vector<BufferCursor> m_buffer_stack;
|
||||
@@ -50,7 +50,8 @@ public:
|
||||
int64_t flip_arg = 0;
|
||||
};
|
||||
|
||||
explicit CommandProcessor(RenderContext& renderer): m_renderer(renderer) {}
|
||||
CommandProcessor(RenderContext& renderer, int interrupt_event_id)
|
||||
: m_renderer(renderer), m_interrupt_event_id(interrupt_event_id) {}
|
||||
~CommandProcessor() = default;
|
||||
|
||||
KYTY_CLASS_NO_COPY(CommandProcessor);
|
||||
@@ -72,13 +73,9 @@ public:
|
||||
void SetDrawIndirectArgsBaseAddress(uint64_t draw_indirect_args_base_addr);
|
||||
void SetDispatchIndirectArgsBaseAddress(uint64_t dispatch_indirect_args_base_addr);
|
||||
void SetNumInstances(uint32_t num_instances);
|
||||
void DrawIndex(uint32_t index_count, const void* index_addr, uint32_t flags, uint32_t type,
|
||||
uint32_t instance_count = 0, const void* object_ids = nullptr,
|
||||
uint32_t render_target_slice_offset = 0, int32_t vertex_offset_add = 0,
|
||||
uint32_t first_instance = 0);
|
||||
void DrawIndexOffset(uint32_t index_offset, uint32_t index_count, uint32_t flags);
|
||||
void DrawIndexAuto(uint32_t index_count, uint32_t flags,
|
||||
uint32_t render_target_slice_offset = 0);
|
||||
void DrawIndex(DrawIndexArgs args);
|
||||
void DrawIndexOffset(uint32_t index_offset, uint32_t index_count);
|
||||
void DrawIndexAuto(DrawAutoArgs args);
|
||||
void DrawIndirect(uint32_t data_offset, uint32_t draw_initiator, bool indexed);
|
||||
void DrawIndirectMulti(uint32_t data_offset, uint32_t max_count_or_count,
|
||||
const volatile uint32_t* count_addr, uint32_t stride_in_bytes,
|
||||
@@ -103,7 +100,7 @@ public:
|
||||
uint32_t mode);
|
||||
void DispatchIndirect(uint32_t data_offset, uint32_t mode);
|
||||
void WaitFlipDone(uint32_t video_out_handle, uint32_t display_buffer_index);
|
||||
void TriggerEvent(uint32_t event_type, uint32_t event_index);
|
||||
void TriggerEvent(uint32_t event_type, uint32_t event_index, uint64_t event_address = 0);
|
||||
|
||||
void SetUserDataMarker(HW::UserSgprType type) { m_user_data_marker = type; }
|
||||
[[nodiscard]] HW::UserSgprType GetUserDataMarker() const { return m_user_data_marker; }
|
||||
@@ -112,6 +109,7 @@ public:
|
||||
void SetCeComplete(bool complete) { m_ce_complete = complete; }
|
||||
void WaitCe();
|
||||
void WaitDeDiff(uint32_t diff);
|
||||
void WaitForRewind(bool valid);
|
||||
void IncrementDe();
|
||||
void IncrementCe();
|
||||
|
||||
@@ -130,13 +128,14 @@ public:
|
||||
const volatile void* address, uint32_t count_in_dwords);
|
||||
[[nodiscard]] bool ShouldSkipPredicatedPackets() const { return m_predicate_skip; }
|
||||
|
||||
Pm4ProcessResult Process(Pm4Execution& execution, uint32_t* buffer, uint32_t size_dw);
|
||||
void ProcessIndirectBuffer(uint32_t* buffer, uint32_t size_dw);
|
||||
Pm4ProcessResult Process(Pm4Execution& execution, std::span<const uint32_t> commands);
|
||||
void ProcessIndirectBuffer(std::span<const uint32_t> commands);
|
||||
|
||||
void SetFlip(const FlipInfo& flip) { m_flip = flip; }
|
||||
|
||||
[[nodiscard]] uint64_t GetSubmitId() const { return m_submit_id; }
|
||||
void SetSubmitId(uint64_t submit_id) { m_submit_id = submit_id; }
|
||||
[[nodiscard]] bool IsAsyncComputeQueue() const { return m_interrupt_event_id >= 0x20; }
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
@@ -146,14 +145,9 @@ private:
|
||||
uint32_t interrupt_context_id);
|
||||
void ProcessPm4(Pm4Execution& execution, size_t stop_depth);
|
||||
void SuspendPm4();
|
||||
void SubmitNonIndexedDraw(uint32_t vertex_count, uint32_t flags,
|
||||
uint32_t render_target_slice_offset, uint32_t first_vertex,
|
||||
uint32_t first_instance);
|
||||
|
||||
CommandScheduler& GetScheduler() const { return m_renderer.GetCommandScheduler(); }
|
||||
RenderCommandBuffer& CurrentBuffer() { return GetScheduler().Current(); }
|
||||
void CheckBuffer() const { GetScheduler().CheckActive(); }
|
||||
GpuResourceManager& GetGpuResources() const { return m_renderer.GetGpuResources(); }
|
||||
CommandScheduler& GetScheduler() const { return m_renderer.GetCommandScheduler(); }
|
||||
CommandBuffer& CurrentBuffer() { return GetScheduler().Current(); }
|
||||
void CheckBuffer() const { GetScheduler().CheckActive(); }
|
||||
|
||||
RenderContext& m_renderer;
|
||||
HW::Context m_ctx;
|
||||
@@ -176,9 +170,11 @@ private:
|
||||
|
||||
uint32_t m_const_ram[0x3000] = {0};
|
||||
|
||||
FlipInfo m_flip;
|
||||
uint64_t m_submit_id = 0;
|
||||
bool m_predicate_skip = false;
|
||||
FlipInfo m_flip;
|
||||
const int m_interrupt_event_id;
|
||||
uint64_t m_submit_id = 0;
|
||||
uint64_t m_synthetic_occlusion_counter = 0;
|
||||
bool m_predicate_skip = false;
|
||||
};
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
@@ -23,9 +23,7 @@ constexpr auto MakeContextDispatchTable() {
|
||||
g_hw_ctx_func[Pm4::DB_STENCIL_CLEAR] = HwCtxSetStencilClear;
|
||||
g_hw_ctx_func[Pm4::DB_DEPTH_CLEAR] = HwCtxSetDepthClear;
|
||||
g_hw_ctx_func[Pm4::PA_SC_SCREEN_SCISSOR_TL] = HwCtxSetScreenScissor;
|
||||
g_hw_ctx_func[Pm4::DB_Z_INFO] = HwCtxSetDepthRenderTarget;
|
||||
g_hw_ctx_func[Pm4::DB_STENCIL_INFO] = HwCtxSetStencilInfo;
|
||||
g_hw_ctx_func[Pm4::DB_HTILE_SURFACE] = HwCtxSetDepthHtileSurface;
|
||||
g_hw_ctx_func[Pm4::PA_SU_HARDWARE_SCREEN_OFFSET] = HwCtxSetHardwareScreenOffset;
|
||||
g_hw_ctx_func[Pm4::PA_SC_WINDOW_OFFSET] = HwCtxSetWindowOffset;
|
||||
g_hw_ctx_func[Pm4::PA_SC_WINDOW_SCISSOR_TL] = HwCtxSetWindowScissor;
|
||||
@@ -35,7 +33,6 @@ constexpr auto MakeContextDispatchTable() {
|
||||
}
|
||||
g_hw_ctx_func[Pm4::CB_TARGET_MASK] = HwCtxSetRenderTargetMask;
|
||||
g_hw_ctx_func[Pm4::PA_SC_GENERIC_SCISSOR_TL] = HwCtxSetGenericScissor;
|
||||
g_hw_ctx_func[Pm4::CB_BLEND_RED] = HwCtxSetBlendColor;
|
||||
g_hw_ctx_func[Pm4::CB_DCC_CONTROL] = HwCtxSetCbDccControl;
|
||||
g_hw_ctx_func[Pm4::DB_STENCIL_CONTROL] = HwCtxSetStencilControl;
|
||||
g_hw_ctx_func[Pm4::DB_STENCILREFMASK] = HwCtxSetStencilMask;
|
||||
@@ -76,16 +73,13 @@ constexpr auto MakeContextDispatchTable() {
|
||||
g_hw_ctx_func[Pm4::PA_SC_BINNER_CNTL_0] = HwCtxSetPaScExtendedControl;
|
||||
g_hw_ctx_func[Pm4::PA_SC_BINNER_CNTL_1] = HwCtxSetPaScExtendedControl;
|
||||
g_hw_ctx_func[Pm4::PA_SC_CONSERVATIVE_RASTERIZATION_CNTL] = HwCtxSetPaScExtendedControl;
|
||||
g_hw_ctx_func[Pm4::PA_SC_NGG_MODE_CNTL] = HwCtxSetPaScExtendedControl;
|
||||
g_hw_ctx_func[Pm4::DB_ALPHA_TO_MASK] = HwCtxSetAlphaToMask;
|
||||
g_hw_ctx_func[Pm4::VGT_DRAW_PAYLOAD_CNTL] = HwCtxSetDrawPayloadControl;
|
||||
g_hw_ctx_func[Pm4::VGT_PRIMITIVEID_RESET] = HwCtxSetPrimitiveIdReset;
|
||||
g_hw_ctx_func[Pm4::PA_CL_OBJPRIM_ID_CNTL] = HwCtxSetObjprimIdControl;
|
||||
g_hw_ctx_func[Pm4::VGT_SHADER_STAGES_EN] = HwCtxSetShaderStages;
|
||||
g_hw_ctx_func[Pm4::PA_CL_GB_VERT_CLIP_ADJ] = HwCtxSetGuardBands;
|
||||
|
||||
for (uint32_t slot = 0; slot < 8; slot++) {
|
||||
g_hw_ctx_func[Pm4::CB_COLOR0_BASE + slot * 15] = HwCtxSetRenderTarget;
|
||||
g_hw_ctx_func[Pm4::CB_COLOR0_INFO + slot * 15] = HwCtxSetColorInfo;
|
||||
|
||||
g_hw_ctx_func[Pm4::CB_BLEND0_CONTROL + slot * 1] = HwCtxSetBlendControl;
|
||||
@@ -118,25 +112,21 @@ constexpr auto MakeShaderDispatchTable() {
|
||||
}
|
||||
|
||||
for (uint32_t slot = 0; slot < 4; slot++) {
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_ACCUM_PS_0 + slot] = HwShSetPsUserAccumSgpr;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_ACCUM_PS_0 + slot] = HwShIgnoreUserAccumulator;
|
||||
}
|
||||
|
||||
for (uint32_t slot = 0; slot < 4; slot++) {
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_ACCUM_ESGS_0 + slot] = HwShSetGsUserAccumSgpr;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_ACCUM_ESGS_0 + slot] = HwShIgnoreUserAccumulator;
|
||||
}
|
||||
for (uint32_t slot = 0; slot < 4; slot++) {
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_ACCUM_LSHS_0 + slot] = HwShSetHsUserAccumSgpr;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_ACCUM_LSHS_0 + slot] = HwShIgnoreUserAccumulator;
|
||||
}
|
||||
|
||||
for (uint32_t slot = 0; Pm4::COMPUTE_USER_ACCUM_0 + slot < Pm4::COMPUTE_USER_DATA_0; slot++) {
|
||||
g_hw_sh_func[Pm4::COMPUTE_USER_ACCUM_0 + slot] = HwShSetCsUserAccumSgpr;
|
||||
for (uint32_t slot = 0; slot < 4; slot++) {
|
||||
g_hw_sh_func[Pm4::COMPUTE_USER_ACCUM_0 + slot] = HwShIgnoreUserAccumulator;
|
||||
}
|
||||
g_hw_sh_func[Pm4::COMPUTE_PGM_LO] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_PGM_HI] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_TBA_LO] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_TBA_HI] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_TMA_LO] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_TMA_HI] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_PGM_RSRC1] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_PGM_RSRC2] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_START_X] = HwShSetCsRegisters;
|
||||
@@ -146,30 +136,16 @@ constexpr auto MakeShaderDispatchTable() {
|
||||
g_hw_sh_func[Pm4::COMPUTE_NUM_THREAD_Y] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_NUM_THREAD_Z] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_RESOURCE_LIMITS] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_DESTINATION_EN_SE0] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_DESTINATION_EN_SE1] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_TMPRING_SIZE] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_DESTINATION_EN_SE2] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_DESTINATION_EN_SE3] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_PGM_RSRC3] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_SHADER_CHKSUM] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_DISPATCH_TUNNEL] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_TBA_LO_PS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_PGM_RSRC3_PS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_PGM_RSRC4_PS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_TMA_LO_PS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_TMA_HI_PS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_REQ_CTRL_PS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_PGM_RSRC3_GS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_PGM_RSRC4_GS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_DATA_ADDR_LO_GS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_DATA_ADDR_HI_GS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_REQ_CTRL_ESGS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_PGM_RSRC3_HS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_PGM_RSRC4_HS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_DATA_ADDR_LO_HS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_DATA_ADDR_HI_HS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_REQ_CTRL_LSHS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_PACE_ID] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_GRAPHICS_SHADER_CONTROL_PS] = HwShSetRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_GRAPHICS_SHADER_CONTROL_GS] = HwShSetRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_DATA_ADDR_LO_GS] = HwShSetRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_DATA_ADDR_HI_GS] = HwShSetRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_GRAPHICS_SHADER_CONTROL_HS] = HwShSetRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_DATA_ADDR_LO_HS] = HwShSetRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_DATA_ADDR_HI_HS] = HwShSetRegisters;
|
||||
|
||||
return g_hw_sh_func;
|
||||
}
|
||||
@@ -181,6 +157,7 @@ constexpr auto MakeUconfigDispatchTable() {
|
||||
g_hw_uc_func[Pm4::VGT_INDEX_TYPE] = HwUcSetIndexType;
|
||||
g_hw_uc_func[Pm4::VGT_OBJECT_ID] = HwUcSetObjectId;
|
||||
g_hw_uc_func[Pm4::TEXTURE_GRADIENT_FACTORS] = HwUcSetTextureGradientFactors;
|
||||
g_hw_uc_func[Pm4::TEXTURE_GRADIENT_CONTROL] = HwUcSetTextureGradientFactors;
|
||||
g_hw_uc_func[Pm4::IA_MULTI_VGT_PARAM] = HwUcSetIaMultiVgtParam;
|
||||
g_hw_uc_func[Pm4::GE_MULTI_PRIM_IB_RESET_EN] = HwUcSetMultiPrimIbReset;
|
||||
g_hw_uc_func[Pm4::TA_CS_BC_BASE_ADDR] = HwUcSetBorderColorTableAddr;
|
||||
@@ -217,6 +194,7 @@ constexpr auto MakeOpcodeDispatchTable() {
|
||||
g_cp_op_func[Pm4::IT_COND_EXEC] = CpOpCondExec;
|
||||
g_cp_op_func[Pm4::IT_SET_PREDICATION] = CpOpSetPredication;
|
||||
g_cp_op_func[Pm4::IT_WRITE_DATA] = CpOpWriteData;
|
||||
g_cp_op_func[Pm4::IT_WAIT_REG_MEM] = CpOpWaitRegMem32;
|
||||
g_cp_op_func[Pm4::IT_INDIRECT_BUFFER] = CpOpIndirectBuffer;
|
||||
g_cp_op_func[Pm4::IT_COPY_DATA] = CpOpCopyData;
|
||||
g_cp_op_func[Pm4::IT_EVENT_WRITE] = CpOpEventWrite;
|
||||
@@ -224,6 +202,7 @@ constexpr auto MakeOpcodeDispatchTable() {
|
||||
g_cp_op_func[Pm4::IT_EVENT_WRITE_EOS] = CpOpEventWriteEos;
|
||||
g_cp_op_func[Pm4::IT_DMA_DATA] = CpOpDmaData;
|
||||
g_cp_op_func[Pm4::IT_ACQUIRE_MEM] = CpOpAcquireMem;
|
||||
g_cp_op_func[Pm4::IT_REWIND] = CpOpRewind;
|
||||
g_cp_op_func[Pm4::IT_SET_CONTEXT_REG] = CpOpSetContextReg;
|
||||
g_cp_op_func[Pm4::IT_SET_SH_REG] = CpOpSetShaderReg;
|
||||
g_cp_op_func[Pm4::IT_SET_UCONFIG_REG] = CpOpSetUconfigReg;
|
||||
@@ -238,6 +217,7 @@ constexpr auto MakeOpcodeDispatchTable() {
|
||||
g_cp_op_func[Pm4::IT_WAIT_ON_CE_COUNTER] = CpOpWaitOnCeCounter;
|
||||
g_cp_op_func[Pm4::IT_WAIT_ON_DE_COUNTER_DIFF] = CpOpWaitOnDeCounterDiff;
|
||||
g_cp_op_func[Pm4::IT_GET_LOD_STATS] = CpOpGetLodStats;
|
||||
g_cp_op_func[Pm4::IT_WAIT_REG_MEM_64] = CpOpWaitRegMem64;
|
||||
|
||||
return g_cp_op_func;
|
||||
}
|
||||
@@ -246,12 +226,10 @@ constexpr auto MakeCustomOpcodeDispatchTable() {
|
||||
std::array<cp_op_parser_func_t, Pm4::R_NUM> g_cp_op_custom_func {};
|
||||
|
||||
g_cp_op_custom_func[Pm4::R_DISPATCH_RESET] = CpOpDispatchReset;
|
||||
g_cp_op_custom_func[Pm4::R_WAIT_MEM_32] = CpOpWaitRegMem32;
|
||||
g_cp_op_custom_func[Pm4::R_WAIT_FLIP_DONE] = CpOpWaitFlipDone;
|
||||
g_cp_op_custom_func[Pm4::R_PUSH_MARKER] = CpOpPushMarker;
|
||||
g_cp_op_custom_func[Pm4::R_POP_MARKER] = CpOpPopMarker;
|
||||
g_cp_op_custom_func[Pm4::R_ACQUIRE_MEM] = CpOpAcquireMem;
|
||||
g_cp_op_custom_func[Pm4::R_WAIT_MEM_64] = CpOpWaitRegMem64;
|
||||
g_cp_op_custom_func[Pm4::R_FLIP] = CpOpFlip;
|
||||
g_cp_op_custom_func[Pm4::R_RELEASE_MEM] = CpOpReleaseMem;
|
||||
g_cp_op_custom_func[Pm4::R_CONTEXT_STATE] = CpOpContextState;
|
||||
|
||||
@@ -44,11 +44,7 @@ uint32_t HwCtxSetBorderColorTableAddr(CommandProcessor&, uint32_t, uint32_t, con
|
||||
uint32_t HwCtxSetStencilClear(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetDepthClear(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetScreenScissor(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetDepthRenderTarget(CommandProcessor&, uint32_t, uint32_t, const uint32_t*,
|
||||
uint32_t);
|
||||
uint32_t HwCtxSetStencilInfo(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetDepthHtileSurface(CommandProcessor&, uint32_t, uint32_t, const uint32_t*,
|
||||
uint32_t);
|
||||
uint32_t HwCtxSetHardwareScreenOffset(CommandProcessor&, uint32_t, uint32_t, const uint32_t*,
|
||||
uint32_t);
|
||||
uint32_t HwCtxSetWindowOffset(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
@@ -56,7 +52,6 @@ uint32_t HwCtxSetWindowScissor(CommandProcessor&, uint32_t, uint32_t, const uint
|
||||
uint32_t HwCtxSetClipRect(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetRenderTargetMask(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetGenericScissor(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetBlendColor(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetCbDccControl(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetStencilControl(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetStencilMask(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
@@ -90,8 +85,6 @@ uint32_t HwCtxSetDrawPayloadControl(CommandProcessor&, uint32_t, uint32_t, const
|
||||
uint32_t HwCtxSetPrimitiveIdReset(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetObjprimIdControl(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetShaderStages(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetGuardBands(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetRenderTarget(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetColorInfo(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetBlendControl(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetViewportScissor(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
@@ -102,12 +95,10 @@ uint32_t HwShSetPsUserSgpr(CommandProcessor&, uint32_t, uint32_t, const uint32_t
|
||||
uint32_t HwShSetGsUserSgpr(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwShSetHsUserSgpr(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwShSetCsUserSgpr(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwShSetPsUserAccumSgpr(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwShSetGsUserAccumSgpr(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwShSetHsUserAccumSgpr(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwShSetCsUserAccumSgpr(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwShIgnoreUserAccumulator(CommandProcessor&, uint32_t, uint32_t, const uint32_t*,
|
||||
uint32_t);
|
||||
uint32_t HwShSetCsRegisters(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwShIgnoreRegisters(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwShSetRegisters(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwUcSetPrimitiveType(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwUcSetIndexType(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwUcSetObjectId(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
@@ -133,6 +124,7 @@ uint32_t CpOpDrawIndirectMulti(CommandProcessor&, uint32_t, const uint32_t*, uin
|
||||
uint32_t CpOpDrawIndex(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t);
|
||||
uint32_t CpOpDrawIndexOffset(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t);
|
||||
uint32_t CpOpPfpSyncMe(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t);
|
||||
uint32_t CpOpRewind(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t);
|
||||
uint32_t CpOpIndexType(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t);
|
||||
uint32_t CpOpNumInstances(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t);
|
||||
uint32_t CpOpDrawIndexAuto(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t);
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -3,15 +3,8 @@
|
||||
|
||||
#include "common/common.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace Libs::Graphics::Prospero {
|
||||
|
||||
template <typename T>
|
||||
constexpr auto GpuEnumValue(T value) {
|
||||
return static_cast<std::underlying_type_t<T>>(value);
|
||||
}
|
||||
|
||||
enum class PrimitiveType : uint32_t {
|
||||
kNone = 0,
|
||||
kPointList = 1,
|
||||
@@ -115,32 +108,52 @@ enum class ChannelOrder : uint32_t {
|
||||
kAltReversed = 3,
|
||||
};
|
||||
|
||||
// Maps a shader's logical RGBA export to the host color-attachment components. Two bits per
|
||||
// component keep this cheap to carry in shader and pipeline keys.
|
||||
// Selects the logical shader component written to each physical color-attachment component.
|
||||
// Two bits per component keep this cheap to carry in shader and pipeline keys.
|
||||
struct ColorComponentMapping {
|
||||
uint8_t packed = 0xe4u; // RGBA: 0, 1, 2, 3
|
||||
static constexpr uint8_t kIdentity = 0xe4u;
|
||||
|
||||
uint8_t packed = kIdentity;
|
||||
|
||||
[[nodiscard]] constexpr uint32_t Map(uint32_t component) const {
|
||||
return component < 4u ? (packed >> (component * 2u)) & 0x3u : component;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr uint32_t ApplyMask(uint32_t mask) const {
|
||||
[[nodiscard]] constexpr uint32_t ApplyMask(uint32_t logical_mask) const {
|
||||
uint32_t mapped = 0;
|
||||
for (uint32_t component = 0; component < 4u; component++) {
|
||||
mapped |= ((mask >> component) & 1u) << Map(component);
|
||||
for (uint32_t physical_component = 0; physical_component < 4u; physical_component++) {
|
||||
mapped |= ((logical_mask >> Map(physical_component)) & 1u) << physical_component;
|
||||
}
|
||||
return mapped;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool IsIdentity() const { return packed == 0xe4u; }
|
||||
// This mapping selects an intermediate component; next selects the final logical component.
|
||||
[[nodiscard]] constexpr ColorComponentMapping Then(ColorComponentMapping next) const {
|
||||
ColorComponentMapping result {0u};
|
||||
for (uint32_t physical_component = 0; physical_component < 4u; physical_component++) {
|
||||
result.packed |= static_cast<uint8_t>(next.Map(Map(physical_component))
|
||||
<< (physical_component * 2u));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool IsIdentity() const { return packed == kIdentity; }
|
||||
|
||||
bool operator==(const ColorComponentMapping&) const = default;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ColorComponentMapping) == sizeof(uint8_t));
|
||||
|
||||
inline constexpr ColorComponentMapping ColorMappingAbgr {0x1bu}; // 3, 2, 1, 0
|
||||
inline constexpr ColorComponentMapping ColorMappingRgba {ColorComponentMapping::kIdentity};
|
||||
inline constexpr ColorComponentMapping ColorMappingGr {0xe1u}; // 1, 0, 2, 3
|
||||
inline constexpr ColorComponentMapping ColorMappingRabg {0x6cu}; // 0, 3, 2, 1
|
||||
inline constexpr ColorComponentMapping ColorMappingRgab {0xb4u}; // 0, 1, 3, 2
|
||||
inline constexpr ColorComponentMapping ColorMappingBgra {0xc6u}; // 2, 1, 0, 3
|
||||
inline constexpr ColorComponentMapping ColorMappingAbgr {0x1bu}; // 3, 2, 1, 0
|
||||
inline constexpr ColorComponentMapping ColorMappingAgba {0x27u}; // 3, 1, 2, 0
|
||||
inline constexpr ColorComponentMapping ColorMappingArbg {0x63u}; // 3, 0, 2, 1
|
||||
inline constexpr ColorComponentMapping ColorMappingAgbr {0x87u}; // 3, 1, 0, 2
|
||||
inline constexpr ColorComponentMapping ColorMappingArgb {0x93u}; // 3, 0, 1, 2
|
||||
|
||||
enum class DepthFormat : uint32_t {
|
||||
kInvalid = 0,
|
||||
@@ -388,11 +401,25 @@ enum class BufferFormat : uint32_t {
|
||||
k8Srgb = 128,
|
||||
k8_8Srgb = 129,
|
||||
k8_8_8_8Srgb = 130,
|
||||
k10_10_10_2Float = 131,
|
||||
k9_9_9_5Float = 132,
|
||||
k5_6_5UNorm = 133,
|
||||
k5_5_5_1UNorm = 134,
|
||||
k1_5_5_5UNorm = 135,
|
||||
k4_4_4_4UNorm = 136,
|
||||
kFmask8_S2_F1 = 156,
|
||||
kFmask8_S4_F1 = 157,
|
||||
kFmask8_S8_F1 = 158,
|
||||
kFmask8_S2_F2 = 159,
|
||||
kFmask8_S4_F2 = 160,
|
||||
kFmask8_S4_F4 = 161,
|
||||
kFmask16_S16_F1 = 162,
|
||||
kFmask16_S8_F2 = 163,
|
||||
kFmask32_S16_F2 = 164,
|
||||
kFmask32_S8_F4 = 165,
|
||||
kFmask32_S8_F8 = 166,
|
||||
kFmask64_S16_F4 = 167,
|
||||
kFmask64_S16_F8 = 168,
|
||||
kBc1UNorm = 169,
|
||||
kBc1Srgb = 170,
|
||||
kBc2UNorm = 171,
|
||||
|
||||
@@ -3,129 +3,235 @@
|
||||
#include "graphics/guest_gpu/gpu_defs.h"
|
||||
|
||||
#include <array>
|
||||
#include <bit>
|
||||
|
||||
namespace Libs::Graphics::Prospero {
|
||||
namespace {
|
||||
|
||||
struct FormatInfo {
|
||||
uint32_t format;
|
||||
uint32_t bytes_per_element;
|
||||
uint32_t block_compressed_bytes_per_block;
|
||||
uint32_t render_target_bytes_per_element;
|
||||
bool supported_sampled_texture_format;
|
||||
bool unsigned_integer_texture_format;
|
||||
BufferFormat format;
|
||||
uint32_t bytes_per_element;
|
||||
uint32_t block_compressed_bytes_per_block;
|
||||
uint32_t render_target_bytes_per_element;
|
||||
bool sampled_texture;
|
||||
bool uint_texture;
|
||||
bool sint_texture = false;
|
||||
};
|
||||
|
||||
constexpr FormatInfo kFormatInfo[] = {
|
||||
{GpuEnumValue(BufferFormat::k8UNorm), 1, 0, 1, true, false},
|
||||
{GpuEnumValue(BufferFormat::k8SNorm), 0, 0, 1, false, false},
|
||||
{GpuEnumValue(BufferFormat::k8UInt), 1, 0, 1, true, true},
|
||||
{GpuEnumValue(BufferFormat::k16UNorm), 2, 0, 2, true, false},
|
||||
{GpuEnumValue(BufferFormat::k16SNorm), 2, 0, 2, true, false},
|
||||
{GpuEnumValue(BufferFormat::k16UInt), 2, 0, 2, true, true},
|
||||
{GpuEnumValue(BufferFormat::k16SInt), 2, 0, 2, false, false},
|
||||
{GpuEnumValue(BufferFormat::k16Float), 2, 0, 2, true, false},
|
||||
{GpuEnumValue(BufferFormat::k8_8UNorm), 2, 0, 2, true, false},
|
||||
{GpuEnumValue(BufferFormat::k8_8SNorm), 2, 0, 2, true, false},
|
||||
{GpuEnumValue(BufferFormat::k8_8UInt), 2, 0, 2, true, true},
|
||||
{GpuEnumValue(BufferFormat::k8_8SInt), 2, 0, 2, false, false},
|
||||
{GpuEnumValue(BufferFormat::k32UInt), 4, 0, 4, true, true},
|
||||
{GpuEnumValue(BufferFormat::k32SInt), 4, 0, 4, false, false},
|
||||
{GpuEnumValue(BufferFormat::k32Float), 4, 0, 4, true, false},
|
||||
{GpuEnumValue(BufferFormat::k16_16UNorm), 4, 0, 4, true, false},
|
||||
{GpuEnumValue(BufferFormat::k16_16SNorm), 4, 0, 4, true, false},
|
||||
{GpuEnumValue(BufferFormat::k16_16UInt), 4, 0, 4, true, true},
|
||||
{GpuEnumValue(BufferFormat::k16_16SInt), 4, 0, 4, false, false},
|
||||
{GpuEnumValue(BufferFormat::k16_16Float), 4, 0, 4, true, false},
|
||||
{GpuEnumValue(BufferFormat::k11_11_10Float), 4, 0, 4, true, false},
|
||||
{GpuEnumValue(BufferFormat::k10_10_10_2UNorm), 4, 0, 4, true, false},
|
||||
{GpuEnumValue(BufferFormat::k10_10_10_2UInt), 4, 0, 4, true, true},
|
||||
{GpuEnumValue(BufferFormat::k8_8_8_8UNorm), 4, 0, 4, true, false},
|
||||
{GpuEnumValue(BufferFormat::k8_8_8_8SNorm), 4, 0, 4, true, false},
|
||||
{GpuEnumValue(BufferFormat::k8_8_8_8UInt), 4, 0, 4, true, true},
|
||||
{GpuEnumValue(BufferFormat::k8_8_8_8SInt), 4, 0, 4, false, false},
|
||||
{GpuEnumValue(BufferFormat::k32_32UInt), 8, 0, 8, true, true},
|
||||
{GpuEnumValue(BufferFormat::k32_32SInt), 8, 0, 8, false, false},
|
||||
{GpuEnumValue(BufferFormat::k32_32Float), 8, 0, 8, true, false},
|
||||
{GpuEnumValue(BufferFormat::k16_16_16_16UNorm), 8, 0, 8, true, false},
|
||||
{GpuEnumValue(BufferFormat::k16_16_16_16SNorm), 8, 0, 8, true, false},
|
||||
{GpuEnumValue(BufferFormat::k16_16_16_16UInt), 8, 0, 8, true, true},
|
||||
{GpuEnumValue(BufferFormat::k16_16_16_16SInt), 8, 0, 8, false, false},
|
||||
{GpuEnumValue(BufferFormat::k16_16_16_16Float), 8, 0, 8, true, false},
|
||||
{GpuEnumValue(BufferFormat::k32_32_32UInt), 12, 0, 12, true, true},
|
||||
{GpuEnumValue(BufferFormat::k32_32_32SInt), 12, 0, 12, false, false},
|
||||
{GpuEnumValue(BufferFormat::k32_32_32Float), 12, 0, 12, true, false},
|
||||
{GpuEnumValue(BufferFormat::k32_32_32_32UInt), 16, 0, 16, true, true},
|
||||
{GpuEnumValue(BufferFormat::k32_32_32_32SInt), 16, 0, 16, false, false},
|
||||
{GpuEnumValue(BufferFormat::k32_32_32_32Float), 16, 0, 16, true, false},
|
||||
{GpuEnumValue(BufferFormat::k8Srgb), 1, 0, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::k8_8Srgb), 2, 0, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::k8_8_8_8Srgb), 4, 0, 4, true, false},
|
||||
{GpuEnumValue(BufferFormat::k9_9_9_5Float), 4, 0, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::k5_6_5UNorm), 2, 0, 2, true, false},
|
||||
{GpuEnumValue(BufferFormat::k5_5_5_1UNorm), 2, 0, 2, true, false},
|
||||
{GpuEnumValue(BufferFormat::k4_4_4_4UNorm), 2, 0, 2, true, false},
|
||||
{GpuEnumValue(BufferFormat::kFmask8_S4_F4), 1, 0, 1, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc1UNorm), 0, 8, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc1Srgb), 0, 8, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc2UNorm), 0, 16, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc2Srgb), 0, 16, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc3UNorm), 0, 16, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc3Srgb), 0, 16, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc4UNorm), 0, 8, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc4SNorm), 0, 8, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc5UNorm), 0, 16, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc5SNorm), 0, 16, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc6UFloat), 0, 16, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc6SFloat), 0, 16, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc7UNorm), 0, 16, 0, true, false},
|
||||
{GpuEnumValue(BufferFormat::kBc7Srgb), 0, 16, 0, true, false},
|
||||
{BufferFormat::k8UNorm, 1, 0, 1, true, false},
|
||||
{BufferFormat::k8SNorm, 0, 0, 1, false, false},
|
||||
{BufferFormat::k8UInt, 1, 0, 1, true, true},
|
||||
{BufferFormat::k16UNorm, 2, 0, 2, true, false},
|
||||
{BufferFormat::k16SNorm, 2, 0, 2, true, false},
|
||||
{BufferFormat::k16UInt, 2, 0, 2, true, true},
|
||||
{BufferFormat::k16SInt, 2, 0, 2, true, false, true},
|
||||
{BufferFormat::k16Float, 2, 0, 2, true, false},
|
||||
{BufferFormat::k8_8UNorm, 2, 0, 2, true, false},
|
||||
{BufferFormat::k8_8SNorm, 2, 0, 2, true, false},
|
||||
{BufferFormat::k8_8UInt, 2, 0, 2, true, true},
|
||||
{BufferFormat::k8_8SInt, 2, 0, 2, true, false, true},
|
||||
{BufferFormat::k32UInt, 4, 0, 4, true, true},
|
||||
{BufferFormat::k32SInt, 4, 0, 4, true, false, true},
|
||||
{BufferFormat::k32Float, 4, 0, 4, true, false},
|
||||
{BufferFormat::k16_16UNorm, 4, 0, 4, true, false},
|
||||
{BufferFormat::k16_16SNorm, 4, 0, 4, true, false},
|
||||
{BufferFormat::k16_16UInt, 4, 0, 4, true, true},
|
||||
{BufferFormat::k16_16SInt, 4, 0, 4, true, false, true},
|
||||
{BufferFormat::k16_16Float, 4, 0, 4, true, false},
|
||||
{BufferFormat::k11_11_10UInt, 4, 0, 4, true, true},
|
||||
{BufferFormat::k11_11_10Float, 4, 0, 4, true, false},
|
||||
{BufferFormat::k10_10_10_2UNorm, 4, 0, 4, true, false},
|
||||
{BufferFormat::k10_10_10_2UInt, 4, 0, 4, true, true},
|
||||
{BufferFormat::k8_8_8_8UNorm, 4, 0, 4, true, false},
|
||||
{BufferFormat::k8_8_8_8SNorm, 4, 0, 4, true, false},
|
||||
{BufferFormat::k8_8_8_8UInt, 4, 0, 4, true, true},
|
||||
{BufferFormat::k8_8_8_8SInt, 4, 0, 4, true, false, true},
|
||||
{BufferFormat::k32_32UInt, 8, 0, 8, true, true},
|
||||
{BufferFormat::k32_32SInt, 8, 0, 8, true, false, true},
|
||||
{BufferFormat::k32_32Float, 8, 0, 8, true, false},
|
||||
{BufferFormat::k16_16_16_16UNorm, 8, 0, 8, true, false},
|
||||
{BufferFormat::k16_16_16_16SNorm, 8, 0, 8, true, false},
|
||||
{BufferFormat::k16_16_16_16UInt, 8, 0, 8, true, true},
|
||||
{BufferFormat::k16_16_16_16SInt, 8, 0, 8, true, false, true},
|
||||
{BufferFormat::k16_16_16_16Float, 8, 0, 8, true, false},
|
||||
{BufferFormat::k32_32_32UInt, 12, 0, 12, true, true},
|
||||
{BufferFormat::k32_32_32SInt, 12, 0, 12, true, false, true},
|
||||
{BufferFormat::k32_32_32Float, 12, 0, 12, true, false},
|
||||
{BufferFormat::k32_32_32_32UInt, 16, 0, 16, true, true},
|
||||
{BufferFormat::k32_32_32_32SInt, 16, 0, 16, true, false, true},
|
||||
{BufferFormat::k32_32_32_32Float, 16, 0, 16, true, false},
|
||||
{BufferFormat::k8Srgb, 1, 0, 0, true, false},
|
||||
{BufferFormat::k8_8Srgb, 2, 0, 0, true, false},
|
||||
{BufferFormat::k8_8_8_8Srgb, 4, 0, 4, true, false},
|
||||
{BufferFormat::k9_9_9_5Float, 4, 0, 0, true, false},
|
||||
{BufferFormat::k5_6_5UNorm, 2, 0, 2, true, false},
|
||||
{BufferFormat::k5_5_5_1UNorm, 2, 0, 2, true, false},
|
||||
{BufferFormat::k1_5_5_5UNorm, 0, 0, 2, false, false},
|
||||
{BufferFormat::k4_4_4_4UNorm, 2, 0, 2, true, false},
|
||||
{BufferFormat::kFmask8_S2_F1, 1, 0, 1, true, false},
|
||||
{BufferFormat::kFmask8_S4_F1, 1, 0, 1, true, false},
|
||||
{BufferFormat::kFmask8_S8_F1, 1, 0, 1, true, false},
|
||||
{BufferFormat::kFmask8_S2_F2, 1, 0, 1, true, false},
|
||||
{BufferFormat::kFmask8_S4_F2, 1, 0, 1, true, false},
|
||||
{BufferFormat::kFmask8_S4_F4, 1, 0, 1, true, false},
|
||||
{BufferFormat::kFmask16_S16_F1, 2, 0, 2, true, false},
|
||||
{BufferFormat::kFmask16_S8_F2, 2, 0, 2, true, false},
|
||||
{BufferFormat::kFmask32_S16_F2, 4, 0, 4, true, false},
|
||||
{BufferFormat::kFmask32_S8_F4, 4, 0, 4, true, false},
|
||||
{BufferFormat::kFmask32_S8_F8, 4, 0, 4, true, false},
|
||||
{BufferFormat::kFmask64_S16_F4, 8, 0, 8, true, false},
|
||||
{BufferFormat::kFmask64_S16_F8, 8, 0, 8, true, false},
|
||||
{BufferFormat::kBc1UNorm, 0, 8, 0, true, false},
|
||||
{BufferFormat::kBc1Srgb, 0, 8, 0, true, false},
|
||||
{BufferFormat::kBc2UNorm, 0, 16, 0, true, false},
|
||||
{BufferFormat::kBc2Srgb, 0, 16, 0, true, false},
|
||||
{BufferFormat::kBc3UNorm, 0, 16, 0, true, false},
|
||||
{BufferFormat::kBc3Srgb, 0, 16, 0, true, false},
|
||||
{BufferFormat::kBc4UNorm, 0, 8, 0, true, false},
|
||||
{BufferFormat::kBc4SNorm, 0, 8, 0, true, false},
|
||||
{BufferFormat::kBc5UNorm, 0, 16, 0, true, false},
|
||||
{BufferFormat::kBc5SNorm, 0, 16, 0, true, false},
|
||||
{BufferFormat::kBc6UFloat, 0, 16, 0, true, false},
|
||||
{BufferFormat::kBc6SFloat, 0, 16, 0, true, false},
|
||||
{BufferFormat::kBc7UNorm, 0, 16, 0, true, false},
|
||||
{BufferFormat::kBc7Srgb, 0, 16, 0, true, false},
|
||||
};
|
||||
|
||||
constexpr uint8_t RenderTargetTypeBit(ChannelType type) {
|
||||
return static_cast<uint8_t>(1u << static_cast<uint32_t>(type));
|
||||
}
|
||||
|
||||
constexpr auto kUNormType = RenderTargetTypeBit(ChannelType::kUNorm);
|
||||
constexpr auto kIntegerTypes =
|
||||
RenderTargetTypeBit(ChannelType::kUInt) | RenderTargetTypeBit(ChannelType::kSInt);
|
||||
constexpr auto kNormalizedIntegerTypes =
|
||||
kUNormType | RenderTargetTypeBit(ChannelType::kSNorm) | kIntegerTypes;
|
||||
|
||||
struct RenderTargetLayoutInfo {
|
||||
ChannelLayout layout;
|
||||
BufferFormat first_linear_format;
|
||||
uint8_t linear_type_mask;
|
||||
uint8_t components;
|
||||
BufferFormat extra_format = BufferFormat::kInvalid;
|
||||
ChannelType extra_type = ChannelType::kUNorm;
|
||||
ChannelOrderSupport order_support = ChannelOrderSupport::kAll;
|
||||
};
|
||||
|
||||
constexpr RenderTargetLayoutInfo kRenderTargetLayouts[] = {
|
||||
{ChannelLayout::k8, BufferFormat::k8UNorm, kNormalizedIntegerTypes, 1, BufferFormat::k8Srgb,
|
||||
ChannelType::kSrgb},
|
||||
{ChannelLayout::k16, BufferFormat::k16UNorm, kNormalizedIntegerTypes, 1, BufferFormat::k16Float,
|
||||
ChannelType::kFloat},
|
||||
{ChannelLayout::k8_8, BufferFormat::k8_8UNorm, kNormalizedIntegerTypes, 2,
|
||||
BufferFormat::k8_8Srgb, ChannelType::kSrgb},
|
||||
{ChannelLayout::k32, BufferFormat::k32UInt, kIntegerTypes, 1, BufferFormat::k32Float,
|
||||
ChannelType::kFloat},
|
||||
{ChannelLayout::k16_16, BufferFormat::k16_16UNorm, kNormalizedIntegerTypes, 2,
|
||||
BufferFormat::k16_16Float, ChannelType::kFloat},
|
||||
{ChannelLayout::k11_11_10, BufferFormat::k11_11_10UNorm, kNormalizedIntegerTypes, 3,
|
||||
BufferFormat::k11_11_10Float, ChannelType::kFloat},
|
||||
{ChannelLayout::k10_11_11, BufferFormat::k10_11_11UNorm, kNormalizedIntegerTypes, 3,
|
||||
BufferFormat::k10_11_11Float, ChannelType::kFloat},
|
||||
{ChannelLayout::k2_10_10_10, BufferFormat::k2_10_10_10UNorm, kNormalizedIntegerTypes, 4},
|
||||
{ChannelLayout::k10_10_10_2, BufferFormat::k10_10_10_2UNorm, kNormalizedIntegerTypes, 4},
|
||||
{ChannelLayout::k8_8_8_8, BufferFormat::k8_8_8_8UNorm, kNormalizedIntegerTypes, 4,
|
||||
BufferFormat::k8_8_8_8Srgb, ChannelType::kSrgb},
|
||||
{ChannelLayout::k32_32, BufferFormat::k32_32UInt, kIntegerTypes, 2, BufferFormat::k32_32Float,
|
||||
ChannelType::kFloat},
|
||||
{ChannelLayout::k16_16_16_16, BufferFormat::k16_16_16_16UNorm, kNormalizedIntegerTypes, 4,
|
||||
BufferFormat::k16_16_16_16Float, ChannelType::kFloat},
|
||||
{ChannelLayout::k32_32_32_32, BufferFormat::k32_32_32_32UInt, kIntegerTypes, 4,
|
||||
BufferFormat::k32_32_32_32Float, ChannelType::kFloat},
|
||||
{ChannelLayout::k5_6_5, BufferFormat::k5_6_5UNorm, kUNormType, 3},
|
||||
{ChannelLayout::k5_5_5_1, BufferFormat::k5_5_5_1UNorm, kUNormType, 4},
|
||||
{ChannelLayout::k1_5_5_5, BufferFormat::k1_5_5_5UNorm, kUNormType, 4},
|
||||
{ChannelLayout::k4_4_4_4, BufferFormat::k4_4_4_4UNorm, kUNormType, 4},
|
||||
{ChannelLayout::k10_10_10_2Float, BufferFormat::kInvalid, 0, 4, BufferFormat::k10_10_10_2Float,
|
||||
ChannelType::kFloat, ChannelOrderSupport::kStandardOnly},
|
||||
};
|
||||
|
||||
const RenderTargetLayoutInfo* FindRenderTargetLayout(ChannelLayout layout) {
|
||||
for (const auto& info: kRenderTargetLayouts) {
|
||||
if (info.layout == layout) {
|
||||
return &info;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
constexpr auto MakeFormatInfoLookup() {
|
||||
constexpr uint32_t kMaxFormat = GpuEnumValue(BufferFormat::kBc7Srgb);
|
||||
constexpr size_t kMaxFormat = static_cast<size_t>(BufferFormat::kBc7Srgb);
|
||||
std::array<const FormatInfo*, kMaxFormat + 1> lookup {};
|
||||
for (const auto& info: kFormatInfo) {
|
||||
lookup[info.format] = &info;
|
||||
lookup[static_cast<size_t>(info.format)] = &info;
|
||||
}
|
||||
return lookup;
|
||||
}
|
||||
|
||||
constexpr auto kFormatInfoLookup = MakeFormatInfoLookup();
|
||||
|
||||
const FormatInfo* FindFormatInfo(uint32_t format) {
|
||||
return format < kFormatInfoLookup.size() ? kFormatInfoLookup[format] : nullptr;
|
||||
const FormatInfo* FindFormatInfo(BufferFormat format) {
|
||||
const auto index = static_cast<size_t>(format);
|
||||
return index < kFormatInfoLookup.size() ? kFormatInfoLookup[index] : nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
uint32_t NumBytesPerElement(uint32_t format) {
|
||||
RenderTargetFormatEncoding ResolveRenderTargetFormat(ChannelLayout layout, ChannelType type) {
|
||||
const auto* layout_info = FindRenderTargetLayout(layout);
|
||||
const auto raw_type = static_cast<uint32_t>(type);
|
||||
if (layout_info == nullptr || raw_type >= 8u) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto format = BufferFormat::kInvalid;
|
||||
if (layout_info->extra_format != BufferFormat::kInvalid && type == layout_info->extra_type) {
|
||||
format = layout_info->extra_format;
|
||||
} else if ((layout_info->linear_type_mask & (1u << raw_type)) != 0) {
|
||||
const auto first_type =
|
||||
std::countr_zero(static_cast<uint32_t>(layout_info->linear_type_mask));
|
||||
format = static_cast<BufferFormat>(static_cast<uint32_t>(layout_info->first_linear_format) +
|
||||
raw_type - first_type);
|
||||
}
|
||||
if (format == BufferFormat::kInvalid) {
|
||||
return {};
|
||||
}
|
||||
return {format, layout_info->components, layout_info->order_support};
|
||||
}
|
||||
|
||||
uint32_t NumBytesPerElement(BufferFormat format) {
|
||||
const auto* info = FindFormatInfo(format);
|
||||
return info != nullptr ? info->bytes_per_element : 0;
|
||||
}
|
||||
|
||||
uint32_t BlockCompressedBytesPerBlock(uint32_t format) {
|
||||
uint32_t BlockCompressedBytesPerBlock(BufferFormat format) {
|
||||
const auto* info = FindFormatInfo(format);
|
||||
return info != nullptr ? info->block_compressed_bytes_per_block : 0;
|
||||
}
|
||||
|
||||
uint32_t RenderTargetBytesPerElement(uint32_t format) {
|
||||
uint32_t RenderTargetBytesPerElement(BufferFormat format) {
|
||||
const auto* info = FindFormatInfo(format);
|
||||
return info != nullptr ? info->render_target_bytes_per_element : 0;
|
||||
}
|
||||
|
||||
bool IsSupportedTextureFormat(uint32_t format) {
|
||||
const auto* info = FindFormatInfo(format);
|
||||
return info != nullptr && info->supported_sampled_texture_format;
|
||||
bool IsFmaskTextureFormat(BufferFormat format) {
|
||||
return format >= BufferFormat::kFmask8_S2_F1 && format <= BufferFormat::kFmask64_S16_F8;
|
||||
}
|
||||
|
||||
bool IsUintTextureFormat(uint32_t format) {
|
||||
TextureNumericClass SampledTextureNumericClass(BufferFormat format) {
|
||||
const auto* info = FindFormatInfo(format);
|
||||
return info != nullptr && info->unsigned_integer_texture_format;
|
||||
if (info == nullptr || !info->sampled_texture) {
|
||||
return TextureNumericClass::Unsupported;
|
||||
}
|
||||
if (info->sint_texture) {
|
||||
return TextureNumericClass::Sint;
|
||||
}
|
||||
return info->uint_texture ? TextureNumericClass::Uint : TextureNumericClass::Float;
|
||||
}
|
||||
|
||||
bool IsFmaskTextureFormat(uint32_t format) {
|
||||
return format == GpuEnumValue(BufferFormat::kFmask8_S4_F4);
|
||||
BufferFormat RemapTextureFormat(BufferFormat format) {
|
||||
return format == BufferFormat::k11_11_10UInt ? BufferFormat::k32UInt : format;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::Prospero
|
||||
@@ -1,16 +1,51 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_GUEST_GPU_GPU_FORMAT_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_GUEST_GPU_GPU_FORMAT_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "graphics/guest_gpu/gpu_defs.h"
|
||||
|
||||
namespace Libs::Graphics::Prospero {
|
||||
|
||||
uint32_t NumBytesPerElement(uint32_t format);
|
||||
uint32_t BlockCompressedBytesPerBlock(uint32_t format);
|
||||
uint32_t RenderTargetBytesPerElement(uint32_t format);
|
||||
bool IsSupportedTextureFormat(uint32_t format);
|
||||
bool IsUintTextureFormat(uint32_t format);
|
||||
bool IsFmaskTextureFormat(uint32_t format);
|
||||
enum class ChannelOrderSupport : uint8_t {
|
||||
kNone,
|
||||
kStandardOnly,
|
||||
kAll,
|
||||
};
|
||||
|
||||
struct RenderTargetFormatEncoding {
|
||||
BufferFormat buffer_format = BufferFormat::kInvalid;
|
||||
uint8_t components = 0;
|
||||
ChannelOrderSupport order_support = ChannelOrderSupport::kNone;
|
||||
|
||||
[[nodiscard]] constexpr bool IsValid() const {
|
||||
return buffer_format != BufferFormat::kInvalid && components >= 1u && components <= 4u &&
|
||||
order_support != ChannelOrderSupport::kNone;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool SupportsOrder(ChannelOrder order) const {
|
||||
switch (order_support) {
|
||||
case ChannelOrderSupport::kStandardOnly: return order == ChannelOrder::kStandard;
|
||||
case ChannelOrderSupport::kAll:
|
||||
switch (order) {
|
||||
case ChannelOrder::kStandard:
|
||||
case ChannelOrder::kAlt:
|
||||
case ChannelOrder::kReversed:
|
||||
case ChannelOrder::kAltReversed: return true;
|
||||
}
|
||||
return false;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
enum class TextureNumericClass { Unsupported, Float, Uint, Sint };
|
||||
|
||||
RenderTargetFormatEncoding ResolveRenderTargetFormat(ChannelLayout layout, ChannelType type);
|
||||
uint32_t NumBytesPerElement(BufferFormat format);
|
||||
uint32_t BlockCompressedBytesPerBlock(BufferFormat format);
|
||||
uint32_t RenderTargetBytesPerElement(BufferFormat format);
|
||||
bool IsFmaskTextureFormat(BufferFormat format);
|
||||
TextureNumericClass SampledTextureNumericClass(BufferFormat format);
|
||||
BufferFormat RemapTextureFormat(BufferFormat format);
|
||||
|
||||
} // namespace Libs::Graphics::Prospero
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <cstdio>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <semaphore>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
@@ -36,7 +37,22 @@ static thread_local CommandProcessor* g_current_processor = nullptr;
|
||||
static thread_local Pm4Execution* g_current_execution = nullptr;
|
||||
static thread_local bool g_gpu_mutex_owned = false;
|
||||
static thread_local bool g_gpu_thread = false;
|
||||
static thread_local GpuState* g_gpu_state = nullptr;
|
||||
static thread_local GuestGpu* g_gpu_state = nullptr;
|
||||
|
||||
struct DrawIndirectArgs {
|
||||
uint32_t vertex_count_per_instance;
|
||||
uint32_t instance_count;
|
||||
uint32_t start_vertex_location;
|
||||
uint32_t start_instance_location;
|
||||
};
|
||||
|
||||
struct DrawIndexedIndirectArgs {
|
||||
uint32_t index_count_per_instance;
|
||||
uint32_t instance_count;
|
||||
uint32_t start_index_location;
|
||||
uint32_t base_vertex_location;
|
||||
uint32_t start_instance_location;
|
||||
};
|
||||
|
||||
class GpuMutexLock final {
|
||||
public:
|
||||
@@ -59,115 +75,23 @@ private:
|
||||
Common::Mutex& m_mutex;
|
||||
};
|
||||
|
||||
struct OwnedCmdBuffer {
|
||||
OwnedCmdBuffer() = default;
|
||||
explicit OwnedCmdBuffer(const uint32_t* data, uint32_t count) {
|
||||
EXIT_IF(data == nullptr && count != 0);
|
||||
if (count != 0) {
|
||||
m_words.assign(data, data + count);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool Empty() const noexcept { return m_words.empty(); }
|
||||
[[nodiscard]] uint32_t Size() const noexcept { return static_cast<uint32_t>(m_words.size()); }
|
||||
[[nodiscard]] uint32_t* Data() noexcept { return m_words.data(); }
|
||||
|
||||
private:
|
||||
std::vector<uint32_t> m_words;
|
||||
};
|
||||
|
||||
class GpuState {
|
||||
public:
|
||||
static constexpr uint32_t ComputePipeCount = 7;
|
||||
static constexpr uint32_t QueuesPerComputePipe = 8;
|
||||
static constexpr uint32_t ComputeQueueCount = ComputePipeCount * QueuesPerComputePipe;
|
||||
static constexpr uint32_t QueueCount = 1 + ComputeQueueCount;
|
||||
|
||||
explicit GpuState(RenderContext& renderer): m_renderer(renderer) {
|
||||
EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread());
|
||||
m_gfx_cp = std::make_unique<CommandProcessor>(renderer);
|
||||
m_thread = std::jthread(ThreadRun, this);
|
||||
}
|
||||
~GpuState();
|
||||
|
||||
KYTY_CLASS_NO_COPY(GpuState);
|
||||
|
||||
void Submit(uint32_t* cmd_draw_buffer, uint32_t num_draw_dw, uint32_t* cmd_const_buffer,
|
||||
uint32_t num_const_dw, bool trigger_agc_interrupt_on_done);
|
||||
void SubmitCompute(uint32_t queue, uint32_t* cmd_buffer, uint32_t num_dw,
|
||||
bool trigger_agc_interrupt_on_done);
|
||||
void SubmitFlipPreparation(uint64_t request_id);
|
||||
void Done();
|
||||
void Shutdown();
|
||||
[[nodiscard]] bool IsStopping();
|
||||
void SendCommand(Common::UniqueFunction<void>&& command);
|
||||
void SendCommandSync(Common::UniqueFunction<void>&& command);
|
||||
int GetFrameNum();
|
||||
[[nodiscard]] static bool IsGpuThread() noexcept { return g_gpu_thread; }
|
||||
|
||||
private:
|
||||
enum class SubmissionType { Graphics, Compute, FlipPreparation };
|
||||
|
||||
struct Submission {
|
||||
SubmissionType type = SubmissionType::Graphics;
|
||||
uint32_t queue_id = 0;
|
||||
OwnedCmdBuffer commands;
|
||||
OwnedCmdBuffer constant_commands;
|
||||
Pm4Execution command_execution;
|
||||
Pm4Execution constant_execution;
|
||||
bool trigger_agc_interrupt_on_done = false;
|
||||
bool reset_processor = false;
|
||||
bool started = false;
|
||||
bool command_complete = false;
|
||||
bool constant_complete = false;
|
||||
bool blocked = false;
|
||||
uint64_t flip_request_id = 0;
|
||||
};
|
||||
|
||||
void Enqueue(Submission submission);
|
||||
void WaitForIdle();
|
||||
void ProcessCommands();
|
||||
bool Process(Submission& submission);
|
||||
static void ThreadRun(void* data);
|
||||
CommandProcessor& GetProcessor(uint32_t queue_id);
|
||||
|
||||
RenderContext& m_renderer;
|
||||
Common::Mutex m_submission_mutex;
|
||||
Common::Mutex m_queue_mutex;
|
||||
std::mutex m_shutdown_mutex;
|
||||
Common::CondVar m_work_available;
|
||||
Common::CondVar m_idle;
|
||||
std::array<std::deque<Submission>, QueueCount> m_queues;
|
||||
std::deque<Common::UniqueFunction<void>> m_commands;
|
||||
std::atomic_uint32_t m_pending_commands {0};
|
||||
uint32_t m_next_queue = 0;
|
||||
uint32_t m_submission_count = 0;
|
||||
bool m_processing = false;
|
||||
bool m_graphics_done = true;
|
||||
bool m_accepting = true;
|
||||
bool m_stopping = false;
|
||||
bool m_shutdown_complete = false;
|
||||
|
||||
std::unique_ptr<CommandProcessor> m_gfx_cp;
|
||||
std::array<std::unique_ptr<CommandProcessor>, ComputeQueueCount> m_compute_cp;
|
||||
|
||||
uint64_t m_submit_id = 0;
|
||||
std::atomic_int m_done_num = 0;
|
||||
std::jthread m_thread;
|
||||
|
||||
friend class CommandProcessor;
|
||||
};
|
||||
|
||||
static bool GraphicsRunDebugDumpEnabled() {
|
||||
return Config::GraphicsDebugDumpEnabled() &&
|
||||
Config::GetPrintfDirection() != Config::OutputDirection::Silent;
|
||||
Config::GetPrintfDirection() != Config::LogDirection::Silent;
|
||||
}
|
||||
|
||||
GpuState::~GpuState() {
|
||||
GuestGpu::GuestGpu(RenderContext& renderer): m_renderer(renderer) {
|
||||
EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread());
|
||||
GraphicsInitJmpTables();
|
||||
m_gfx_cp = std::make_unique<CommandProcessor>(renderer, 0);
|
||||
m_thread = std::jthread(ThreadRun, this);
|
||||
}
|
||||
|
||||
GuestGpu::~GuestGpu() {
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void GpuState::Shutdown() {
|
||||
void GuestGpu::Shutdown() {
|
||||
std::lock_guard shutdown_lock(m_shutdown_mutex);
|
||||
if (m_shutdown_complete) {
|
||||
return;
|
||||
@@ -184,12 +108,12 @@ void GpuState::Shutdown() {
|
||||
m_shutdown_complete = true;
|
||||
}
|
||||
|
||||
bool GpuState::IsStopping() {
|
||||
bool GuestGpu::IsStopping() {
|
||||
Common::LockGuard lock(m_queue_mutex);
|
||||
return m_stopping;
|
||||
}
|
||||
|
||||
void GpuState::SendCommand(Common::UniqueFunction<void>&& command) {
|
||||
void GuestGpu::SendCommand(Common::UniqueFunction<void>&& command) {
|
||||
EXIT_IF(!command);
|
||||
if (IsGpuThread()) {
|
||||
command();
|
||||
@@ -202,7 +126,7 @@ void GpuState::SendCommand(Common::UniqueFunction<void>&& command) {
|
||||
m_work_available.Signal();
|
||||
}
|
||||
|
||||
void GpuState::ProcessCommands() {
|
||||
void GuestGpu::ProcessCommands() {
|
||||
EXIT_IF(!IsGpuThread());
|
||||
while (m_pending_commands.load(std::memory_order_acquire) != 0) {
|
||||
Common::UniqueFunction<void> command;
|
||||
@@ -217,7 +141,7 @@ void GpuState::ProcessCommands() {
|
||||
}
|
||||
}
|
||||
|
||||
void GpuState::SendCommandSync(Common::UniqueFunction<void>&& command) {
|
||||
void GuestGpu::SendCommandSync(Common::UniqueFunction<void>&& command) {
|
||||
EXIT_IF(!command);
|
||||
if (IsGpuThread()) {
|
||||
command();
|
||||
@@ -231,38 +155,37 @@ void GpuState::SendCommandSync(Common::UniqueFunction<void>&& command) {
|
||||
done.acquire();
|
||||
}
|
||||
|
||||
void GpuState::Submit(uint32_t* cmd_draw_buffer, uint32_t num_draw_dw, uint32_t* cmd_const_buffer,
|
||||
uint32_t num_const_dw, bool trigger_agc_interrupt_on_done) {
|
||||
void GuestGpu::Submit(std::span<const uint32_t> draw_commands,
|
||||
std::span<const uint32_t> constant_commands) {
|
||||
if (draw_commands.empty()) {
|
||||
return;
|
||||
}
|
||||
GpuMutexLock lock(m_submission_mutex);
|
||||
Submission submission;
|
||||
submission.type = SubmissionType::Graphics;
|
||||
submission.queue_id = 0;
|
||||
submission.commands = OwnedCmdBuffer(cmd_draw_buffer, num_draw_dw);
|
||||
submission.constant_commands = OwnedCmdBuffer(cmd_const_buffer, num_const_dw);
|
||||
submission.trigger_agc_interrupt_on_done = trigger_agc_interrupt_on_done;
|
||||
submission.reset_processor = m_graphics_done;
|
||||
m_graphics_done = false;
|
||||
submission.type = SubmissionType::Graphics;
|
||||
submission.queue_id = 0;
|
||||
submission.commands = draw_commands;
|
||||
submission.constant_commands = constant_commands;
|
||||
submission.reset_processor = m_graphics_done;
|
||||
m_graphics_done = false;
|
||||
Enqueue(std::move(submission));
|
||||
}
|
||||
|
||||
void GpuState::SubmitCompute(uint32_t queue, uint32_t* cmd_buffer, uint32_t num_dw,
|
||||
bool trigger_agc_interrupt_on_done) {
|
||||
void GuestGpu::SubmitCompute(uint32_t queue, std::span<const uint32_t> commands) {
|
||||
EXIT_IF(commands.empty());
|
||||
GpuMutexLock lock(m_submission_mutex);
|
||||
|
||||
constexpr uint32_t compute_queue_base = 0x20u;
|
||||
EXIT_NOT_IMPLEMENTED(queue < compute_queue_base ||
|
||||
queue >= compute_queue_base + ComputeQueueCount);
|
||||
EXIT_NOT_IMPLEMENTED(queue < ComputeQueueBase || queue >= ComputeQueueBase + ComputeQueueCount);
|
||||
|
||||
const auto compute_queue = queue - compute_queue_base;
|
||||
const auto compute_queue = queue - ComputeQueueBase;
|
||||
Submission submission;
|
||||
submission.type = SubmissionType::Compute;
|
||||
submission.queue_id = 1 + compute_queue;
|
||||
submission.commands = OwnedCmdBuffer(cmd_buffer, num_dw);
|
||||
submission.trigger_agc_interrupt_on_done = trigger_agc_interrupt_on_done;
|
||||
submission.type = SubmissionType::Compute;
|
||||
submission.queue_id = 1 + compute_queue;
|
||||
submission.commands = commands;
|
||||
Enqueue(std::move(submission));
|
||||
}
|
||||
|
||||
void GpuState::SubmitFlipPreparation(uint64_t request_id) {
|
||||
void GuestGpu::SubmitFlipPreparation(uint64_t request_id) {
|
||||
GpuMutexLock lock(m_submission_mutex);
|
||||
Submission submission;
|
||||
submission.type = SubmissionType::FlipPreparation;
|
||||
@@ -273,7 +196,7 @@ void GpuState::SubmitFlipPreparation(uint64_t request_id) {
|
||||
Enqueue(std::move(submission));
|
||||
}
|
||||
|
||||
void GpuState::Done() {
|
||||
void GuestGpu::Done() {
|
||||
GpuMutexLock lock(m_submission_mutex);
|
||||
if (!IsGpuThread()) {
|
||||
WaitForIdle();
|
||||
@@ -282,18 +205,18 @@ void GpuState::Done() {
|
||||
m_done_num++;
|
||||
}
|
||||
|
||||
int GpuState::GetFrameNum() {
|
||||
int GuestGpu::GetFrameNum() const {
|
||||
return m_done_num;
|
||||
}
|
||||
|
||||
CommandProcessor& GpuState::GetProcessor(uint32_t queue_id) {
|
||||
CommandProcessor& GuestGpu::GetProcessor(uint32_t queue_id) {
|
||||
EXIT_IF(queue_id >= QueueCount);
|
||||
if (queue_id == 0) {
|
||||
return *m_gfx_cp;
|
||||
}
|
||||
auto& processor = m_compute_cp[queue_id - 1];
|
||||
if (processor == nullptr) {
|
||||
processor = std::make_unique<CommandProcessor>(m_renderer);
|
||||
processor = std::make_unique<CommandProcessor>(m_renderer, ComputeQueueBase + queue_id - 1);
|
||||
}
|
||||
return *processor;
|
||||
}
|
||||
@@ -346,8 +269,7 @@ void CommandProcessor::BufferFlush() {
|
||||
}
|
||||
|
||||
void CommandProcessor::BufferFlushAndWait() {
|
||||
auto& submitted = GetScheduler().FlushAndGetSubmitted();
|
||||
submitted.WaitForFenceOnly();
|
||||
GetScheduler().FlushAndWait();
|
||||
}
|
||||
|
||||
void CommandProcessor::BufferWait() {
|
||||
@@ -374,6 +296,12 @@ void CommandProcessor::WaitDeDiff(uint32_t diff) {
|
||||
}
|
||||
}
|
||||
|
||||
void CommandProcessor::WaitForRewind(bool valid) {
|
||||
if (!valid) {
|
||||
SuspendPm4();
|
||||
}
|
||||
}
|
||||
|
||||
void CommandProcessor::IncrementDe() {
|
||||
m_de_count++;
|
||||
}
|
||||
@@ -426,10 +354,8 @@ template void CommandProcessor::WaitRegMem<uint64_t>(uint32_t, const uint64_t*,
|
||||
|
||||
void CommandProcessor::WriteData(uint32_t* dst, const uint32_t* src, uint32_t dw_num,
|
||||
uint32_t write_control) {
|
||||
const uint32_t dst_sel = ((write_control >> 30u) & 0x1u) | ((write_control >> 7u) & 0x1eu);
|
||||
const uint32_t cache_policy = (write_control >> 25u) & 0x3u;
|
||||
const uint32_t increment = (write_control >> 16u) & 0x1u;
|
||||
const uint32_t write_confirm = (write_control >> 20u) & 0x1u;
|
||||
const uint32_t dst_sel = ((write_control >> 30u) & 0x1u) | ((write_control >> 7u) & 0x1eu);
|
||||
const bool write_one_address = ((write_control >> 16u) & 0x1u) != 0;
|
||||
|
||||
switch (dst_sel) {
|
||||
case 0:
|
||||
@@ -439,16 +365,17 @@ void CommandProcessor::WriteData(uint32_t* dst, const uint32_t* src, uint32_t dw
|
||||
case 6: break;
|
||||
default: EXIT("unsupported writeData destination selector 0x%02" PRIx32 "\n", dst_sel);
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(increment != 0);
|
||||
|
||||
if (cache_policy > 3 || write_confirm > 1) {
|
||||
LOGF("\t warning: unexpected write_data control 0x%08" PRIx32 "\n", write_control);
|
||||
}
|
||||
if (dw_num == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(dst, src, static_cast<size_t>(dw_num) * sizeof(uint32_t));
|
||||
if (write_one_address) {
|
||||
for (uint32_t i = 0; i < dw_num; i++) {
|
||||
dst[0] = src[i];
|
||||
}
|
||||
} else {
|
||||
memcpy(dst, src, static_cast<size_t>(dw_num) * sizeof(uint32_t));
|
||||
}
|
||||
}
|
||||
|
||||
void CommandProcessor::WriteReferenceClock(uint64_t dst_address, uint32_t num_bytes) {
|
||||
@@ -489,11 +416,18 @@ void CommandProcessor::DmaData(uint8_t engine, uint8_t dst_sel, uint8_t dst_cach
|
||||
default: return false;
|
||||
}
|
||||
};
|
||||
if (dst_sel == 2) {
|
||||
// kNowhere discards the GL2 prefetch destination without a guest-visible write.
|
||||
if (src_sel != 3) {
|
||||
EXIT("unsupported dmaData nowhere source selector 0x%02" PRIx8 "\n", src_sel);
|
||||
}
|
||||
return;
|
||||
}
|
||||
bool dst_gds = false;
|
||||
if (!decode_gds(dst_sel, dst_gds)) {
|
||||
EXIT("unsupported dmaData destination selector 0x%02" PRIx8 "\n", dst_sel);
|
||||
}
|
||||
auto& buffer_cache = GetGpuResources().GetBufferCache();
|
||||
auto& buffer_cache = m_renderer.GetBufferCache();
|
||||
if (src_sel == 2) {
|
||||
buffer_cache.FillBuffer(
|
||||
dst_address_or_offset, num_bytes,
|
||||
@@ -511,7 +445,7 @@ void CommandProcessor::DmaData(uint8_t engine, uint8_t dst_sel, uint8_t dst_cach
|
||||
dst_gds, src_gds);
|
||||
}
|
||||
|
||||
void GpuState::Enqueue(Submission submission) {
|
||||
void GuestGpu::Enqueue(Submission submission) {
|
||||
EXIT_IF(submission.queue_id >= QueueCount);
|
||||
Common::LockGuard lock(m_queue_mutex);
|
||||
EXIT_IF(!m_accepting);
|
||||
@@ -520,15 +454,15 @@ void GpuState::Enqueue(Submission submission) {
|
||||
m_work_available.Signal();
|
||||
}
|
||||
|
||||
void GpuState::WaitForIdle() {
|
||||
void GuestGpu::WaitForIdle() {
|
||||
Common::LockGuard lock(m_queue_mutex);
|
||||
while (m_processing || !m_commands.empty() || m_submission_count != 0) {
|
||||
m_idle.Wait(&m_queue_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
void GpuState::ThreadRun(void* data) {
|
||||
auto* gpu = static_cast<GpuState*>(data);
|
||||
void GuestGpu::ThreadRun(void* data) {
|
||||
auto* gpu = static_cast<GuestGpu*>(data);
|
||||
EXIT_IF(gpu == nullptr);
|
||||
KYTY_PROFILER_THREAD("Thread_Gpu");
|
||||
g_gpu_thread = true;
|
||||
@@ -624,9 +558,9 @@ void GpuState::ThreadRun(void* data) {
|
||||
}
|
||||
}
|
||||
|
||||
bool GpuState::Process(Submission& submission) {
|
||||
auto& cp = GetProcessor(submission.queue_id);
|
||||
bool GuestGpu::Process(Submission& submission) {
|
||||
const bool first_slice = !submission.started;
|
||||
auto& cp = GetProcessor(submission.queue_id);
|
||||
|
||||
if (first_slice && submission.reset_processor) {
|
||||
cp.Reset();
|
||||
@@ -645,21 +579,20 @@ bool GpuState::Process(Submission& submission) {
|
||||
switch (submission.type) {
|
||||
case SubmissionType::Graphics: {
|
||||
bool progressed = false;
|
||||
submission.constant_complete |= submission.constant_commands.Empty();
|
||||
submission.constant_complete |= submission.constant_commands.empty();
|
||||
for (;;) {
|
||||
bool round_progress = false;
|
||||
if (!submission.constant_complete) {
|
||||
submission.constant_complete =
|
||||
cp.Process(
|
||||
submission.constant_execution, submission.constant_commands.Data(),
|
||||
submission.constant_commands.Size()) == Pm4ProcessResult::Complete;
|
||||
cp.Process(submission.constant_execution, submission.constant_commands) ==
|
||||
Pm4ProcessResult::Complete;
|
||||
round_progress |= submission.constant_execution.MadeProgress();
|
||||
}
|
||||
cp.SetCeComplete(submission.constant_complete);
|
||||
if (!submission.command_complete) {
|
||||
submission.command_complete =
|
||||
cp.Process(submission.command_execution, submission.commands.Data(),
|
||||
submission.commands.Size()) == Pm4ProcessResult::Complete;
|
||||
cp.Process(submission.command_execution, submission.commands) ==
|
||||
Pm4ProcessResult::Complete;
|
||||
round_progress |= submission.command_execution.MadeProgress();
|
||||
}
|
||||
progressed |= round_progress;
|
||||
@@ -670,20 +603,17 @@ bool GpuState::Process(Submission& submission) {
|
||||
}
|
||||
if (progressed) {
|
||||
if (complete) {
|
||||
m_renderer.GetGpuResources().RunGarbageCollector();
|
||||
m_renderer.RunGarbageCollector();
|
||||
}
|
||||
cp.BufferFlush();
|
||||
} else if (complete) {
|
||||
m_renderer.GetGpuResources().RunGarbageCollector();
|
||||
}
|
||||
if (complete && submission.trigger_agc_interrupt_on_done) {
|
||||
m_renderer.TriggerEopEvent(0);
|
||||
m_renderer.RunGarbageCollector();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SubmissionType::Compute: {
|
||||
const auto num_dw = submission.commands.Size();
|
||||
auto* buffer = submission.commands.Data();
|
||||
const auto num_dw = static_cast<uint32_t>(submission.commands.size());
|
||||
const auto* buffer = submission.commands.data();
|
||||
static uint32_t compute_batch_log_count = 0;
|
||||
if (first_slice && num_dw <= 128 && compute_batch_log_count++ < 32) {
|
||||
LOGF("compute direct batch: data=0x%016" PRIx64 ", num_dw=%" PRIu32 "\n",
|
||||
@@ -695,23 +625,20 @@ bool GpuState::Process(Submission& submission) {
|
||||
if (first_slice) {
|
||||
GraphicsDbgDumpDcb("cc", num_dw, buffer);
|
||||
}
|
||||
complete = cp.Process(submission.command_execution, buffer, num_dw) ==
|
||||
complete = cp.Process(submission.command_execution, submission.commands) ==
|
||||
Pm4ProcessResult::Complete;
|
||||
if (submission.command_execution.MadeProgress()) {
|
||||
if (complete) {
|
||||
m_renderer.GetGpuResources().RunGarbageCollector();
|
||||
m_renderer.RunGarbageCollector();
|
||||
}
|
||||
cp.BufferFlush();
|
||||
} else if (complete) {
|
||||
m_renderer.GetGpuResources().RunGarbageCollector();
|
||||
}
|
||||
if (complete && submission.trigger_agc_interrupt_on_done) {
|
||||
Sync::TriggerAgcUserInterrupt();
|
||||
m_renderer.RunGarbageCollector();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SubmissionType::FlipPreparation:
|
||||
m_renderer.GetGpuResources().RunGarbageCollector();
|
||||
m_renderer.RunGarbageCollector();
|
||||
cp.PrepareCpuFlip(submission.flip_request_id);
|
||||
break;
|
||||
}
|
||||
@@ -719,12 +646,13 @@ bool GpuState::Process(Submission& submission) {
|
||||
return complete;
|
||||
}
|
||||
|
||||
Pm4ProcessResult CommandProcessor::Process(Pm4Execution& execution, uint32_t* buffer,
|
||||
uint32_t size_dw) {
|
||||
Pm4ProcessResult CommandProcessor::Process(Pm4Execution& execution,
|
||||
std::span<const uint32_t> commands) {
|
||||
KYTY_PROFILER_BLOCK("CommandProcessor::Process");
|
||||
EXIT_IF(g_current_execution != nullptr);
|
||||
if (execution.m_buffer_stack.empty() && size_dw != 0) {
|
||||
execution.m_buffer_stack.push_back({buffer, size_dw, size_dw, 0});
|
||||
EXIT_IF(commands.size() > UINT32_MAX);
|
||||
if (execution.m_buffer_stack.empty() && !commands.empty()) {
|
||||
execution.m_buffer_stack.push_back({commands});
|
||||
}
|
||||
execution.m_suspended = false;
|
||||
execution.m_made_progress = false;
|
||||
@@ -749,14 +677,14 @@ Pm4ProcessResult CommandProcessor::Process(Pm4Execution& execution, uint32_t* bu
|
||||
: Pm4ProcessResult::Blocked;
|
||||
}
|
||||
|
||||
void CommandProcessor::ProcessIndirectBuffer(uint32_t* buffer, uint32_t size_dw) {
|
||||
void CommandProcessor::ProcessIndirectBuffer(std::span<const uint32_t> commands) {
|
||||
EXIT_IF(g_current_execution == nullptr);
|
||||
if (size_dw == 0) {
|
||||
if (commands.empty()) {
|
||||
return;
|
||||
}
|
||||
auto& execution = *g_current_execution;
|
||||
const auto stop_depth = execution.m_buffer_stack.size();
|
||||
execution.m_buffer_stack.push_back({buffer, size_dw, size_dw, 0});
|
||||
execution.m_buffer_stack.push_back({commands});
|
||||
ProcessPm4(execution, stop_depth);
|
||||
}
|
||||
|
||||
@@ -772,29 +700,28 @@ void CommandProcessor::ProcessPm4(Pm4Execution& execution, size_t stop_depth) {
|
||||
}
|
||||
const auto buffer_index = execution.m_buffer_stack.size() - 1;
|
||||
auto& cursor = execution.m_buffer_stack[buffer_index];
|
||||
EXIT_IF(cursor.offset_dw > cursor.commands.size());
|
||||
if (cursor.deferred_advance_dw != 0) {
|
||||
EXIT_IF(cursor.deferred_advance_dw > cursor.remaining_dw);
|
||||
cursor.next_packet += cursor.deferred_advance_dw;
|
||||
cursor.remaining_dw -= cursor.deferred_advance_dw;
|
||||
EXIT_IF(cursor.deferred_advance_dw > cursor.commands.size() - cursor.offset_dw);
|
||||
cursor.offset_dw += cursor.deferred_advance_dw;
|
||||
cursor.deferred_advance_dw = 0;
|
||||
execution.m_made_progress = true;
|
||||
continue;
|
||||
}
|
||||
if (cursor.remaining_dw == 0) {
|
||||
if (cursor.offset_dw == cursor.commands.size()) {
|
||||
execution.m_buffer_stack.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* const packet = cursor.next_packet;
|
||||
const auto remaining_dw = cursor.remaining_dw;
|
||||
const auto total_dw = cursor.total_dw;
|
||||
const auto packet_header = packet[0];
|
||||
const auto opcode = (packet_header >> 8u) & 0xffu;
|
||||
const auto* const packet = cursor.commands.data() + cursor.offset_dw;
|
||||
const auto total_dw = static_cast<uint32_t>(cursor.commands.size());
|
||||
const auto remaining_dw = total_dw - cursor.offset_dw;
|
||||
const auto packet_header = packet[0];
|
||||
const auto opcode = (packet_header >> 8u) & 0xffu;
|
||||
EXIT_NOT_IMPLEMENTED(remaining_dw > total_dw);
|
||||
|
||||
if (packet_header == 0x80000000u) {
|
||||
cursor.next_packet++;
|
||||
cursor.remaining_dw--;
|
||||
cursor.offset_dw++;
|
||||
execution.m_made_progress = true;
|
||||
continue;
|
||||
}
|
||||
@@ -829,8 +756,7 @@ void CommandProcessor::ProcessPm4(Pm4Execution& execution, size_t stop_depth) {
|
||||
dst, val, packet[1], packet[2]);
|
||||
}
|
||||
}
|
||||
cursor.next_packet += packet_dw;
|
||||
cursor.remaining_dw -= packet_dw;
|
||||
cursor.offset_dw += packet_dw;
|
||||
execution.m_made_progress = true;
|
||||
continue;
|
||||
}
|
||||
@@ -863,8 +789,7 @@ void CommandProcessor::ProcessPm4(Pm4Execution& execution, size_t stop_depth) {
|
||||
return;
|
||||
}
|
||||
EXIT_IF(execution.m_buffer_stack.size() != buffer_index + 1);
|
||||
execution.m_buffer_stack[buffer_index].next_packet += packet_dw;
|
||||
execution.m_buffer_stack[buffer_index].remaining_dw -= packet_dw;
|
||||
execution.m_buffer_stack[buffer_index].offset_dw += packet_dw;
|
||||
execution.m_made_progress = true;
|
||||
}
|
||||
}
|
||||
@@ -932,36 +857,21 @@ void CommandProcessor::SetPredication(uint32_t condition, uint32_t op, uint32_t
|
||||
}
|
||||
}
|
||||
|
||||
void CommandProcessor::DrawIndex(uint32_t index_count, const void* index_addr, uint32_t flags,
|
||||
uint32_t type, uint32_t instance_count, const void* object_ids,
|
||||
uint32_t render_target_slice_offset, int32_t vertex_offset_add,
|
||||
uint32_t first_instance) {
|
||||
void CommandProcessor::DrawIndex(DrawIndexArgs args) {
|
||||
CheckBuffer();
|
||||
|
||||
if (instance_count == 0) {
|
||||
instance_count = m_num_instances;
|
||||
args.index_type_and_size = m_index_type_and_size;
|
||||
if (args.instance_count == 0) {
|
||||
args.instance_count = m_num_instances;
|
||||
}
|
||||
if (object_ids != nullptr) {
|
||||
LOGF("\t draw indexed multi-instanced objectIds = 0x%016" PRIx64 "\n",
|
||||
reinterpret_cast<uint64_t>(object_ids));
|
||||
if (args.base_vertex != 0 || args.first_instance != 0) {
|
||||
LOGF("\t draw indexed offsets: base_vertex = %" PRId32 ", first_instance = %" PRIu32 "\n",
|
||||
args.base_vertex, args.first_instance);
|
||||
}
|
||||
if (render_target_slice_offset != 0) {
|
||||
LOGF("\t draw render target slice offset = %" PRIu32 "\n", render_target_slice_offset);
|
||||
}
|
||||
if (vertex_offset_add != 0 || first_instance != 0) {
|
||||
LOGF("\t draw indexed offsets: vertex_offset_add = %" PRId32 ", first_instance = %" PRIu32
|
||||
"\n",
|
||||
vertex_offset_add, first_instance);
|
||||
}
|
||||
m_renderer.GetRenderExecutor().DrawIndex(
|
||||
m_submit_id, CurrentBuffer(), m_index_type_and_size, index_count, index_addr, flags, type,
|
||||
instance_count, render_target_slice_offset, vertex_offset_add, first_instance);
|
||||
m_renderer.GetRenderExecutor().DrawIndex(m_submit_id, CurrentBuffer(), args);
|
||||
}
|
||||
|
||||
void CommandProcessor::DrawIndexOffset(uint32_t index_offset, uint32_t index_count,
|
||||
uint32_t flags) {
|
||||
CheckBuffer();
|
||||
|
||||
void CommandProcessor::DrawIndexOffset(uint32_t index_offset, uint32_t index_count) {
|
||||
uint64_t index_size = 0;
|
||||
switch (m_index_type_and_size) {
|
||||
case 0: index_size = 2; break;
|
||||
@@ -973,25 +883,10 @@ void CommandProcessor::DrawIndexOffset(uint32_t index_offset, uint32_t index_cou
|
||||
auto* index_addr = reinterpret_cast<const void*>(
|
||||
m_index_base_addr + static_cast<uint64_t>(index_offset) * index_size);
|
||||
|
||||
m_renderer.GetRenderExecutor().DrawIndex(m_submit_id, CurrentBuffer(), m_index_type_and_size,
|
||||
index_count, index_addr, flags, 1, m_num_instances);
|
||||
DrawIndex({.index_count = index_count, .index_addr = index_addr});
|
||||
}
|
||||
|
||||
void CommandProcessor::DrawIndirect(uint32_t data_offset, uint32_t draw_initiator, bool indexed) {
|
||||
struct DrawIndirectArgs {
|
||||
uint32_t vertex_count_per_instance;
|
||||
uint32_t instance_count;
|
||||
uint32_t start_vertex_location;
|
||||
uint32_t start_instance_location;
|
||||
};
|
||||
struct DrawIndexedIndirectArgs {
|
||||
uint32_t index_count_per_instance;
|
||||
uint32_t instance_count;
|
||||
uint32_t start_index_location;
|
||||
uint32_t base_vertex_location;
|
||||
uint32_t start_instance_location;
|
||||
};
|
||||
|
||||
EXIT_NOT_IMPLEMENTED((draw_initiator & ~0x20u) != 2u);
|
||||
EXIT_NOT_IMPLEMENTED(m_draw_indirect_args_base_addr == 0);
|
||||
|
||||
@@ -1013,8 +908,11 @@ void CommandProcessor::DrawIndirect(uint32_t data_offset, uint32_t draw_initiato
|
||||
}
|
||||
}
|
||||
m_num_instances = args.instance_count;
|
||||
SubmitNonIndexedDraw(args.vertex_count_per_instance, 0, 0, args.start_vertex_location,
|
||||
args.start_instance_location);
|
||||
DrawIndexAuto({.vertex_count = args.vertex_count_per_instance,
|
||||
.instance_count = args.instance_count,
|
||||
.first_vertex = args.start_vertex_location,
|
||||
.first_instance = args.start_instance_location,
|
||||
.offset_source = DrawOffsetSource::IndirectArgs});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1055,28 +953,18 @@ void CommandProcessor::DrawIndirect(uint32_t data_offset, uint32_t draw_initiato
|
||||
}
|
||||
|
||||
m_num_instances = args.instance_count;
|
||||
DrawIndex(index_count, index_addr, 0, 1, args.instance_count, nullptr, 0,
|
||||
static_cast<int32_t>(args.base_vertex_location), args.start_instance_location);
|
||||
DrawIndex({.index_count = index_count,
|
||||
.index_addr = index_addr,
|
||||
.instance_count = args.instance_count,
|
||||
.base_vertex = static_cast<int32_t>(args.base_vertex_location),
|
||||
.first_instance = args.start_instance_location,
|
||||
.offset_source = DrawOffsetSource::IndirectArgs});
|
||||
}
|
||||
|
||||
void CommandProcessor::DrawIndirectMulti(uint32_t data_offset, uint32_t max_count_or_count,
|
||||
const volatile uint32_t* count_addr,
|
||||
uint32_t stride_in_bytes, uint32_t draw_initiator,
|
||||
bool indexed) {
|
||||
struct DrawIndirectArgs {
|
||||
uint32_t vertex_count_per_instance;
|
||||
uint32_t instance_count;
|
||||
uint32_t start_vertex_location;
|
||||
uint32_t start_instance_location;
|
||||
};
|
||||
struct DrawIndexedIndirectArgs {
|
||||
uint32_t index_count_per_instance;
|
||||
uint32_t instance_count;
|
||||
uint32_t start_index_location;
|
||||
uint32_t base_vertex_location;
|
||||
uint32_t start_instance_location;
|
||||
};
|
||||
|
||||
EXIT_NOT_IMPLEMENTED((draw_initiator & ~0x20u) != 2u);
|
||||
EXIT_NOT_IMPLEMENTED(m_draw_indirect_args_base_addr == 0);
|
||||
|
||||
@@ -1113,8 +1001,11 @@ void CommandProcessor::DrawIndirectMulti(uint32_t data_offset, uint32_t max_coun
|
||||
}
|
||||
}
|
||||
m_num_instances = args->instance_count;
|
||||
SubmitNonIndexedDraw(args->vertex_count_per_instance, 0, 0, args->start_vertex_location,
|
||||
args->start_instance_location);
|
||||
DrawIndexAuto({.vertex_count = args->vertex_count_per_instance,
|
||||
.instance_count = args->instance_count,
|
||||
.first_vertex = args->start_vertex_location,
|
||||
.first_instance = args->start_instance_location,
|
||||
.offset_source = DrawOffsetSource::IndirectArgs});
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1156,13 +1047,19 @@ void CommandProcessor::DrawIndirectMulti(uint32_t data_offset, uint32_t max_coun
|
||||
}
|
||||
|
||||
m_num_instances = args->instance_count;
|
||||
DrawIndex(index_count, index_addr, 0, 1, args->instance_count, nullptr, 0,
|
||||
static_cast<int32_t>(args->base_vertex_location), args->start_instance_location);
|
||||
DrawIndex({.index_count = index_count,
|
||||
.index_addr = index_addr,
|
||||
.instance_count = args->instance_count,
|
||||
.base_vertex = static_cast<int32_t>(args->base_vertex_location),
|
||||
.first_instance = args->start_instance_location,
|
||||
.offset_source = DrawOffsetSource::IndirectArgs});
|
||||
}
|
||||
}
|
||||
|
||||
void CommandProcessor::DispatchDirect(uint32_t thread_group_x, uint32_t thread_group_y,
|
||||
uint32_t thread_group_z, uint32_t mode) {
|
||||
m_sh_ctx.SetCsWaveSize(Pm4::ComputeWaveSize(mode));
|
||||
|
||||
uint32_t frame_num = 0;
|
||||
// uint32_t local_x = 1;
|
||||
// uint32_t local_y = 1;
|
||||
@@ -1246,19 +1143,13 @@ void CommandProcessor::DispatchIndirect(uint32_t data_offset, uint32_t mode) {
|
||||
DispatchDirect(args->thread_group_x, args->thread_group_y, args->thread_group_z, mode);
|
||||
}
|
||||
|
||||
void CommandProcessor::DrawIndexAuto(uint32_t index_count, uint32_t flags,
|
||||
uint32_t render_target_slice_offset) {
|
||||
SubmitNonIndexedDraw(index_count, flags, render_target_slice_offset, 0, 0);
|
||||
}
|
||||
|
||||
void CommandProcessor::SubmitNonIndexedDraw(uint32_t vertex_count, uint32_t flags,
|
||||
uint32_t render_target_slice_offset,
|
||||
uint32_t first_vertex, uint32_t first_instance) {
|
||||
void CommandProcessor::DrawIndexAuto(DrawAutoArgs args) {
|
||||
CheckBuffer();
|
||||
|
||||
m_renderer.GetRenderExecutor().DrawAuto(m_submit_id, CurrentBuffer(), vertex_count, flags,
|
||||
render_target_slice_offset, m_num_instances,
|
||||
first_vertex, first_instance);
|
||||
if (args.instance_count == 0) {
|
||||
args.instance_count = m_num_instances;
|
||||
}
|
||||
m_renderer.GetRenderExecutor().DrawAuto(m_submit_id, CurrentBuffer(), args);
|
||||
}
|
||||
|
||||
void CommandProcessor::WaitFlipDone(uint32_t video_out_handle, uint32_t display_buffer_index) {
|
||||
@@ -1305,7 +1196,14 @@ void CommandProcessor::WriteAtEndOfPipe(uint32_t cache_policy, uint32_t event_wr
|
||||
switch (interrupt_selector) {
|
||||
case 0x00:
|
||||
case 0x03: with_interrupt = false; break;
|
||||
case 0x01: Sync::TriggerEopEventAtEndOfPipe(CurrentBuffer(), interrupt_context_id); return;
|
||||
case 0x01:
|
||||
if (!IsAsyncComputeQueue()) {
|
||||
Sync::TriggerEopEventAtEndOfPipe(CurrentBuffer(), m_interrupt_event_id,
|
||||
interrupt_context_id);
|
||||
return;
|
||||
}
|
||||
with_interrupt = true;
|
||||
break;
|
||||
case 0x02: with_interrupt = true; break;
|
||||
default: EXIT("unknown interrupt_selector\n");
|
||||
}
|
||||
@@ -1318,10 +1216,11 @@ void CommandProcessor::WriteAtEndOfPipe(uint32_t cache_policy, uint32_t event_wr
|
||||
if (with_interrupt) {
|
||||
if (with_writeback) {
|
||||
Sync::WriteAtEndOfPipeWithInterruptWriteBack32(m_submit_id, CurrentBuffer(), dst,
|
||||
data, interrupt_context_id);
|
||||
data, m_interrupt_event_id,
|
||||
interrupt_context_id);
|
||||
} else {
|
||||
Sync::WriteAtEndOfPipeWithInterrupt32(m_submit_id, CurrentBuffer(), dst, data,
|
||||
interrupt_context_id);
|
||||
m_interrupt_event_id, interrupt_context_id);
|
||||
}
|
||||
} else if (with_writeback) {
|
||||
Sync::WriteAtEndOfPipeWithWriteBack32(m_submit_id, CurrentBuffer(), dst, data);
|
||||
@@ -1336,12 +1235,12 @@ void CommandProcessor::WriteAtEndOfPipe(uint32_t cache_policy, uint32_t event_wr
|
||||
if (eop_event_type == 0x2f && cache_action == 0x00 && event_index == 0x06) {
|
||||
auto* dst = static_cast<uint32_t*>(dst_gpu_addr);
|
||||
SynchronizeGpu();
|
||||
Sync::ReadGds(m_renderer.GetBufferCache().GetGdsBuffer(), dst, value & 0xffffu,
|
||||
Sync::ReadGds(*m_renderer.GetBufferCache().GetGdsBuffer(), dst, value & 0xffffu,
|
||||
value >> 16u);
|
||||
Sync::WriteAtEndOfPipeGds32(m_submit_id, CurrentBuffer(), dst, value & 0xffffu,
|
||||
value >> 16u);
|
||||
if (with_interrupt) {
|
||||
m_renderer.TriggerEopEvent(interrupt_context_id);
|
||||
m_renderer.TriggerInterrupt(m_interrupt_event_id, interrupt_context_id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1367,10 +1266,12 @@ void CommandProcessor::WriteAtEndOfPipe(uint32_t cache_policy, uint32_t event_wr
|
||||
if (with_interrupt) {
|
||||
if (with_writeback) {
|
||||
Sync::WriteAtEndOfPipeWithInterruptWriteBack64(
|
||||
m_submit_id, CurrentBuffer(), dst, value, interrupt_context_id);
|
||||
m_submit_id, CurrentBuffer(), dst, value, m_interrupt_event_id,
|
||||
interrupt_context_id);
|
||||
} else {
|
||||
Sync::WriteAtEndOfPipeWithInterrupt64(m_submit_id, CurrentBuffer(), dst,
|
||||
value, interrupt_context_id);
|
||||
value, m_interrupt_event_id,
|
||||
interrupt_context_id);
|
||||
}
|
||||
} else if (with_writeback) {
|
||||
Sync::WriteAtEndOfPipeWithWriteBack64(m_submit_id, CurrentBuffer(), dst,
|
||||
@@ -1384,16 +1285,21 @@ void CommandProcessor::WriteAtEndOfPipe(uint32_t cache_policy, uint32_t event_wr
|
||||
case 0x00:
|
||||
switch (eop_event_type) {
|
||||
case 0x04:
|
||||
if (event_index == 0x05) {
|
||||
write64(false);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 0x14:
|
||||
case 0x28:
|
||||
if ((eop_event_type == 0x04 && event_index == 0x05) ||
|
||||
(eop_event_type == 0x28 && event_index == 0x00)) {
|
||||
case 0x2f:
|
||||
if (event_index == 0x00) {
|
||||
write64(false);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 0x2b:
|
||||
case 0x2d:
|
||||
case 0x2f:
|
||||
case 0x30:
|
||||
if (event_index == 0x00 && !with_interrupt) {
|
||||
write64(false);
|
||||
@@ -1444,26 +1350,35 @@ void CommandProcessor::WriteAtEndOfPipe(uint32_t cache_policy, uint32_t event_wr
|
||||
case 0x04:
|
||||
if constexpr (sizeof(T) == sizeof(uint64_t)) {
|
||||
const auto clock = Sync::ReadReferenceClock();
|
||||
std::memcpy(dst_gpu_addr, &clock, sizeof(clock));
|
||||
auto* dst = static_cast<uint64_t*>(dst_gpu_addr);
|
||||
std::memcpy(dst, &clock, sizeof(clock));
|
||||
switch (cache_action) {
|
||||
case 0x00:
|
||||
if (((eop_event_type == 0x04 && event_index == 0x05) ||
|
||||
(eop_event_type == 0x28 && event_index == 0x00)) &&
|
||||
!with_interrupt) {
|
||||
Sync::WriteAtEndOfPipeClockCounter(m_submit_id, CurrentBuffer(),
|
||||
static_cast<uint64_t*>(dst_gpu_addr),
|
||||
clock);
|
||||
if ((eop_event_type == 0x04 && event_index == 0x05) ||
|
||||
(eop_event_type == 0x28 && event_index == 0x00)) {
|
||||
if (with_interrupt) {
|
||||
Sync::WriteAtEndOfPipeWithInterrupt64(
|
||||
m_submit_id, CurrentBuffer(), dst, clock, m_interrupt_event_id,
|
||||
interrupt_context_id);
|
||||
} else {
|
||||
Sync::WriteAtEndOfPipeClockCounter(m_submit_id, CurrentBuffer(),
|
||||
dst, clock);
|
||||
}
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 0x38:
|
||||
if (((eop_event_type == 0x04 &&
|
||||
(event_index == 0x00 || event_index == 0x05)) ||
|
||||
(eop_event_type == 0x28 && event_index == 0x00)) &&
|
||||
!with_interrupt) {
|
||||
Sync::WriteAtEndOfPipeClockCounterWithWriteBack(
|
||||
m_submit_id, CurrentBuffer(), static_cast<uint64_t*>(dst_gpu_addr),
|
||||
clock);
|
||||
if ((eop_event_type == 0x04 &&
|
||||
(event_index == 0x00 || event_index == 0x05)) ||
|
||||
(eop_event_type == 0x28 && event_index == 0x00)) {
|
||||
if (with_interrupt) {
|
||||
Sync::WriteAtEndOfPipeWithInterruptWriteBack64(
|
||||
m_submit_id, CurrentBuffer(), dst, clock, m_interrupt_event_id,
|
||||
interrupt_context_id);
|
||||
} else {
|
||||
Sync::WriteAtEndOfPipeClockCounterWithWriteBack(
|
||||
m_submit_id, CurrentBuffer(), dst, clock);
|
||||
}
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@@ -1520,15 +1435,17 @@ void CommandProcessor::EmitGlobalBarrier() {
|
||||
void CommandProcessor::TriggerEopEventAtEndOfPipe(uint32_t interrupt_context_id) {
|
||||
CheckBuffer();
|
||||
|
||||
Sync::TriggerEopEventAtEndOfPipe(CurrentBuffer(), interrupt_context_id);
|
||||
Sync::TriggerEopEventAtEndOfPipe(CurrentBuffer(), m_interrupt_event_id, interrupt_context_id);
|
||||
}
|
||||
|
||||
void CommandProcessor::TriggerEvent(uint32_t event_type, uint32_t event_index) {
|
||||
void CommandProcessor::TriggerEvent(uint32_t event_type, uint32_t event_index,
|
||||
uint64_t event_address) {
|
||||
if (GraphicsRunDebugDumpEnabled()) {
|
||||
LOGF("CommandProcessor::TriggerEvent()\n"
|
||||
"\t event_type = 0x%08" PRIx32 "\n"
|
||||
"\t event_index = 0x%08" PRIx32 "\n",
|
||||
event_type, event_index);
|
||||
"\t event_index = 0x%08" PRIx32 "\n"
|
||||
"\t address = 0x%016" PRIx64 "\n",
|
||||
event_type, event_index, event_address);
|
||||
}
|
||||
|
||||
const auto valid_cache_event_index = event_index == 0x00000000 || event_index == 0x00000007;
|
||||
@@ -1565,12 +1482,36 @@ void CommandProcessor::TriggerEvent(uint32_t event_type, uint32_t event_index) {
|
||||
case 0x0000001a:
|
||||
case 0x0000001b:
|
||||
case 0x00000038:
|
||||
case 0x00000039:
|
||||
case 0x0000003a:
|
||||
LOGF("\t temporary: ignoring unsupported event_write type 0x%08" PRIx32
|
||||
", index 0x%08" PRIx32 "\n",
|
||||
event_type, event_index);
|
||||
break;
|
||||
case 0x00000039: {
|
||||
if (event_index != 0x00000001 || event_address == 0 || (event_address & 0x7u) != 0) {
|
||||
EXIT("invalid occlusion-counter dump: index=0x%08" PRIx32 ", address=0x%016" PRIx64
|
||||
"\n",
|
||||
event_index, event_address);
|
||||
}
|
||||
static std::once_flag warning_once;
|
||||
std::call_once(warning_once, [] {
|
||||
std::printf("Warning: game uses occlusion queries, which are currently treated as "
|
||||
"always visible; GPU usage may be higher and FPS may be lower.\n");
|
||||
});
|
||||
|
||||
// Until host occlusion queries are implemented, publish an always-visible result. The
|
||||
// PS5 layout contains one interleaved begin/end pair per DB, and bit 63 marks a result
|
||||
// ready.
|
||||
constexpr uint64_t ready_bit = 1ull << 63u;
|
||||
constexpr uint64_t counter_mask = ready_bit - 1u;
|
||||
auto* results = reinterpret_cast<volatile uint64_t*>(event_address);
|
||||
const auto value = ready_bit | m_synthetic_occlusion_counter;
|
||||
for (uint32_t db = 0; db < 16u; db++) {
|
||||
results[db * 2u] = value;
|
||||
}
|
||||
m_synthetic_occlusion_counter = (m_synthetic_occlusion_counter + 1u) & counter_mask;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
EXIT("unknown event type: 0x%08" PRIx32 ", 0x%08" PRIx32 "\n", event_type, event_index);
|
||||
}
|
||||
@@ -1633,7 +1574,7 @@ void CommandProcessor::FlipWithInterrupt(uint32_t eop_event_type, uint32_t cache
|
||||
m_flip.flip_arg);
|
||||
Sync::WriteAtEndOfPipeWithInterruptWriteBackFlip32(
|
||||
m_submit_id, command, static_cast<uint32_t*>(dst_gpu_addr), value, m_flip.handle,
|
||||
m_flip.index, m_flip.flip_mode, m_flip.flip_arg, request);
|
||||
m_flip.index, m_flip.flip_mode, m_flip.flip_arg, request, m_interrupt_event_id);
|
||||
GetScheduler().Flush();
|
||||
}
|
||||
|
||||
@@ -1654,60 +1595,11 @@ void CommandProcessor::PrepareCpuFlip(uint64_t request_id) {
|
||||
}
|
||||
|
||||
void CommandProcessor::SynchronizeGpu() {
|
||||
GetScheduler().FinishCurrent();
|
||||
GetScheduler().Finish();
|
||||
}
|
||||
|
||||
Gpu::Gpu(RenderContext& renderer) {
|
||||
EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread());
|
||||
GraphicsInitJmpTables();
|
||||
m_state = std::make_unique<GpuState>(renderer);
|
||||
}
|
||||
|
||||
Gpu::~Gpu() = default;
|
||||
|
||||
void Gpu::Shutdown() {
|
||||
m_state->Shutdown();
|
||||
}
|
||||
|
||||
bool Gpu::IsStopping() {
|
||||
return m_state->IsStopping();
|
||||
}
|
||||
|
||||
void Gpu::SendCommand(Common::UniqueFunction<void>&& command) {
|
||||
m_state->SendCommand(std::move(command));
|
||||
}
|
||||
|
||||
void Gpu::SendCommandSync(Common::UniqueFunction<void>&& command) {
|
||||
m_state->SendCommandSync(std::move(command));
|
||||
}
|
||||
|
||||
void Gpu::Submit(uint32_t* draw_commands, uint32_t draw_size_dw, uint32_t* constant_commands,
|
||||
uint32_t constant_size_dw, bool trigger_agc_interrupt_on_done) {
|
||||
EXIT_IF(draw_commands == nullptr || draw_size_dw == 0);
|
||||
m_state->Submit(draw_commands, draw_size_dw, constant_commands, constant_size_dw,
|
||||
trigger_agc_interrupt_on_done);
|
||||
}
|
||||
|
||||
void Gpu::SubmitCompute(uint32_t queue, uint32_t* commands, uint32_t size_dw,
|
||||
bool trigger_agc_interrupt_on_done) {
|
||||
EXIT_IF(commands == nullptr || size_dw == 0);
|
||||
m_state->SubmitCompute(queue, commands, size_dw, trigger_agc_interrupt_on_done);
|
||||
}
|
||||
|
||||
void Gpu::SubmitFlipPreparation(uint64_t request_id) {
|
||||
m_state->SubmitFlipPreparation(request_id);
|
||||
}
|
||||
|
||||
void Gpu::Done() {
|
||||
m_state->Done();
|
||||
}
|
||||
|
||||
int Gpu::GetFrameNum() const {
|
||||
return m_state->GetFrameNum();
|
||||
}
|
||||
|
||||
bool Gpu::IsGpuThread() noexcept {
|
||||
return GpuState::IsGpuThread();
|
||||
bool GuestGpu::IsGpuThread() noexcept {
|
||||
return g_gpu_thread;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
@@ -3,38 +3,100 @@
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "common/common.h"
|
||||
#include "common/threads.h"
|
||||
#include "common/uniqueFunction.h"
|
||||
#include "graphics/guest_gpu/command_processor/commandProcessor.h"
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <span>
|
||||
#include <thread>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
class GpuState;
|
||||
class RenderContext;
|
||||
|
||||
class Gpu final {
|
||||
class GuestGpu final {
|
||||
public:
|
||||
explicit Gpu(RenderContext& renderer);
|
||||
~Gpu();
|
||||
KYTY_CLASS_NO_COPY(Gpu);
|
||||
explicit GuestGpu(RenderContext& renderer);
|
||||
~GuestGpu();
|
||||
KYTY_CLASS_NO_COPY(GuestGpu);
|
||||
|
||||
void Shutdown();
|
||||
[[nodiscard]] bool IsStopping();
|
||||
void SendCommand(Common::UniqueFunction<void>&& command);
|
||||
void SendCommandSync(Common::UniqueFunction<void>&& command);
|
||||
|
||||
void Submit(uint32_t* draw_commands, uint32_t draw_size_dw, uint32_t* constant_commands,
|
||||
uint32_t constant_size_dw, bool trigger_agc_interrupt_on_done = false);
|
||||
void SubmitCompute(uint32_t queue, uint32_t* commands, uint32_t size_dw,
|
||||
bool trigger_agc_interrupt_on_done = false);
|
||||
void SubmitFlipPreparation(uint64_t request_id);
|
||||
void Done();
|
||||
// Submitted command memory is borrowed and must remain valid until GPU execution completes.
|
||||
void Submit(std::span<const uint32_t> draw_commands,
|
||||
std::span<const uint32_t> constant_commands);
|
||||
void SubmitCompute(uint32_t queue, std::span<const uint32_t> commands);
|
||||
void SubmitFlipPreparation(uint64_t request_id);
|
||||
void Done();
|
||||
[[nodiscard]] int GetFrameNum() const;
|
||||
|
||||
[[nodiscard]] static bool IsGpuThread() noexcept;
|
||||
|
||||
private:
|
||||
std::unique_ptr<GpuState> m_state;
|
||||
static constexpr uint32_t ComputePipeCount = 7;
|
||||
static constexpr uint32_t QueuesPerComputePipe = 8;
|
||||
static constexpr uint32_t ComputeQueueCount = ComputePipeCount * QueuesPerComputePipe;
|
||||
static constexpr uint32_t ComputeQueueBase = 0x20;
|
||||
static constexpr uint32_t QueueCount = 1 + ComputeQueueCount;
|
||||
|
||||
enum class SubmissionType { Graphics, Compute, FlipPreparation };
|
||||
|
||||
struct Submission {
|
||||
SubmissionType type = SubmissionType::Graphics;
|
||||
uint32_t queue_id = 0;
|
||||
std::span<const uint32_t> commands;
|
||||
std::span<const uint32_t> constant_commands;
|
||||
Pm4Execution command_execution;
|
||||
Pm4Execution constant_execution;
|
||||
bool reset_processor = false;
|
||||
bool started = false;
|
||||
bool command_complete = false;
|
||||
bool constant_complete = false;
|
||||
bool blocked = false;
|
||||
uint64_t flip_request_id = 0;
|
||||
};
|
||||
|
||||
void Enqueue(Submission submission);
|
||||
void WaitForIdle();
|
||||
void ProcessCommands();
|
||||
bool Process(Submission& submission);
|
||||
static void ThreadRun(void* data);
|
||||
CommandProcessor& GetProcessor(uint32_t queue_id);
|
||||
|
||||
RenderContext& m_renderer;
|
||||
Common::Mutex m_submission_mutex;
|
||||
Common::Mutex m_queue_mutex;
|
||||
std::mutex m_shutdown_mutex;
|
||||
Common::CondVar m_work_available;
|
||||
Common::CondVar m_idle;
|
||||
std::array<std::deque<Submission>, QueueCount> m_queues;
|
||||
std::deque<Common::UniqueFunction<void>> m_commands;
|
||||
std::atomic_uint32_t m_pending_commands {0};
|
||||
uint32_t m_next_queue = 0;
|
||||
uint32_t m_submission_count = 0;
|
||||
bool m_processing = false;
|
||||
bool m_graphics_done = true;
|
||||
bool m_accepting = true;
|
||||
bool m_stopping = false;
|
||||
bool m_shutdown_complete = false;
|
||||
|
||||
std::unique_ptr<CommandProcessor> m_gfx_cp;
|
||||
std::array<std::unique_ptr<CommandProcessor>, ComputeQueueCount> m_compute_cp;
|
||||
|
||||
uint64_t m_submit_id = 0;
|
||||
std::atomic_int m_done_num = 0;
|
||||
std::jthread m_thread;
|
||||
|
||||
friend class CommandProcessor;
|
||||
};
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
|
||||
@@ -11,15 +11,6 @@ struct ColorBase {
|
||||
uint64_t addr = 0;
|
||||
};
|
||||
|
||||
struct ColorPitch {
|
||||
uint32_t pitch_div8_minus1 = 0;
|
||||
uint32_t fmask_pitch_div8_minus1 = 0;
|
||||
};
|
||||
|
||||
struct ColorSlice {
|
||||
uint32_t slice_div64_minus1 = 0;
|
||||
};
|
||||
|
||||
struct ColorView {
|
||||
uint32_t base_array_slice_index = 0;
|
||||
uint32_t last_array_slice_index = 0;
|
||||
@@ -29,25 +20,20 @@ struct ColorView {
|
||||
struct ColorInfo {
|
||||
bool fmask_compression_enable = false;
|
||||
// uint32_t fmask_compression_mode = 0;
|
||||
bool fmask_data_compression_disable = false;
|
||||
bool fmask_one_frag_mode = false;
|
||||
bool cmask_fast_clear_enable = false;
|
||||
bool dcc_compression_enable = false;
|
||||
bool blend_clamp = false;
|
||||
bool blend_bypass = false;
|
||||
bool round_mode = false;
|
||||
uint32_t cmask_is_linear = 0;
|
||||
uint32_t cmask_addr_type = 0;
|
||||
bool alt_tile_mode = false;
|
||||
uint32_t format = 0;
|
||||
uint32_t channel_type = 0;
|
||||
uint32_t channel_order = 0;
|
||||
bool fmask_data_compression_disable = false;
|
||||
bool fmask_one_frag_mode = false;
|
||||
bool cmask_fast_clear_enable = false;
|
||||
bool dcc_compression_enable = false;
|
||||
bool blend_clamp = false;
|
||||
bool blend_bypass = false;
|
||||
bool round_mode = false;
|
||||
Prospero::ChannelLayout format = Prospero::ChannelLayout::kInvalid;
|
||||
Prospero::ChannelType channel_type = Prospero::ChannelType::kUNorm;
|
||||
Prospero::ChannelOrder channel_order = Prospero::ChannelOrder::kStandard;
|
||||
};
|
||||
|
||||
struct ColorAttrib {
|
||||
bool force_dest_alpha_to_one = false;
|
||||
uint32_t tile_mode = 0;
|
||||
uint32_t fmask_tile_mode = 0;
|
||||
uint32_t num_samples = 0;
|
||||
uint32_t num_fragments = 0;
|
||||
};
|
||||
@@ -59,41 +45,33 @@ struct ColorAttrib2 {
|
||||
};
|
||||
|
||||
struct ColorAttrib3 {
|
||||
uint32_t depth = 0;
|
||||
uint32_t tile_mode = 0;
|
||||
uint32_t dimension = 0;
|
||||
bool cmask_pipe_aligned = false;
|
||||
bool dcc_pipe_aligned = false;
|
||||
uint32_t depth = 0;
|
||||
Prospero::TileMode tile_mode = Prospero::TileMode::kRenderTarget;
|
||||
uint32_t dimension = 0;
|
||||
bool metadata_pipe_aligned = false;
|
||||
bool write_vrs_rate_hint_to_cmask = false;
|
||||
};
|
||||
|
||||
struct ColorDccControl {
|
||||
uint32_t max_uncompressed_block_size = 0;
|
||||
uint32_t max_compressed_block_size = 0;
|
||||
uint32_t min_compressed_block_size = 0;
|
||||
uint32_t color_transform = 0;
|
||||
bool dcc_clear_key_enable = false;
|
||||
bool overwrite_combiner_disable = false;
|
||||
bool independent_64b_blocks = false;
|
||||
bool independent_128b_blocks = false;
|
||||
bool data_write_on_dcc_clear_to_reg = false;
|
||||
enum class IndependentBlockSize : uint8_t { Disabled, Bytes64, Bytes128 };
|
||||
|
||||
uint32_t max_uncompressed_block_size = 2;
|
||||
uint32_t max_compressed_block_size = 2;
|
||||
uint32_t color_transform = 0;
|
||||
bool dcc_clear_key_enable = false;
|
||||
bool overwrite_combiner_disable = false;
|
||||
IndependentBlockSize independent_block_size = IndependentBlockSize::Disabled;
|
||||
bool data_write_on_dcc_clear_to_reg = false;
|
||||
};
|
||||
|
||||
struct ColorCmask {
|
||||
uint64_t addr = 0;
|
||||
};
|
||||
|
||||
struct ColorCmaskSlice {
|
||||
uint32_t slice_minus1 = 0;
|
||||
};
|
||||
|
||||
struct ColorFmask {
|
||||
uint64_t addr = 0;
|
||||
};
|
||||
|
||||
struct ColorFmaskSlice {
|
||||
uint32_t slice_minus1 = 0;
|
||||
};
|
||||
|
||||
struct ColorClearWord0 {
|
||||
uint32_t word0 = 0;
|
||||
};
|
||||
@@ -106,15 +84,8 @@ struct ColorDccAddr {
|
||||
uint64_t addr = 0;
|
||||
};
|
||||
|
||||
struct ColorSize {
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
};
|
||||
|
||||
struct RenderTarget {
|
||||
ColorBase base;
|
||||
ColorPitch pitch;
|
||||
ColorSlice slice;
|
||||
ColorView view;
|
||||
ColorInfo info;
|
||||
ColorAttrib attrib;
|
||||
@@ -122,21 +93,18 @@ struct RenderTarget {
|
||||
ColorAttrib3 attrib3;
|
||||
ColorDccControl dcc;
|
||||
ColorCmask cmask;
|
||||
ColorCmaskSlice cmask_slice;
|
||||
ColorFmask fmask;
|
||||
ColorFmaskSlice fmask_slice;
|
||||
ColorClearWord0 clear_word0;
|
||||
ColorClearWord1 clear_word1;
|
||||
ColorDccAddr dcc_addr;
|
||||
ColorSize size;
|
||||
};
|
||||
|
||||
struct DepthZInfo {
|
||||
uint32_t format = 0;
|
||||
Prospero::DepthFormat format = Prospero::DepthFormat::kInvalid;
|
||||
uint32_t num_samples = 0;
|
||||
Prospero::TextureCompatiblePlaneCompression texture_compatibility =
|
||||
Prospero::TextureCompatiblePlaneCompression::kDisable;
|
||||
Prospero::ZCompareBase z_compare_base = Prospero::ZCompareBase::kZMin;
|
||||
Prospero::ZCompareBase z_compare_base = Prospero::ZCompareBase::kZMax;
|
||||
bool htile_acceleration = false;
|
||||
bool expclear_enabled = false;
|
||||
bool partially_resident = false;
|
||||
@@ -144,16 +112,16 @@ struct DepthZInfo {
|
||||
|
||||
[[nodiscard]] static DepthZInfo Decode(uint32_t value) {
|
||||
DepthZInfo info;
|
||||
info.format = value & 0x3u;
|
||||
info.format = static_cast<Prospero::DepthFormat>(value & 0x3u);
|
||||
info.num_samples = (value >> 2u) & 0x3u;
|
||||
info.texture_compatibility = static_cast<Prospero::TextureCompatiblePlaneCompression>(
|
||||
value & Prospero::GpuEnumValue(Prospero::TextureCompatiblePlaneCompression::kBitMask));
|
||||
value & static_cast<uint32_t>(Prospero::TextureCompatiblePlaneCompression::kBitMask));
|
||||
info.partially_resident = (value & 0x00001000u) != 0;
|
||||
info.max_mip_level = static_cast<uint8_t>((value >> 16u) & 0x0fu);
|
||||
info.expclear_enabled = (value & 0x08000000u) != 0;
|
||||
info.htile_acceleration = (value & 0x20000000u) != 0;
|
||||
info.z_compare_base = static_cast<Prospero::ZCompareBase>(
|
||||
value & Prospero::GpuEnumValue(Prospero::ZCompareBase::kBitMask));
|
||||
value & static_cast<uint32_t>(Prospero::ZCompareBase::kBitMask));
|
||||
return info;
|
||||
}
|
||||
|
||||
@@ -167,18 +135,18 @@ struct DepthZInfo {
|
||||
};
|
||||
|
||||
struct DepthStencilInfo {
|
||||
uint32_t format = 0;
|
||||
Prospero::StencilFormat format = Prospero::StencilFormat::kInvalid;
|
||||
Prospero::TextureCompatibleStencil texture_compatibility =
|
||||
Prospero::TextureCompatibleStencil::kDisable;
|
||||
bool expclear_enabled = false;
|
||||
bool htile_stencil_disabled = false;
|
||||
bool htile_stencil_disabled = true;
|
||||
bool partially_resident = false;
|
||||
|
||||
[[nodiscard]] static DepthStencilInfo Decode(uint32_t value) {
|
||||
DepthStencilInfo info;
|
||||
info.format = value & 0x1u;
|
||||
info.format = static_cast<Prospero::StencilFormat>(value & 0x1u);
|
||||
info.texture_compatibility = static_cast<Prospero::TextureCompatibleStencil>(
|
||||
value & Prospero::GpuEnumValue(Prospero::TextureCompatibleStencil::kBitMask));
|
||||
value & static_cast<uint32_t>(Prospero::TextureCompatibleStencil::kBitMask));
|
||||
info.partially_resident = (value & 0x00001000u) != 0;
|
||||
info.expclear_enabled = (value & 0x08000000u) != 0;
|
||||
info.htile_stencil_disabled = (value & 0x20000000u) != 0;
|
||||
@@ -194,16 +162,6 @@ struct DepthStencilInfo {
|
||||
}
|
||||
};
|
||||
|
||||
struct DepthRenderTargetDepthInfo {
|
||||
uint32_t addr5_swizzle_mask = 0;
|
||||
uint32_t array_mode = 0;
|
||||
uint32_t pipe_config = 0;
|
||||
uint32_t bank_width = 0;
|
||||
uint32_t bank_height = 0;
|
||||
uint32_t macro_tile_aspect = 0;
|
||||
uint32_t num_banks = 0;
|
||||
};
|
||||
|
||||
struct DepthDepthView {
|
||||
uint32_t slice_start = 0;
|
||||
uint32_t slice_max = 0;
|
||||
@@ -218,36 +176,18 @@ struct DepthDepthSizeXY {
|
||||
bool valid = false;
|
||||
};
|
||||
|
||||
struct DepthRenderTargetHTileSurface {
|
||||
uint32_t linear = 0;
|
||||
uint32_t full_cache = 0;
|
||||
uint32_t htile_uses_preload_win = 0;
|
||||
uint32_t preload = 0;
|
||||
uint32_t prefetch_width = 0;
|
||||
uint32_t prefetch_height = 0;
|
||||
uint32_t dst_outside_zero_to_one = 0;
|
||||
};
|
||||
|
||||
struct DepthRenderTarget {
|
||||
DepthZInfo z_info;
|
||||
DepthStencilInfo stencil_info;
|
||||
DepthDepthView depth_view;
|
||||
DepthDepthSizeXY size;
|
||||
DepthRenderTargetDepthInfo depth_info;
|
||||
DepthRenderTargetHTileSurface htile_surface;
|
||||
DepthZInfo z_info;
|
||||
DepthStencilInfo stencil_info;
|
||||
DepthDepthView depth_view;
|
||||
DepthDepthSizeXY size;
|
||||
|
||||
uint64_t z_read_base_addr = 0;
|
||||
uint64_t stencil_read_base_addr = 0;
|
||||
uint64_t z_write_base_addr = 0;
|
||||
uint64_t stencil_write_base_addr = 0;
|
||||
uint32_t pitch_div8_minus1 = 0;
|
||||
uint32_t height_div8_minus1 = 0;
|
||||
bool pitch_height_valid = false;
|
||||
uint32_t slice_div64_minus1 = 0;
|
||||
uint64_t htile_data_base_addr = 0;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
bool width_height_valid = false;
|
||||
uint8_t shading_rate_encoding = 0;
|
||||
};
|
||||
|
||||
struct RenderControl {
|
||||
@@ -256,10 +196,19 @@ struct RenderControl {
|
||||
bool resummarize_enable = false;
|
||||
bool stencil_compress_disable = false;
|
||||
bool depth_compress_disable = false;
|
||||
bool copy_depth_to_color = false;
|
||||
bool copy_stencil_to_color = false;
|
||||
bool copy_centroid = false;
|
||||
uint8_t copy_sample = 0;
|
||||
};
|
||||
|
||||
struct DepthRenderOverride {
|
||||
bool force_z_valid = false;
|
||||
bool force_z_dirty = false;
|
||||
bool force_stencil_valid = false;
|
||||
bool force_stencil_dirty = false;
|
||||
};
|
||||
|
||||
struct GdsOaCounter {
|
||||
uint32_t counter = 0;
|
||||
uint32_t address = 0;
|
||||
@@ -295,23 +244,18 @@ struct ClipControl {
|
||||
bool linear_attribute_clip_enable = false;
|
||||
bool force_viewport_index_from_vs_enable = false;
|
||||
|
||||
[[nodiscard]] bool IsZClipModeRepresentable() const {
|
||||
return min_z_clip_disable == max_z_clip_disable;
|
||||
}
|
||||
[[nodiscard]] bool IsZClipEnabled() const { return !min_z_clip_disable; }
|
||||
[[nodiscard]] bool IsZClipEnabled() const { return !min_z_clip_disable && !max_z_clip_disable; }
|
||||
};
|
||||
|
||||
struct DepthControl {
|
||||
bool stencil_enable = false;
|
||||
bool z_enable = false;
|
||||
bool z_write_enable = false;
|
||||
bool depth_bounds_enable = false;
|
||||
uint8_t zfunc = 0;
|
||||
bool backface_enable = false;
|
||||
uint8_t stencilfunc = 0;
|
||||
uint8_t stencilfunc_bf = 0;
|
||||
bool color_writes_on_depth_fail_enable = false;
|
||||
bool color_writes_on_depth_pass_disable = false;
|
||||
bool stencil_enable = false;
|
||||
bool z_enable = false;
|
||||
bool z_write_enable = false;
|
||||
bool depth_bounds_enable = false;
|
||||
uint8_t zfunc = 0;
|
||||
bool backface_enable = false;
|
||||
uint8_t stencilfunc = 0;
|
||||
uint8_t stencilfunc_bf = 0;
|
||||
};
|
||||
|
||||
struct StencilControl {
|
||||
@@ -425,16 +369,16 @@ struct AaConfig {
|
||||
struct Viewport {
|
||||
float zmin = 0.0f;
|
||||
float zmax = 0.0f;
|
||||
float xscale = 0.0f;
|
||||
float xscale = 1.0f;
|
||||
float xoffset = 0.0f;
|
||||
float yscale = 0.0f;
|
||||
float yscale = 1.0f;
|
||||
float yoffset = 0.0f;
|
||||
float zscale = 0.0f;
|
||||
float zscale = 1.0f;
|
||||
float zoffset = 0.0f;
|
||||
int viewport_scissor_left = 0;
|
||||
int viewport_scissor_top = 0;
|
||||
int viewport_scissor_right = 0;
|
||||
int viewport_scissor_bottom = 0;
|
||||
int viewport_scissor_right = 16384;
|
||||
int viewport_scissor_bottom = 16384;
|
||||
bool viewport_scissor_window_offset_enable = false;
|
||||
};
|
||||
|
||||
@@ -443,26 +387,26 @@ struct ScreenViewport {
|
||||
uint32_t transform_control = 1087;
|
||||
int screen_scissor_left = 0;
|
||||
int screen_scissor_top = 0;
|
||||
int screen_scissor_right = 0;
|
||||
int screen_scissor_bottom = 0;
|
||||
int screen_scissor_right = 16384;
|
||||
int screen_scissor_bottom = 16384;
|
||||
int window_scissor_left = 0;
|
||||
int window_scissor_top = 0;
|
||||
int window_scissor_right = 0;
|
||||
int window_scissor_bottom = 0;
|
||||
int window_scissor_right = 16384;
|
||||
int window_scissor_bottom = 16384;
|
||||
bool window_scissor_window_offset_enable = false;
|
||||
int generic_scissor_left = 0;
|
||||
int generic_scissor_top = 0;
|
||||
int generic_scissor_right = 0;
|
||||
int generic_scissor_bottom = 0;
|
||||
int generic_scissor_right = 16384;
|
||||
int generic_scissor_bottom = 16384;
|
||||
bool generic_scissor_window_offset_enable = false;
|
||||
int window_offset_x = 0;
|
||||
int window_offset_y = 0;
|
||||
uint32_t hw_offset_x = 0;
|
||||
uint32_t hw_offset_y = 0;
|
||||
float guard_band_horz_clip = 0.0f;
|
||||
float guard_band_vert_clip = 0.0f;
|
||||
float guard_band_horz_discard = 0.0f;
|
||||
float guard_band_vert_discard = 0.0f;
|
||||
float guard_band_horz_clip = 1.0f;
|
||||
float guard_band_vert_clip = 1.0f;
|
||||
float guard_band_horz_discard = 1.0f;
|
||||
float guard_band_vert_discard = 1.0f;
|
||||
uint16_t clip_rect_rule = 0xffffu;
|
||||
int clip_rect_left[4] = {};
|
||||
int clip_rect_top[4] = {};
|
||||
@@ -471,27 +415,6 @@ struct ScreenViewport {
|
||||
bool clip_rect_window_offset_enable[4] = {};
|
||||
};
|
||||
|
||||
struct VsShaderResource1 {
|
||||
uint8_t vgprs = 0;
|
||||
uint8_t sgprs = 0;
|
||||
uint8_t priority = 0;
|
||||
uint8_t float_mode = 0;
|
||||
bool dx10_clamp = false;
|
||||
bool ieee_mode = false;
|
||||
uint8_t vgpr_component_count = 0;
|
||||
bool cu_group_enable = false;
|
||||
bool require_forward_progress = false;
|
||||
bool fp16_overflow = false;
|
||||
};
|
||||
|
||||
struct VsShaderResource2 {
|
||||
bool scratch_en = false;
|
||||
uint8_t user_sgpr = 0;
|
||||
bool offchip_lds = false;
|
||||
bool streamout_enabled = false;
|
||||
uint8_t shared_vgprs = 0;
|
||||
};
|
||||
|
||||
struct HsShaderResource1 {
|
||||
uint8_t vgprs = 0;
|
||||
uint8_t priority = 0;
|
||||
@@ -514,7 +437,6 @@ struct HsShaderResource2 {
|
||||
|
||||
struct PsShaderResource1 {
|
||||
uint8_t vgprs = 0;
|
||||
uint8_t sgprs = 0;
|
||||
uint8_t priority = 0;
|
||||
uint8_t float_mode = 0;
|
||||
bool dx10_clamp = false;
|
||||
@@ -538,7 +460,6 @@ struct PsStageRegisters {
|
||||
uint64_t data_addr = 0;
|
||||
PsShaderResource1 rsrc1;
|
||||
PsShaderResource2 rsrc2;
|
||||
uint64_t chksum = 0;
|
||||
};
|
||||
|
||||
struct CsStageRegisters {
|
||||
@@ -548,12 +469,13 @@ struct CsStageRegisters {
|
||||
uint32_t num_thread_y = 0;
|
||||
uint32_t num_thread_z = 0;
|
||||
uint8_t vgprs = 0;
|
||||
uint8_t sgprs = 0;
|
||||
uint8_t priority = 0;
|
||||
uint8_t float_mode = 0;
|
||||
bool dx10_clamp = false;
|
||||
bool debug_mode = false;
|
||||
bool ieee_mode = false;
|
||||
bool require_forward_progress = false;
|
||||
bool fp16_overflow = false;
|
||||
bool bulky = false;
|
||||
bool threadgroup_configuration = false;
|
||||
uint8_t wave_size = 64;
|
||||
bool scratch_en = false;
|
||||
@@ -564,6 +486,7 @@ struct CsStageRegisters {
|
||||
bool tg_size_en = false;
|
||||
uint8_t tidig_comp_cnt = 0;
|
||||
uint16_t lds_size = 0;
|
||||
uint8_t shared_vgprs = 0;
|
||||
};
|
||||
|
||||
struct EsStageRegisters {
|
||||
@@ -571,21 +494,18 @@ struct EsStageRegisters {
|
||||
};
|
||||
|
||||
struct LsStageRegisters {
|
||||
uint64_t data_addr = 0;
|
||||
VsShaderResource1 rsrc1;
|
||||
VsShaderResource2 rsrc2;
|
||||
uint64_t data_addr = 0;
|
||||
};
|
||||
|
||||
struct HsStageRegisters {
|
||||
uint64_t data_addr = 0;
|
||||
uint64_t data_addr = 0;
|
||||
uint64_t user_data_addr = 0;
|
||||
HsShaderResource1 rsrc1;
|
||||
HsShaderResource2 rsrc2;
|
||||
uint64_t chksum = 0;
|
||||
};
|
||||
|
||||
struct GsShaderResource1 {
|
||||
uint8_t vgprs = 0;
|
||||
uint8_t sgprs = 0;
|
||||
uint8_t priority = 0;
|
||||
uint8_t float_mode = 0;
|
||||
bool dx10_clamp = false;
|
||||
@@ -608,10 +528,10 @@ struct GsShaderResource2 {
|
||||
};
|
||||
|
||||
struct GsStageRegisters {
|
||||
uint64_t data_addr = 0;
|
||||
uint64_t data_addr = 0;
|
||||
uint64_t user_data_addr = 0;
|
||||
GsShaderResource1 rsrc1;
|
||||
GsShaderResource2 rsrc2;
|
||||
uint64_t chksum = 0;
|
||||
};
|
||||
|
||||
struct ShaderRegisters {
|
||||
@@ -667,8 +587,7 @@ struct ShaderRegisters {
|
||||
enum class UserSgprType { Unknown, Region, Vsharp };
|
||||
|
||||
struct UserSgprInfo {
|
||||
// Changed from 16 to 64
|
||||
static constexpr int SGPRS_MAX = 64;
|
||||
static constexpr int SGPRS_MAX = 32;
|
||||
|
||||
uint32_t value[SGPRS_MAX] = {0};
|
||||
UserSgprType type[SGPRS_MAX] = {};
|
||||
@@ -715,12 +634,6 @@ public:
|
||||
void Reset() { *this = Context(); }
|
||||
|
||||
void SetColorBase(uint32_t slot, const ColorBase& base) { m_render_targets[slot].base = base; }
|
||||
void SetColorPitch(uint32_t slot, const ColorPitch& pitch) {
|
||||
m_render_targets[slot].pitch = pitch;
|
||||
}
|
||||
void SetColorSlice(uint32_t slot, const ColorSlice& slice) {
|
||||
m_render_targets[slot].slice = slice;
|
||||
}
|
||||
void SetColorView(uint32_t slot, const ColorView& view) { m_render_targets[slot].view = view; }
|
||||
void SetColorInfo(uint32_t slot, const ColorInfo& info) { m_render_targets[slot].info = info; }
|
||||
void SetColorAttrib(uint32_t slot, const ColorAttrib& attrib) {
|
||||
@@ -738,15 +651,9 @@ public:
|
||||
void SetColorCmask(uint32_t slot, const ColorCmask& cmask) {
|
||||
m_render_targets[slot].cmask = cmask;
|
||||
}
|
||||
void SetColorCmaskSlice(uint32_t slot, const ColorCmaskSlice& cmask_slice) {
|
||||
m_render_targets[slot].cmask_slice = cmask_slice;
|
||||
}
|
||||
void SetColorFmask(uint32_t slot, const ColorFmask& fmask) {
|
||||
m_render_targets[slot].fmask = fmask;
|
||||
}
|
||||
void SetColorFmaskSlice(uint32_t slot, const ColorFmaskSlice& fmask_slice) {
|
||||
m_render_targets[slot].fmask_slice = fmask_slice;
|
||||
}
|
||||
void SetColorClearWord0(uint32_t slot, const ColorClearWord0& clear_word0) {
|
||||
m_render_targets[slot].clear_word0 = clear_word0;
|
||||
}
|
||||
@@ -756,7 +663,6 @@ public:
|
||||
void SetColorDccAddr(uint32_t slot, const ColorDccAddr& dcc_addr) {
|
||||
m_render_targets[slot].dcc_addr = dcc_addr;
|
||||
}
|
||||
void SetColorSize(uint32_t slot, const ColorSize& size) { m_render_targets[slot].size = size; }
|
||||
[[nodiscard]] const RenderTarget& GetRenderTarget(uint32_t slot) const {
|
||||
return m_render_targets[slot];
|
||||
}
|
||||
@@ -911,6 +817,12 @@ public:
|
||||
void SetClipControl(const ClipControl& control) { m_clip_control = control; }
|
||||
[[nodiscard]] const RenderControl& GetRenderControl() const { return m_render_control; }
|
||||
void SetRenderControl(const RenderControl& control) { m_render_control = control; }
|
||||
[[nodiscard]] const DepthRenderOverride& GetDepthRenderOverride() const {
|
||||
return m_depth_render_override;
|
||||
}
|
||||
void SetDepthRenderOverride(const DepthRenderOverride& control) {
|
||||
m_depth_render_override = control;
|
||||
}
|
||||
[[nodiscard]] const DepthControl& GetDepthControl() const { return m_depth_control; }
|
||||
void SetDepthControl(const DepthControl& control) { m_depth_control = control; }
|
||||
[[nodiscard]] const ModeControl& GetModeControl() const { return m_mode_control; }
|
||||
@@ -941,8 +853,10 @@ public:
|
||||
[[nodiscard]] uint8_t GetStencilClearValue() const { return m_stencil_clear_value; }
|
||||
void SetStencilClearValue(uint8_t clear_value) { m_stencil_clear_value = clear_value; }
|
||||
|
||||
[[nodiscard]] float GetLineWidth() const { return m_line_width; }
|
||||
void SetLineWidth(float width) { m_line_width = width; }
|
||||
[[nodiscard]] float GetLineWidth() const { return m_line_width; }
|
||||
void SetLineWidth(float width) { m_line_width = width; }
|
||||
[[nodiscard]] uint32_t GetPrimitiveResetIndex() const { return m_primitive_reset_index; }
|
||||
void SetPrimitiveResetIndex(uint32_t index) { m_primitive_reset_index = index; }
|
||||
|
||||
[[nodiscard]] const ShaderRegisters& GetShaderRegisters() const { return m_sh_regs; }
|
||||
|
||||
@@ -987,7 +901,8 @@ public:
|
||||
void SetScShaderControl(uint32_t value) { m_sh_regs.m_paScShaderControl = value; }
|
||||
|
||||
private:
|
||||
float m_line_width = 1.0f;
|
||||
float m_line_width = 1.0f;
|
||||
uint32_t m_primitive_reset_index = 0xffffffffu;
|
||||
|
||||
BlendControl m_blend_control[8];
|
||||
BlendColor m_blend_color;
|
||||
@@ -1003,15 +918,16 @@ private:
|
||||
|
||||
uint32_t m_shader_stages = 0;
|
||||
|
||||
DepthRenderTarget m_depth_render_target;
|
||||
RenderControl m_render_control;
|
||||
DepthControl m_depth_control;
|
||||
StencilControl m_stencil_control;
|
||||
StencilMask m_stencil_mask;
|
||||
float m_depth_clear_value = 0.0f;
|
||||
float m_depth_bounds_min = 0.0f;
|
||||
float m_depth_bounds_max = 1.0f;
|
||||
uint8_t m_stencil_clear_value = 0;
|
||||
DepthRenderTarget m_depth_render_target;
|
||||
RenderControl m_render_control;
|
||||
DepthRenderOverride m_depth_render_override;
|
||||
DepthControl m_depth_control;
|
||||
StencilControl m_stencil_control;
|
||||
StencilMask m_stencil_mask;
|
||||
float m_depth_clear_value = 0.0f;
|
||||
float m_depth_bounds_min = 0.0f;
|
||||
float m_depth_bounds_max = 1.0f;
|
||||
uint8_t m_stencil_clear_value = 0;
|
||||
|
||||
ModeControl m_mode_control;
|
||||
PolyOffset m_poly_offset;
|
||||
@@ -1020,6 +936,12 @@ private:
|
||||
ShaderRegisters m_sh_regs;
|
||||
};
|
||||
|
||||
struct FsrView {
|
||||
uint32_t control_points[2][4] {};
|
||||
uint32_t alphas[2][2] {};
|
||||
uint32_t window[2] {};
|
||||
};
|
||||
|
||||
class UserConfig {
|
||||
public:
|
||||
UserConfig() = default;
|
||||
@@ -1029,12 +951,14 @@ public:
|
||||
|
||||
void Reset() { *this = UserConfig(); }
|
||||
|
||||
void SetPrimitiveType(uint32_t prim_type) { m_prim_type = prim_type; }
|
||||
[[nodiscard]] uint32_t GetPrimType() const { return m_prim_type; }
|
||||
void SetPrimitiveType(Prospero::PrimitiveType prim_type) { m_prim_type = prim_type; }
|
||||
[[nodiscard]] Prospero::PrimitiveType GetPrimType() const { return m_prim_type; }
|
||||
void SetIndexOffset(uint32_t index_offset) { m_index_offset = index_offset; }
|
||||
[[nodiscard]] uint32_t GetIndexOffset() const { return m_index_offset; }
|
||||
void SetObjectId(uint32_t object_id) { m_object_id = object_id; }
|
||||
[[nodiscard]] uint32_t GetObjectId() const { return m_object_id; }
|
||||
void SetPrimitiveResetControl(uint32_t control) { m_primitive_reset_control = control; }
|
||||
[[nodiscard]] uint32_t GetPrimitiveResetControl() const { return m_primitive_reset_control; }
|
||||
|
||||
[[nodiscard]] const GeControl& GetGeControl() const { return m_ge_cntl; }
|
||||
void SetGeControl(const GeControl& control) { m_ge_cntl = control; }
|
||||
@@ -1047,11 +971,21 @@ public:
|
||||
void SetGdsOaCntl(uint32_t value) { m_gds_oa.cntl = value; }
|
||||
void SetGdsOaCounter(uint32_t value) { m_gds_oa.counters[m_gds_oa.GetIndex()].counter = value; }
|
||||
void SetGdsOaAddress(uint32_t value) { m_gds_oa.counters[m_gds_oa.GetIndex()].address = value; }
|
||||
void SetFsrControlPoint(uint32_t axis, uint32_t index, uint32_t value) {
|
||||
m_fsr_view.control_points[axis][index] = value;
|
||||
}
|
||||
void SetFsrAlpha(uint32_t axis, uint32_t index, uint32_t value) {
|
||||
m_fsr_view.alphas[axis][index] = value;
|
||||
}
|
||||
void SetFsrWindow(uint32_t index, uint32_t value) { m_fsr_view.window[index] = value; }
|
||||
[[nodiscard]] const FsrView& GetFsrView() const { return m_fsr_view; }
|
||||
|
||||
private:
|
||||
uint32_t m_prim_type = 0;
|
||||
uint32_t m_index_offset = 0;
|
||||
uint32_t m_object_id = 0;
|
||||
FsrView m_fsr_view;
|
||||
Prospero::PrimitiveType m_prim_type = Prospero::PrimitiveType::kNone;
|
||||
uint32_t m_index_offset = 0;
|
||||
uint32_t m_object_id = 0;
|
||||
uint32_t m_primitive_reset_control = 0;
|
||||
|
||||
GeControl m_ge_cntl;
|
||||
GeUserVgprEn m_ge_user_vgpr_en;
|
||||
@@ -1069,23 +1003,25 @@ public:
|
||||
|
||||
void SetEsShaderBase(uint64_t addr) { m_vs.es_regs.data_addr = addr; }
|
||||
void SetLsShaderBase(uint64_t addr) { m_vs.ls_regs.data_addr = addr; }
|
||||
void SetLsShaderResource1(const VsShaderResource1& rsrc1) { m_vs.ls_regs.rsrc1 = rsrc1; }
|
||||
void SetLsShaderResource2(const VsShaderResource2& rsrc2) { m_vs.ls_regs.rsrc2 = rsrc2; }
|
||||
void SetHsShaderBase(uint64_t addr) { m_vs.hs_regs.data_addr = addr; }
|
||||
void SetHsUserDataAddress(uint32_t word, uint32_t value) {
|
||||
SetUserDataAddressWord(m_vs.hs_regs.user_data_addr, word, value);
|
||||
}
|
||||
void SetHsShaderResource1(const HsShaderResource1& rsrc1) { m_vs.hs_regs.rsrc1 = rsrc1; }
|
||||
void SetHsShaderResource2(const HsShaderResource2& rsrc2) { m_vs.hs_regs.rsrc2 = rsrc2; }
|
||||
void SetHsShaderChksum(uint32_t value) { m_vs.hs_regs.chksum = value; }
|
||||
void SetGsShaderBase(uint64_t addr) { m_vs.gs_regs.data_addr = addr; }
|
||||
void SetGsUserDataAddress(uint32_t word, uint32_t value) {
|
||||
SetUserDataAddressWord(m_vs.gs_regs.user_data_addr, word, value);
|
||||
}
|
||||
void SetGsShaderResource1(const GsShaderResource1& rsrc1) { m_vs.gs_regs.rsrc1 = rsrc1; }
|
||||
void SetGsShaderResource2(const GsShaderResource2& rsrc2) { m_vs.gs_regs.rsrc2 = rsrc2; }
|
||||
void SetGsShaderChksum(uint32_t value) { m_vs.gs_regs.chksum = value; }
|
||||
|
||||
void SetPsShaderBase(uint64_t addr) { m_ps.ps_regs.data_addr = addr; }
|
||||
void SetPsShaderResource1(const PsShaderResource1& rsrc1) { m_ps.ps_regs.rsrc1 = rsrc1; }
|
||||
void SetPsShaderResource2(const PsShaderResource2& rsrc2) { m_ps.ps_regs.rsrc2 = rsrc2; }
|
||||
void SetPsShaderChksum(uint32_t value) { m_ps.ps_regs.chksum = value; }
|
||||
|
||||
void SetCsShader(const CsStageRegisters& cs_regs) { m_cs.cs_regs = cs_regs; }
|
||||
void SetCsWaveSize(uint8_t wave_size) { m_cs.cs_regs.wave_size = wave_size; }
|
||||
|
||||
void SetPsUserSgpr(uint32_t id, uint32_t value, UserSgprType type) {
|
||||
m_ps.ps_user_sgpr.value[id] = value;
|
||||
@@ -1117,6 +1053,12 @@ public:
|
||||
[[nodiscard]] const ComputeShaderInfo& GetCs() const { return m_cs; }
|
||||
|
||||
private:
|
||||
static void SetUserDataAddressWord(uint64_t& address, uint32_t word, uint32_t value) {
|
||||
const auto shift = word * 32u;
|
||||
address = (address & ~(uint64_t {0xffffffffu} << shift)) |
|
||||
(static_cast<uint64_t>(value) << shift);
|
||||
}
|
||||
|
||||
VertexShaderInfo m_vs;
|
||||
PixelShaderInfo m_ps;
|
||||
ComputeShaderInfo m_cs;
|
||||
|
||||
@@ -21,12 +21,10 @@ constexpr auto MakeRegisterNames() {
|
||||
names[R_DRAW_RESET] = "R_DRAW_RESET";
|
||||
names[R_WAIT_FLIP_DONE] = "R_WAIT_FLIP_DONE";
|
||||
names[R_DISPATCH_RESET] = "R_DISPATCH_RESET";
|
||||
names[R_WAIT_MEM_32] = "R_WAIT_MEM_32";
|
||||
names[R_PUSH_MARKER] = "R_PUSH_MARKER";
|
||||
names[R_POP_MARKER] = "R_POP_MARKER";
|
||||
names[R_ACQUIRE_MEM] = "R_ACQUIRE_MEM";
|
||||
names[R_WRITE_DATA] = "R_WRITE_DATA";
|
||||
names[R_WAIT_MEM_64] = "R_WAIT_MEM_64";
|
||||
names[R_FLIP] = "R_FLIP";
|
||||
names[R_RELEASE_MEM] = "R_RELEASE_MEM";
|
||||
names[R_DMA_DATA] = "R_DMA_DATA";
|
||||
@@ -62,6 +60,7 @@ constexpr auto MakeOpcodeNames() {
|
||||
names[IT_WRITE_DATA] = "IT_WRITE_DATA";
|
||||
names[IT_MEM_SEMAPHORE] = "IT_MEM_SEMAPHORE";
|
||||
names[IT_DRAW_INDEX_INDIRECT_MULTI] = "IT_DRAW_INDEX_INDIRECT_MULTI";
|
||||
names[IT_WAIT_REG_MEM] = "IT_WAIT_REG_MEM";
|
||||
names[IT_INDIRECT_BUFFER] = "IT_INDIRECT_BUFFER";
|
||||
names[IT_COPY_DATA] = "IT_COPY_DATA";
|
||||
names[IT_CP_DMA] = "IT_CP_DMA";
|
||||
@@ -91,6 +90,7 @@ constexpr auto MakeOpcodeNames() {
|
||||
names[IT_DISPATCH_DRAW_PREAMBLE] = "IT_DISPATCH_DRAW_PREAMBLE";
|
||||
names[IT_DISPATCH_DRAW] = "IT_DISPATCH_DRAW";
|
||||
names[IT_GET_LOD_STATS] = "IT_GET_LOD_STATS";
|
||||
names[IT_WAIT_REG_MEM_64] = "IT_WAIT_REG_MEM_64";
|
||||
names[IT_SET_CONTEXT_REG_INDIRECT] = "IT_SET_CONTEXT_REG_INDIRECT";
|
||||
return names;
|
||||
}
|
||||
@@ -100,7 +100,7 @@ constexpr auto g_opcode_names = MakeOpcodeNames();
|
||||
|
||||
} // namespace
|
||||
|
||||
void DumpPm4PacketStream(Common::File* file, uint32_t* cmd_buffer, uint32_t start_dw,
|
||||
void DumpPm4PacketStream(Common::File* file, const uint32_t* cmd_buffer, uint32_t start_dw,
|
||||
uint32_t num_dw) {
|
||||
// db_dump();
|
||||
|
||||
@@ -143,9 +143,9 @@ void DumpPm4PacketStream(Common::File* file, uint32_t* cmd_buffer, uint32_t star
|
||||
if ((op == IT_SET_CONTEXT_REG_INDIRECT || op == IT_SET_SH_REG_INDIRECT ||
|
||||
op == IT_SET_UCONFIG_REG_INDIRECT) &&
|
||||
len == 4) {
|
||||
auto* indirect_buffer =
|
||||
reinterpret_cast<uint32_t*>((static_cast<uint64_t>(cmd[0]) & 0xfffffffcu) |
|
||||
(static_cast<uint64_t>(cmd[1]) << 32u));
|
||||
auto* indirect_buffer = reinterpret_cast<const uint32_t*>(
|
||||
(static_cast<uint64_t>(cmd[0]) & 0xfffffffcu) |
|
||||
(static_cast<uint64_t>(cmd[1]) << 32u));
|
||||
const uint32_t indirect_num_regs = cmd[3] & 0x3fffu;
|
||||
|
||||
for (uint32_t i = 0; indirect_buffer != nullptr && i < indirect_num_regs;
|
||||
|
||||
+57
-135
@@ -42,6 +42,7 @@ constexpr uint32_t IT_WRITE_DATA = 0x37;
|
||||
constexpr uint32_t IT_MEM_SEMAPHORE = 0x39;
|
||||
constexpr uint32_t IT_DRAW_INDEX_INDIRECT_MULTI = 0x38;
|
||||
constexpr uint32_t IT_DISPATCH_DRAW_PREAMBLE = 0x3A;
|
||||
constexpr uint32_t IT_WAIT_REG_MEM = 0x3C;
|
||||
constexpr uint32_t IT_INDIRECT_BUFFER = 0x3F;
|
||||
constexpr uint32_t IT_COPY_DATA = 0x40;
|
||||
constexpr uint32_t IT_CP_DMA = 0x41;
|
||||
@@ -70,6 +71,7 @@ constexpr uint32_t IT_WAIT_ON_CE_COUNTER = 0x86;
|
||||
constexpr uint32_t IT_WAIT_ON_DE_COUNTER_DIFF = 0x88;
|
||||
constexpr uint32_t IT_DISPATCH_DRAW = 0x8D;
|
||||
constexpr uint32_t IT_GET_LOD_STATS = 0x8E;
|
||||
constexpr uint32_t IT_WAIT_REG_MEM_64 = 0x93;
|
||||
constexpr uint32_t IT_SET_CONTEXT_REG_INDIRECT = 0x9F;
|
||||
|
||||
/* Custom codes. Implemented via IT_NOP */
|
||||
@@ -78,12 +80,10 @@ constexpr uint32_t R_ZERO = 0x00;
|
||||
constexpr uint32_t R_DRAW_RESET = 0x05;
|
||||
constexpr uint32_t R_WAIT_FLIP_DONE = 0x06;
|
||||
constexpr uint32_t R_DISPATCH_RESET = 0x09;
|
||||
constexpr uint32_t R_WAIT_MEM_32 = 0x0A;
|
||||
constexpr uint32_t R_PUSH_MARKER = 0x0B;
|
||||
constexpr uint32_t R_POP_MARKER = 0x0C;
|
||||
constexpr uint32_t R_ACQUIRE_MEM = 0x14;
|
||||
constexpr uint32_t R_WRITE_DATA = 0x15;
|
||||
constexpr uint32_t R_WAIT_MEM_64 = 0x16;
|
||||
constexpr uint32_t R_FLIP = 0x17;
|
||||
constexpr uint32_t R_RELEASE_MEM = 0x18;
|
||||
constexpr uint32_t R_DMA_DATA = 0x19;
|
||||
@@ -98,6 +98,10 @@ constexpr uint32_t DB_RENDER_CONTROL_DEPTH_CLEAR_ENABLE_SHIFT = 0;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_DEPTH_CLEAR_ENABLE_MASK = 0x1;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_STENCIL_CLEAR_ENABLE_SHIFT = 1;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_STENCIL_CLEAR_ENABLE_MASK = 0x1;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_COPY_DEPTH_TO_COLOR_SHIFT = 2;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_COPY_DEPTH_TO_COLOR_MASK = 0x1;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_COPY_STENCIL_TO_COLOR_SHIFT = 3;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_COPY_STENCIL_TO_COLOR_MASK = 0x1;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_RESUMMARIZE_ENABLE_SHIFT = 4;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_RESUMMARIZE_ENABLE_MASK = 0x1;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_STENCIL_COMPRESS_DISABLE_SHIFT = 5;
|
||||
@@ -163,22 +167,6 @@ constexpr uint32_t PA_SC_SCREEN_SCISSOR_BR_BR_Y_MASK = 0xFFFF;
|
||||
|
||||
constexpr uint32_t DB_DFSM_CONTROL = 0xE;
|
||||
|
||||
constexpr uint32_t DB_DEPTH_INFO = 0xF;
|
||||
constexpr uint32_t DB_DEPTH_INFO_ADDR5_SWIZZLE_MASK_SHIFT = 0;
|
||||
constexpr uint32_t DB_DEPTH_INFO_ADDR5_SWIZZLE_MASK_MASK = 0xF;
|
||||
constexpr uint32_t DB_DEPTH_INFO_ARRAY_MODE_SHIFT = 4;
|
||||
constexpr uint32_t DB_DEPTH_INFO_ARRAY_MODE_MASK = 0xF;
|
||||
constexpr uint32_t DB_DEPTH_INFO_PIPE_CONFIG_SHIFT = 8;
|
||||
constexpr uint32_t DB_DEPTH_INFO_PIPE_CONFIG_MASK = 0x1F;
|
||||
constexpr uint32_t DB_DEPTH_INFO_BANK_WIDTH_SHIFT = 13;
|
||||
constexpr uint32_t DB_DEPTH_INFO_BANK_WIDTH_MASK = 0x3;
|
||||
constexpr uint32_t DB_DEPTH_INFO_BANK_HEIGHT_SHIFT = 15;
|
||||
constexpr uint32_t DB_DEPTH_INFO_BANK_HEIGHT_MASK = 0x3;
|
||||
constexpr uint32_t DB_DEPTH_INFO_MACRO_TILE_ASPECT_SHIFT = 17;
|
||||
constexpr uint32_t DB_DEPTH_INFO_MACRO_TILE_ASPECT_MASK = 0x3;
|
||||
constexpr uint32_t DB_DEPTH_INFO_NUM_BANKS_SHIFT = 19;
|
||||
constexpr uint32_t DB_DEPTH_INFO_NUM_BANKS_MASK = 0x3;
|
||||
|
||||
constexpr uint32_t DB_Z_INFO = 0x10;
|
||||
constexpr uint32_t DB_Z_INFO_FORMAT_SHIFT = 0;
|
||||
constexpr uint32_t DB_Z_INFO_FORMAT_MASK = 0x3;
|
||||
@@ -222,16 +210,6 @@ constexpr uint32_t DB_STENCIL_READ_BASE = 0x13;
|
||||
constexpr uint32_t DB_Z_WRITE_BASE = 0x14;
|
||||
constexpr uint32_t DB_STENCIL_WRITE_BASE = 0x15;
|
||||
|
||||
constexpr uint32_t DB_DEPTH_SIZE = 0x16;
|
||||
constexpr uint32_t DB_DEPTH_SIZE_PITCH_TILE_MAX_SHIFT = 0;
|
||||
constexpr uint32_t DB_DEPTH_SIZE_PITCH_TILE_MAX_MASK = 0x7FF;
|
||||
constexpr uint32_t DB_DEPTH_SIZE_HEIGHT_TILE_MAX_SHIFT = 11;
|
||||
constexpr uint32_t DB_DEPTH_SIZE_HEIGHT_TILE_MAX_MASK = 0x7FF;
|
||||
|
||||
constexpr uint32_t DB_DEPTH_SLICE = 0x17;
|
||||
constexpr uint32_t DB_DEPTH_SLICE_SLICE_TILE_MAX_SHIFT = 0;
|
||||
constexpr uint32_t DB_DEPTH_SLICE_SLICE_TILE_MAX_MASK = 0x3FFFFF;
|
||||
|
||||
constexpr uint32_t DB_Z_READ_BASE_HI = 0x1A;
|
||||
constexpr uint32_t DB_STENCIL_READ_BASE_HI = 0x1B;
|
||||
constexpr uint32_t DB_Z_WRITE_BASE_HI = 0x1C;
|
||||
@@ -317,8 +295,8 @@ constexpr uint32_t FSR_RECURSIONS1 = 0xF2;
|
||||
constexpr uint32_t VGT_MULTI_PRIM_IB_RESET_INDX = 0x103;
|
||||
constexpr uint32_t CB_RMI_GL2_CACHE_CONTROL = 0x104;
|
||||
constexpr uint32_t CB_BLEND_RED = 0x105;
|
||||
constexpr uint32_t CB_BLEND_GREEN = 0x106;
|
||||
constexpr uint32_t CB_BLEND_BLUE = 0x107;
|
||||
constexpr uint32_t CB_BLEND_BLUE = 0x106;
|
||||
constexpr uint32_t CB_BLEND_GREEN = 0x107;
|
||||
constexpr uint32_t CB_BLEND_ALPHA = 0x108;
|
||||
constexpr uint32_t CB_DCC_CONTROL = 0x109;
|
||||
|
||||
@@ -387,9 +365,6 @@ constexpr uint32_t SPI_SHADER_POS_FORMAT = 0x1C3;
|
||||
constexpr uint32_t SPI_SHADER_Z_FORMAT = 0x1C4;
|
||||
constexpr uint32_t SPI_SHADER_COL_FORMAT = 0x1C5;
|
||||
|
||||
// Indirect Cx descriptor selector for the 32-entry PS input-control register bank.
|
||||
constexpr uint32_t CX_PS_SHADER_USAGE_BASE = 0x10000000u;
|
||||
|
||||
constexpr uint32_t CB_BLEND0_CONTROL = 0x1E0;
|
||||
constexpr uint32_t CB_BLEND0_CONTROL_COLOR_SRCBLEND_SHIFT = 0;
|
||||
constexpr uint32_t CB_BLEND0_CONTROL_COLOR_SRCBLEND_MASK = 0x1F;
|
||||
@@ -427,11 +402,6 @@ constexpr uint32_t DB_DEPTH_CONTROL_STENCILFUNC_SHIFT = 8
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_STENCILFUNC_MASK = 0x7;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_STENCILFUNC_BF_SHIFT = 20;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_STENCILFUNC_BF_MASK = 0x7;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_ENABLE_COLOR_WRITES_ON_DEPTH_FAIL_SHIFT = 30;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_ENABLE_COLOR_WRITES_ON_DEPTH_FAIL_MASK = 0x1;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_DISABLE_COLOR_WRITES_ON_DEPTH_PASS_SHIFT = 31;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_DISABLE_COLOR_WRITES_ON_DEPTH_PASS_MASK = 0x1;
|
||||
|
||||
constexpr uint32_t DB_EQAA = 0x201;
|
||||
constexpr uint32_t DB_EQAA_MAX_ANCHOR_SAMPLES_SHIFT = 0;
|
||||
constexpr uint32_t DB_EQAA_MAX_ANCHOR_SAMPLES_MASK = 0x7;
|
||||
@@ -563,21 +533,9 @@ constexpr uint32_t VGT_DRAW_PAYLOAD_CNTL = 0x2A6;
|
||||
constexpr uint32_t VGT_ESGS_RING_ITEMSIZE = 0x2AB;
|
||||
constexpr uint32_t VGT_REUSE_OFF = 0x2AD;
|
||||
|
||||
constexpr uint32_t DB_HTILE_SURFACE = 0x2AF;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_LINEAR_SHIFT = 0;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_LINEAR_MASK = 0x1;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_FULL_CACHE_SHIFT = 1;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_FULL_CACHE_MASK = 0x1;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_HTILE_USES_PRELOAD_WIN_SHIFT = 2;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_HTILE_USES_PRELOAD_WIN_MASK = 0x1;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_PRELOAD_SHIFT = 3;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_PRELOAD_MASK = 0x1;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_PREFETCH_WIDTH_SHIFT = 4;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_PREFETCH_WIDTH_MASK = 0x3F;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_PREFETCH_HEIGHT_SHIFT = 10;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_PREFETCH_HEIGHT_MASK = 0x3F;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_DST_OUTSIDE_ZERO_TO_ONE_SHIFT = 16;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_DST_OUTSIDE_ZERO_TO_ONE_MASK = 0x1;
|
||||
constexpr uint32_t DB_SHADING_RATE_ENCODING = 0x2AF;
|
||||
constexpr uint32_t DB_SHADING_RATE_ENCODING_SHADING_RATE_ENCODING_SHIFT = 19;
|
||||
constexpr uint32_t DB_SHADING_RATE_ENCODING_SHADING_RATE_ENCODING_MASK = 0x3;
|
||||
|
||||
constexpr uint32_t DB_SRESULTS_COMPARE_STATE0 = 0x2B0;
|
||||
constexpr uint32_t DB_SRESULTS_COMPARE_STATE1 = 0x2B1;
|
||||
@@ -624,7 +582,6 @@ constexpr uint32_t PA_SC_SHADER_CONTROL = 0x310;
|
||||
constexpr uint32_t PA_SC_BINNER_CNTL_0 = 0x311;
|
||||
constexpr uint32_t PA_SC_BINNER_CNTL_1 = 0x312;
|
||||
constexpr uint32_t PA_SC_CONSERVATIVE_RASTERIZATION_CNTL = 0x313;
|
||||
constexpr uint32_t PA_SC_NGG_MODE_CNTL = 0x314;
|
||||
constexpr uint32_t CB_COLOR0_BASE = 0x318;
|
||||
|
||||
constexpr uint32_t CB_COLOR0_VIEW = 0x31B;
|
||||
@@ -652,24 +609,13 @@ constexpr uint32_t CB_COLOR0_INFO_BLEND_BYPASS_SHIFT = 16;
|
||||
constexpr uint32_t CB_COLOR0_INFO_BLEND_BYPASS_MASK = 0x1;
|
||||
constexpr uint32_t CB_COLOR0_INFO_ROUND_MODE_SHIFT = 18;
|
||||
constexpr uint32_t CB_COLOR0_INFO_ROUND_MODE_MASK = 0x1;
|
||||
constexpr uint32_t CB_COLOR0_INFO_CMASK_IS_LINEAR_SHIFT = 19;
|
||||
constexpr uint32_t CB_COLOR0_INFO_CMASK_IS_LINEAR_MASK = 0x1;
|
||||
constexpr uint32_t CB_COLOR0_INFO_FMASK_COMPRESSION_DISABLE_SHIFT = 26;
|
||||
constexpr uint32_t CB_COLOR0_INFO_FMASK_COMPRESSION_DISABLE_MASK = 0x1;
|
||||
constexpr uint32_t CB_COLOR0_INFO_FMASK_COMPRESS_1FRAG_ONLY_SHIFT = 27;
|
||||
constexpr uint32_t CB_COLOR0_INFO_FMASK_COMPRESS_1FRAG_ONLY_MASK = 0x1;
|
||||
constexpr uint32_t CB_COLOR0_INFO_DCC_ENABLE_SHIFT = 28;
|
||||
constexpr uint32_t CB_COLOR0_INFO_DCC_ENABLE_MASK = 0x1;
|
||||
constexpr uint32_t CB_COLOR0_INFO_CMASK_ADDR_TYPE_SHIFT = 29;
|
||||
constexpr uint32_t CB_COLOR0_INFO_CMASK_ADDR_TYPE_MASK = 0x3;
|
||||
constexpr uint32_t CB_COLOR0_INFO_ALT_TILE_MODE_SHIFT = 31;
|
||||
constexpr uint32_t CB_COLOR0_INFO_ALT_TILE_MODE_MASK = 0x1;
|
||||
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB = 0x31D;
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB_TILE_MODE_INDEX_SHIFT = 0;
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB_TILE_MODE_INDEX_MASK = 0x1F;
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB_FMASK_TILE_MODE_INDEX_SHIFT = 5;
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB_FMASK_TILE_MODE_INDEX_MASK = 0x1F;
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB_NUM_SAMPLES_SHIFT = 12;
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB_NUM_SAMPLES_MASK = 0x7;
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB_NUM_FRAGMENTS_SHIFT = 15;
|
||||
@@ -684,8 +630,6 @@ constexpr uint32_t CB_COLOR0_DCC_CONTROL_KEY_CLEAR_ENABLE_SHIFT
|
||||
constexpr uint32_t CB_COLOR0_DCC_CONTROL_KEY_CLEAR_ENABLE_MASK = 0x1;
|
||||
constexpr uint32_t CB_COLOR0_DCC_CONTROL_MAX_UNCOMPRESSED_BLOCK_SIZE_SHIFT = 2;
|
||||
constexpr uint32_t CB_COLOR0_DCC_CONTROL_MAX_UNCOMPRESSED_BLOCK_SIZE_MASK = 0x3;
|
||||
constexpr uint32_t CB_COLOR0_DCC_CONTROL_MIN_COMPRESSED_BLOCK_SIZE_SHIFT = 4;
|
||||
constexpr uint32_t CB_COLOR0_DCC_CONTROL_MIN_COMPRESSED_BLOCK_SIZE_MASK = 0x1;
|
||||
constexpr uint32_t CB_COLOR0_DCC_CONTROL_MAX_COMPRESSED_BLOCK_SIZE_SHIFT = 5;
|
||||
constexpr uint32_t CB_COLOR0_DCC_CONTROL_MAX_COMPRESSED_BLOCK_SIZE_MASK = 0x3;
|
||||
constexpr uint32_t CB_COLOR0_DCC_CONTROL_COLOR_TRANSFORM_SHIFT = 7;
|
||||
@@ -742,31 +686,25 @@ constexpr uint32_t CB_COLOR0_ATTRIB3_CMASK_PIPE_ALIGNED_SHIFT = 26;
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB3_CMASK_PIPE_ALIGNED_MASK = 0x1;
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB3_DCC_PIPE_ALIGNED_SHIFT = 30;
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB3_DCC_PIPE_ALIGNED_MASK = 0x1;
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB3_WRITE_VRS_RATE_HINT_TO_CMASK_SHIFT = 31;
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB3_WRITE_VRS_RATE_HINT_TO_CMASK_MASK = 0x1;
|
||||
|
||||
constexpr uint32_t CB_COLOR7_ATTRIB3 = 0x3BF;
|
||||
|
||||
/* Fake codes. Don't exist on real HW */
|
||||
|
||||
constexpr uint32_t CX_NOP = 0x800003FF;
|
||||
constexpr uint32_t CX_NOP = 0xDB;
|
||||
|
||||
constexpr uint32_t CX_NUM = 0x3FF + 1;
|
||||
|
||||
/* Shader registers */
|
||||
|
||||
constexpr uint32_t SPI_SHADER_TBA_LO_PS = 0x0;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC4_PS = 0x1;
|
||||
constexpr uint32_t SPI_SHADER_TMA_LO_PS = 0x2;
|
||||
constexpr uint32_t SPI_SHADER_TMA_HI_PS = 0x3;
|
||||
constexpr uint32_t SPI_SHADER_PGM_CHKSUM_PS = 0x6;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC3_PS = 0x7;
|
||||
constexpr uint32_t SPI_SHADER_PGM_LO_PS = 0x8;
|
||||
constexpr uint32_t SPI_SHADER_PGM_HI_PS = 0x9;
|
||||
constexpr uint32_t SPI_SHADER_PACE_ID_PS = 0x6;
|
||||
constexpr uint32_t SPI_GRAPHICS_SHADER_CONTROL_PS = 0x7;
|
||||
constexpr uint32_t SPI_SHADER_PGM_LO_PS = 0x8;
|
||||
constexpr uint32_t SPI_SHADER_PGM_HI_PS = 0x9;
|
||||
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_PS = 0xA;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_PS_VGPRS_SHIFT = 0;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_PS_VGPRS_MASK = 0x3F;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_PS_SGPRS_SHIFT = 6;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_PS_SGPRS_MASK = 0xF;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_PS_PRIORITY_SHIFT = 10;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_PS_PRIORITY_MASK = 0x3;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_PS_FLOAT_MODE_SHIFT = 12;
|
||||
@@ -803,14 +741,11 @@ constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_SHARED_VGPR_CNT_MASK = 0xF;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_PS_0 = 0xC;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_PS_15 = 0x1B;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_PS_31 = 0x2B;
|
||||
constexpr uint32_t SPI_SHADER_REQ_CTRL_PS = 0x30;
|
||||
constexpr uint32_t SPI_SHADER_USER_ACCUM_PS_0 = 0x32;
|
||||
constexpr uint32_t SPI_SHADER_USER_ACCUM_PS_3 = 0x35;
|
||||
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_VS_VGPRS_SHIFT = 0;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_VS_VGPRS_MASK = 0x3F;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_VS_SGPRS_SHIFT = 6;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_VS_SGPRS_MASK = 0xF;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_VS_PRIORITY_SHIFT = 10;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_VS_PRIORITY_MASK = 0x3;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_VS_FLOAT_MODE_SHIFT = 12;
|
||||
@@ -841,19 +776,20 @@ constexpr uint32_t SPI_SHADER_PGM_RSRC2_VS_USER_SGPR_MSB_MASK = 0x1;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_VS_SHARED_VGPR_CNT_SHIFT = 28;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_VS_SHARED_VGPR_CNT_MASK = 0xF;
|
||||
|
||||
constexpr uint32_t SPI_SHADER_PGM_CHKSUM_GS = 0x80;
|
||||
constexpr uint32_t SPI_SHADER_PACE_ID_GS = 0x80;
|
||||
// AGC compiler records use this numeric tag as opaque metadata. It is not a shader checksum
|
||||
// register and intentionally has no checksum-state consumer in HardwareContext.
|
||||
constexpr uint32_t SPI_SHADER_PGM_CHKSUM_GS = SPI_SHADER_PACE_ID_GS;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC4_GS = 0x81;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_ADDR_LO_GS = 0x82;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_ADDR_HI_GS = 0x83;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC3_GS = 0x87;
|
||||
constexpr uint32_t SPI_GRAPHICS_SHADER_CONTROL_GS = 0x87;
|
||||
constexpr uint32_t SPI_SHADER_PGM_LO_GS = 0x88;
|
||||
constexpr uint32_t SPI_SHADER_PGM_HI_GS = 0x89;
|
||||
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_GS = 0x8A;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_GS_VGPRS_SHIFT = 0;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_GS_VGPRS_MASK = 0x3F;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_GS_SGPRS_SHIFT = 6;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_GS_SGPRS_MASK = 0xF;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_GS_PRIORITY_SHIFT = 10;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_GS_PRIORITY_MASK = 0x3;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_GS_FLOAT_MODE_SHIFT = 12;
|
||||
@@ -894,22 +830,16 @@ constexpr uint32_t SPI_SHADER_PGM_RSRC2_GS_SHARED_VGPR_CNT_MASK = 0xF;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_GS_0 = 0x8C;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_GS_15 = 0x9B;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_GS_31 = 0xAB;
|
||||
constexpr uint32_t SPI_SHADER_REQ_CTRL_ESGS = 0xB0;
|
||||
constexpr uint32_t SPI_SHADER_USER_ACCUM_ESGS_0 = 0xB2;
|
||||
constexpr uint32_t SPI_SHADER_USER_ACCUM_ESGS_3 = 0xB5;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_ES_GS = 0xBC;
|
||||
constexpr uint32_t SPI_SHADER_PGM_LO_ES = 0xC8;
|
||||
constexpr uint32_t SPI_SHADER_PGM_HI_ES = 0xC9;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_ES = 0xCA;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_ES = 0xCB;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_ES_0 = 0xCC;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_ES_15 = 0xDB;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_ES_31 = 0xEB;
|
||||
// Private AGC registers used by shader binaries submitted through SET_SH_REG_INDIRECT.
|
||||
constexpr uint32_t SPI_SHADER_PGM_CHKSUM_HS = 0x100;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC4_HS = 0x101;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_ADDR_LO_HS = 0x102;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_ADDR_HI_HS = 0x103;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC3_HS = 0x107;
|
||||
constexpr uint32_t SPI_GRAPHICS_SHADER_CONTROL_HS = 0x107;
|
||||
constexpr uint32_t SPI_SHADER_PGM_LO_HS = 0x108;
|
||||
constexpr uint32_t SPI_SHADER_PGM_HI_HS = 0x109;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_HS = 0x10A;
|
||||
@@ -947,13 +877,10 @@ constexpr uint32_t SPI_SHADER_PGM_RSRC2_HS_SHARED_VGPR_CNT_MASK = 0xF;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_HS_0 = 0x10C;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_HS_15 = 0x11B;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_HS_31 = 0x12B;
|
||||
constexpr uint32_t SPI_SHADER_REQ_CTRL_LSHS = 0x130;
|
||||
constexpr uint32_t SPI_SHADER_USER_ACCUM_LSHS_0 = 0x132;
|
||||
constexpr uint32_t SPI_SHADER_USER_ACCUM_LSHS_3 = 0x135;
|
||||
constexpr uint32_t SPI_SHADER_PGM_LO_LS = 0x148;
|
||||
constexpr uint32_t SPI_SHADER_PGM_HI_LS = 0x149;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_LS = 0x14A;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_LS = 0x14B;
|
||||
constexpr uint32_t COMPUTE_START_X = 0x204;
|
||||
constexpr uint32_t COMPUTE_START_Y = 0x205;
|
||||
constexpr uint32_t COMPUTE_START_Z = 0x206;
|
||||
@@ -962,30 +889,36 @@ constexpr uint32_t COMPUTE_NUM_THREAD_Y = 0x208;
|
||||
constexpr uint32_t COMPUTE_NUM_THREAD_Z = 0x209;
|
||||
constexpr uint32_t COMPUTE_PGM_LO = 0x20C;
|
||||
constexpr uint32_t COMPUTE_PGM_HI = 0x20D;
|
||||
constexpr uint32_t COMPUTE_TBA_LO = 0x20E;
|
||||
constexpr uint32_t COMPUTE_TBA_HI = 0x20F;
|
||||
constexpr uint32_t COMPUTE_TMA_LO = 0x210;
|
||||
constexpr uint32_t COMPUTE_TMA_HI = 0x211;
|
||||
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1 = 0x212;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_VGPRS_SHIFT = 0;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_VGPRS_MASK = 0x3F;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_SGPRS_SHIFT = 6;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_SGPRS_MASK = 0xF;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_PRIORITY_SHIFT = 10;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_PRIORITY_MASK = 0x3;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_FLOAT_MODE_SHIFT = 12;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_FLOAT_MODE_MASK = 0xFF;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_DX10_CLAMP_SHIFT = 21;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_DX10_CLAMP_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_DEBUG_MODE_SHIFT = 22;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_DEBUG_MODE_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_IEEE_MODE_SHIFT = 23;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_IEEE_MODE_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_BULKY_SHIFT = 24;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_BULKY_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_FP16_OVFL_SHIFT = 26;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_FP16_OVFL_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_WGP_MODE_SHIFT = 29;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_WGP_MODE_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_W32_EN_SHIFT = 30;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_W32_EN_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_FWD_PROGRESS_SHIFT = 31;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_FWD_PROGRESS_MASK = 0x1;
|
||||
|
||||
constexpr uint32_t COMPUTE_DISPATCH_INITIATOR_CS_W32_EN_SHIFT = 15;
|
||||
constexpr uint32_t COMPUTE_DISPATCH_INITIATOR_CS_W32_EN_MASK = 0x1;
|
||||
|
||||
[[nodiscard]] constexpr uint8_t ComputeWaveSize(uint32_t dispatch_initiator) {
|
||||
return ((dispatch_initiator >> COMPUTE_DISPATCH_INITIATOR_CS_W32_EN_SHIFT) &
|
||||
COMPUTE_DISPATCH_INITIATOR_CS_W32_EN_MASK) != 0u
|
||||
? 32u
|
||||
: 64u;
|
||||
}
|
||||
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC2 = 0x213;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC2_SCRATCH_EN_SHIFT = 0;
|
||||
@@ -1006,21 +939,16 @@ constexpr uint32_t COMPUTE_PGM_RSRC2_LDS_SIZE_SHIFT = 15;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC2_LDS_SIZE_MASK = 0x1FF;
|
||||
|
||||
constexpr uint32_t COMPUTE_RESOURCE_LIMITS = 0x215;
|
||||
constexpr uint32_t COMPUTE_DESTINATION_EN_SE0 = 0x216;
|
||||
constexpr uint32_t COMPUTE_DESTINATION_EN_SE1 = 0x217;
|
||||
constexpr uint32_t COMPUTE_TMPRING_SIZE = 0x218;
|
||||
constexpr uint32_t COMPUTE_DESTINATION_EN_SE2 = 0x219;
|
||||
constexpr uint32_t COMPUTE_DESTINATION_EN_SE3 = 0x21A;
|
||||
constexpr uint32_t COMPUTE_USER_ACCUM_0 = 0x224;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC3 = 0x228;
|
||||
constexpr uint32_t COMPUTE_SHADER_CHKSUM = 0x22A;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC3_SHARED_VGPRS_SHIFT = 0;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC3_SHARED_VGPRS_MASK = 0xF;
|
||||
constexpr uint32_t COMPUTE_PACE_ID = 0x22A;
|
||||
constexpr uint32_t COMPUTE_USER_DATA_0 = 0x240;
|
||||
constexpr uint32_t COMPUTE_USER_DATA_15 = 0x24F;
|
||||
constexpr uint32_t COMPUTE_DISPATCH_TUNNEL = 0x27D;
|
||||
|
||||
/* Fake codes. Don't exist on real HW */
|
||||
|
||||
constexpr uint32_t SH_NOP = 0x800002FF;
|
||||
constexpr uint32_t SH_NOP = 0x280;
|
||||
|
||||
constexpr uint32_t SH_NUM = 0x2FF + 1;
|
||||
|
||||
@@ -1037,6 +965,7 @@ constexpr uint32_t GE_INDX_OFFSET = 0x24A;
|
||||
constexpr uint32_t GE_MULTI_PRIM_IB_RESET_EN = 0x24B;
|
||||
constexpr uint32_t VGT_HS_OFFCHIP_PARAM = 0x24F;
|
||||
constexpr uint32_t VGT_TF_MEMORY_BASE = 0x250;
|
||||
constexpr uint32_t IA_MULTI_VGT_PARAM = 0x258;
|
||||
|
||||
constexpr uint32_t GE_CNTL = 0x25B;
|
||||
constexpr uint32_t GE_CNTL_PRIM_GRP_SIZE_SHIFT = 0;
|
||||
@@ -1048,7 +977,7 @@ constexpr uint32_t GE_USER_VGPR1 = 0x25C;
|
||||
constexpr uint32_t GE_USER_VGPR2 = 0x25D;
|
||||
constexpr uint32_t GE_USER_VGPR3 = 0x25E;
|
||||
constexpr uint32_t GE_STEREO_CNTL = 0x25F;
|
||||
constexpr uint32_t GE_PC_ALLOC = 0x260;
|
||||
constexpr uint32_t UC_PARAMETER_OVERSUBSCRIPTION = 0x260;
|
||||
|
||||
constexpr uint32_t GE_USER_VGPR_EN = 0x262;
|
||||
constexpr uint32_t GE_USER_VGPR_EN_EN_USER_VGPR1_SHIFT = 0;
|
||||
@@ -1058,32 +987,25 @@ constexpr uint32_t GE_USER_VGPR_EN_EN_USER_VGPR2_MASK = 0x1;
|
||||
constexpr uint32_t GE_USER_VGPR_EN_EN_USER_VGPR3_SHIFT = 2;
|
||||
constexpr uint32_t GE_USER_VGPR_EN_EN_USER_VGPR3_MASK = 0x1;
|
||||
|
||||
constexpr uint32_t IA_MULTI_VGT_PARAM = 0x258;
|
||||
constexpr uint32_t FSR_WINDOW_LEFT = 0x2BC;
|
||||
constexpr uint32_t FSR_CONTROL_POINTS_LEFT_X = 0x2C4;
|
||||
constexpr uint32_t FSR_CONTROL_POINTS_LEFT_Y = 0x2CC;
|
||||
constexpr uint32_t FSR_ALPHA_LEFT_X = 0x2E4;
|
||||
constexpr uint32_t FSR_ALPHA_LEFT_Y = 0x2E8;
|
||||
|
||||
constexpr uint32_t TA_CS_BC_BASE_ADDR = 0x380;
|
||||
constexpr uint32_t TA_CS_BC_BASE_ADDR_HI = 0x381;
|
||||
constexpr uint32_t TEXTURE_GRADIENT_FACTORS = 0x382;
|
||||
constexpr uint32_t TEXTURE_GRADIENT_FACTORS = 0x383;
|
||||
constexpr uint32_t TEXTURE_GRADIENT_CONTROL = 0x384;
|
||||
constexpr uint32_t GDS_OA_CNTL = 0x41D;
|
||||
constexpr uint32_t GDS_OA_COUNTER = 0x41E;
|
||||
constexpr uint32_t GDS_OA_ADDRESS = 0x41F;
|
||||
|
||||
/* Fake codes. Don't exist on real HW */
|
||||
|
||||
constexpr uint32_t FSR_EXTEND_SUBPIXEL_ROUNDING = 0x80003FF4;
|
||||
constexpr uint32_t FSR_ALPHA_VALUE0 = 0x80003FF5;
|
||||
constexpr uint32_t FSR_ALPHA_VALUE1 = 0x80003FF6;
|
||||
constexpr uint32_t FSR_CONTROL_POINT0 = 0x80003FF7;
|
||||
constexpr uint32_t FSR_CONTROL_POINT1 = 0x80003FF8;
|
||||
constexpr uint32_t FSR_CONTROL_POINT2 = 0x80003FF9;
|
||||
constexpr uint32_t FSR_CONTROL_POINT3 = 0x80003FFA;
|
||||
constexpr uint32_t FSR_WINDOW0 = 0x80003FFB;
|
||||
constexpr uint32_t FSR_WINDOW1 = 0x80003FFC;
|
||||
constexpr uint32_t TEXTURE_GRADIENT_CONTROL = 0x80003FFD;
|
||||
constexpr uint32_t MEMORY_MAPPING_MASK = 0x80003FFE;
|
||||
constexpr uint32_t UC_NOP = 0x80003FFF;
|
||||
constexpr uint32_t UC_NOP = 0xA2;
|
||||
|
||||
constexpr uint32_t UC_NUM = 0x3FFF + 1;
|
||||
|
||||
void DumpPm4PacketStream(Common::File* file, uint32_t* cmd_buffer, uint32_t start_dw,
|
||||
void DumpPm4PacketStream(Common::File* file, const uint32_t* cmd_buffer, uint32_t start_dw,
|
||||
uint32_t num_dw);
|
||||
|
||||
} // namespace Libs::Graphics::Pm4
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "graphics/guest_gpu/tile.h"
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/profiler.h"
|
||||
@@ -15,10 +16,6 @@
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
static uint32_t AlignUp(uint32_t value, uint32_t alignment) {
|
||||
return (value + alignment - 1u) & ~(alignment - 1u);
|
||||
}
|
||||
|
||||
static uint32_t ShiftCeil(uint32_t value, uint32_t shift) {
|
||||
return static_cast<uint32_t>((static_cast<uint64_t>(value) + (1ull << shift) - 1ull) >> shift);
|
||||
}
|
||||
@@ -33,7 +30,7 @@ static uint32_t CalcLinearAlignedLevelPitch(uint32_t base_width, uint32_t base_h
|
||||
const uint32_t level_width = ShiftCeil(base_width, level);
|
||||
const uint32_t level_height = ShiftCeil(base_height, level);
|
||||
const uint32_t padded_width =
|
||||
AlignUp(std::max(level_width, 1u), CalcLinearBlockWidth(bytes_per_element));
|
||||
Common::AlignUp(std::max(level_width, 1u), CalcLinearBlockWidth(bytes_per_element));
|
||||
const uint64_t size =
|
||||
static_cast<uint64_t>(padded_width) * std::max(level_height, 1u) * bytes_per_element;
|
||||
EXIT_NOT_IMPLEMENTED(size > 0xffffffffull);
|
||||
@@ -61,7 +58,7 @@ static uint32_t SetLinearMipChainLayout(uint32_t levels, const uint32_t* mip_pit
|
||||
offset += mip_size[level];
|
||||
}
|
||||
|
||||
return AlignUp(offset, 256u);
|
||||
return Common::AlignUp(offset, 256u);
|
||||
}
|
||||
|
||||
struct Log2BlockDimensions {
|
||||
@@ -265,7 +262,7 @@ static bool GetMipTailLayout(const TileBlockLayout& block, MipTailLayout& out) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TileGetTextureElementLayout(uint32_t format, TileTextureElementLayout& out) {
|
||||
bool TileGetTextureElementLayout(Prospero::BufferFormat format, TileTextureElementLayout& out) {
|
||||
if (const auto bytes = Prospero::NumBytesPerElement(format); std::has_single_bit(bytes)) {
|
||||
out = {bytes, 1, 1};
|
||||
return true;
|
||||
@@ -277,10 +274,10 @@ bool TileGetTextureElementLayout(uint32_t format, TileTextureElementLayout& out)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TileGetTextureBlockLayout(uint32_t format, uint32_t tile, bool volume,
|
||||
bool TileGetTextureBlockLayout(Prospero::BufferFormat format, Prospero::TileMode tile, bool volume,
|
||||
TileTextureBlockLayout& out) {
|
||||
TileBlockFamily family = TileBlockFamily::Count;
|
||||
switch (static_cast<Prospero::TileMode>(tile)) {
|
||||
switch (tile) {
|
||||
case Prospero::TileMode::kStandard256B:
|
||||
if (!volume) family = TileBlockFamily::Standard256B;
|
||||
break;
|
||||
@@ -366,8 +363,8 @@ bool TileGetTiledTextureLayout(const TileSurfaceDescription& description, TileSu
|
||||
((description.width >> level) + texture.texel_width - 1u) / texture.texel_width, 1u);
|
||||
mip.height = std::max(
|
||||
((description.height >> level) + texture.texel_height - 1u) / texture.texel_height, 1u);
|
||||
mip.padded_width = AlignUp(std::max(ShiftCeil(width0, level), 1u), block.block_width);
|
||||
mip.padded_height = AlignUp(std::max(ShiftCeil(height0, level), 1u), block.block_height);
|
||||
mip.padded_width = Common::AlignUp(std::max(ShiftCeil(width0, level), 1u), block.block_width);
|
||||
mip.padded_height = Common::AlignUp(std::max(ShiftCeil(height0, level), 1u), block.block_height);
|
||||
mip.size = static_cast<uint64_t>(block.block_depth) * mip.padded_width * mip.padded_height *
|
||||
block.bytes_per_element;
|
||||
result.block_slice_size += mip.size;
|
||||
@@ -1121,8 +1118,8 @@ static bool TileGetHtileSize(uint32_t width, uint32_t height, TileSizeAlign& hti
|
||||
}
|
||||
// Prospero HTile stores one DWORD per depth tile. Its 32 KiB allocation blocks cover
|
||||
// 1024x512 pixels, independently of the attachment's fragment count.
|
||||
const uint64_t size = static_cast<uint64_t>(AlignUp(width, 1024u) / 1024u) *
|
||||
(AlignUp(height, 512u) / 512u) * 32768u;
|
||||
const uint64_t size = static_cast<uint64_t>(Common::AlignUp(width, 1024u) / 1024u) *
|
||||
(Common::AlignUp(height, 512u) / 512u) * 32768u;
|
||||
if (size == 0 || size > UINT32_MAX) {
|
||||
return false;
|
||||
}
|
||||
@@ -1130,15 +1127,18 @@ static bool TileGetHtileSize(uint32_t width, uint32_t height, TileSizeAlign& hti
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TileGetDepthSize(uint32_t width, uint32_t height, uint32_t pitch, uint32_t z_format,
|
||||
uint32_t stencil_format, bool htile, TileSizeAlign& stencil_size,
|
||||
TileSizeAlign& htile_size, TileSizeAlign& depth_size,
|
||||
uint32_t num_fragments_log2) {
|
||||
bool TileGetDepthSize(uint32_t width, uint32_t height, uint32_t pitch,
|
||||
Prospero::DepthFormat z_format, Prospero::StencilFormat stencil_format,
|
||||
bool htile, TileSizeAlign& stencil_size, TileSizeAlign& htile_size,
|
||||
TileSizeAlign& depth_size, uint32_t num_fragments_log2) {
|
||||
EXIT_IF(pitch != 0);
|
||||
// Prospero derives uncompressed depth/stencil as independent 64 KiB block surfaces.
|
||||
if (width > 0 && width <= 16384 && height > 0 && height <= 16384 &&
|
||||
(z_format == 1 || z_format == 3) && stencil_format <= 1 && num_fragments_log2 <= 3) {
|
||||
const uint32_t depth_bytes = z_format == 1 ? 2u : 4u;
|
||||
(z_format == Prospero::DepthFormat::kZ16 || z_format == Prospero::DepthFormat::kZ32F) &&
|
||||
(stencil_format == Prospero::StencilFormat::kInvalid ||
|
||||
stencil_format == Prospero::StencilFormat::k8UInt) &&
|
||||
num_fragments_log2 <= 3) {
|
||||
const uint32_t depth_bytes = z_format == Prospero::DepthFormat::kZ16 ? 2u : 4u;
|
||||
uint32_t depth_block_width = 0;
|
||||
uint32_t depth_block_height = 0;
|
||||
uint32_t stencil_block_width = 0;
|
||||
@@ -1146,24 +1146,24 @@ bool TileGetDepthSize(uint32_t width, uint32_t height, uint32_t pitch, uint32_t
|
||||
const bool valid_blocks =
|
||||
Gen5Msaa64KBBlockSizeFromElementBytes(depth_bytes, num_fragments_log2,
|
||||
&depth_block_width, &depth_block_height) &&
|
||||
(stencil_format == 0 ||
|
||||
(stencil_format == Prospero::StencilFormat::kInvalid ||
|
||||
Gen5Msaa64KBBlockSizeFromElementBytes(1, num_fragments_log2, &stencil_block_width,
|
||||
&stencil_block_height));
|
||||
const uint32_t fragments = 1u << num_fragments_log2;
|
||||
const uint64_t depth_bytes_total =
|
||||
valid_blocks ? static_cast<uint64_t>(AlignUp(width, depth_block_width)) *
|
||||
AlignUp(height, depth_block_height) * depth_bytes * fragments
|
||||
valid_blocks ? static_cast<uint64_t>(Common::AlignUp(width, depth_block_width)) *
|
||||
Common::AlignUp(height, depth_block_height) * depth_bytes * fragments
|
||||
: 0;
|
||||
const uint64_t stencil_bytes_total =
|
||||
stencil_format == 1 && valid_blocks
|
||||
? static_cast<uint64_t>(AlignUp(width, stencil_block_width)) *
|
||||
AlignUp(height, stencil_block_height) * fragments
|
||||
stencil_format == Prospero::StencilFormat::k8UInt && valid_blocks
|
||||
? static_cast<uint64_t>(Common::AlignUp(width, stencil_block_width)) *
|
||||
Common::AlignUp(height, stencil_block_height) * fragments
|
||||
: 0;
|
||||
TileSizeAlign calculated_htile {};
|
||||
const bool htile_valid = !htile || TileGetHtileSize(width, height, calculated_htile);
|
||||
if (depth_bytes_total <= UINT32_MAX && stencil_bytes_total <= UINT32_MAX && htile_valid) {
|
||||
depth_size = {static_cast<uint32_t>(depth_bytes_total), 65536};
|
||||
stencil_size = stencil_format == 1
|
||||
stencil_size = stencil_format == Prospero::StencilFormat::k8UInt
|
||||
? TileSizeAlign {static_cast<uint32_t>(stencil_bytes_total), 65536}
|
||||
: TileSizeAlign {};
|
||||
htile_size = calculated_htile;
|
||||
@@ -1184,8 +1184,7 @@ uint32_t TileGetRenderTargetPitch(uint32_t width, uint32_t bytes_per_element,
|
||||
&block_width, &block_height)) {
|
||||
return 0;
|
||||
}
|
||||
const uint64_t pitch = (static_cast<uint64_t>(width) + block_width - 1u) &
|
||||
~static_cast<uint64_t>(block_width - 1u);
|
||||
const uint64_t pitch = Common::AlignUp(static_cast<uint64_t>(width), block_width);
|
||||
return pitch <= UINT32_MAX ? static_cast<uint32_t>(pitch) : 0;
|
||||
}
|
||||
|
||||
@@ -1206,8 +1205,7 @@ bool TileGetRenderTargetSize(uint32_t width, uint32_t height, uint32_t pitch,
|
||||
pitch != TileGetRenderTargetPitch(width, bytes_per_element, num_fragments_log2)) {
|
||||
return false;
|
||||
}
|
||||
const uint64_t padded_height = (static_cast<uint64_t>(height) + block_height - 1u) &
|
||||
~static_cast<uint64_t>(block_height - 1u);
|
||||
const uint64_t padded_height = Common::AlignUp(static_cast<uint64_t>(height), block_height);
|
||||
const uint64_t size = static_cast<uint64_t>(pitch) * padded_height * bytes_per_element *
|
||||
(1u << num_fragments_log2);
|
||||
if (size == 0 || size > UINT32_MAX) {
|
||||
@@ -1218,6 +1216,31 @@ bool TileGetRenderTargetSize(uint32_t width, uint32_t height, uint32_t pitch,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TileGetDccSize(uint32_t width, uint32_t height, uint32_t slices,
|
||||
uint32_t bytes_per_element, uint32_t levels, Prospero::TileMode tile,
|
||||
TileSizeAlign& total_size, uint32_t num_fragments_log2) {
|
||||
total_size = {};
|
||||
if (width == 0 || height == 0 || slices == 0 || levels != 1 || num_fragments_log2 != 0 ||
|
||||
!std::has_single_bit(bytes_per_element) || bytes_per_element > 16 ||
|
||||
(tile != Prospero::TileMode::kRenderTarget && tile != Prospero::TileMode::kDepth)) {
|
||||
return false;
|
||||
}
|
||||
// Gen5 color metadata uses 4 KiB blocks, with one byte per 256 bytes of color data.
|
||||
// Metadata stays thin for volume surfaces, so every depth slice has its own block raster.
|
||||
const uint32_t coverage_bits = 20u - std::countr_zero(bytes_per_element);
|
||||
const uint32_t block_width = 1u << ((coverage_bits + 1u) / 2u);
|
||||
const uint32_t block_height = 1u << (coverage_bits / 2u);
|
||||
const uint64_t blocks_x = (static_cast<uint64_t>(width) + block_width - 1u) / block_width;
|
||||
const uint64_t blocks_y = (static_cast<uint64_t>(height) + block_height - 1u) / block_height;
|
||||
const uint64_t blocks = blocks_x * blocks_y;
|
||||
if (blocks > UINT32_MAX / 4096u / slices) {
|
||||
return false;
|
||||
}
|
||||
total_size.size = static_cast<uint32_t>(blocks * slices * 4096u);
|
||||
total_size.align = 4096;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TileGetRenderTargetMipLayout(uint32_t width, uint32_t height, uint32_t pitch,
|
||||
uint32_t bytes_per_element, uint32_t levels,
|
||||
TileSizeAlign& total_size, TileSizeOffset* level_sizes,
|
||||
@@ -1236,31 +1259,29 @@ bool TileGetRenderTargetMipLayout(uint32_t width, uint32_t height, uint32_t pitc
|
||||
if (levels > max_levels) {
|
||||
return false;
|
||||
}
|
||||
uint32_t format = 0;
|
||||
auto format = Prospero::BufferFormat::kInvalid;
|
||||
switch (bytes_per_element) {
|
||||
case 1: format = Prospero::GpuEnumValue(Prospero::BufferFormat::k8UNorm); break;
|
||||
case 2: format = Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm); break;
|
||||
case 4: format = Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float); break;
|
||||
case 8: format = Prospero::GpuEnumValue(Prospero::BufferFormat::k16_16_16_16Float); break;
|
||||
case 16: format = Prospero::GpuEnumValue(Prospero::BufferFormat::k32_32_32_32Float); break;
|
||||
case 1: format = Prospero::BufferFormat::k8UNorm; break;
|
||||
case 2: format = Prospero::BufferFormat::k16UNorm; break;
|
||||
case 4: format = Prospero::BufferFormat::k32Float; break;
|
||||
case 8: format = Prospero::BufferFormat::k16_16_16_16Float; break;
|
||||
case 16: format = Prospero::BufferFormat::k32_32_32_32Float; break;
|
||||
default: return false;
|
||||
}
|
||||
TileGetTextureSize(format, width, height, levels,
|
||||
Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget), &total_size,
|
||||
level_sizes, padded_size);
|
||||
TileGetTextureSize(format, width, height, levels, Prospero::TileMode::kRenderTarget,
|
||||
&total_size, level_sizes, padded_size);
|
||||
return total_size.size != 0 && total_size.align == 65536;
|
||||
}
|
||||
|
||||
void TileGetTextureSize(uint32_t format, uint32_t width, uint32_t height, uint32_t levels,
|
||||
uint32_t tile, TileSizeAlign* total_size, TileSizeOffset* level_sizes,
|
||||
TilePaddedSize* padded_size) {
|
||||
void TileGetTextureSize(Prospero::BufferFormat format, uint32_t width, uint32_t height,
|
||||
uint32_t levels, Prospero::TileMode tile, TileSizeAlign* total_size,
|
||||
TileSizeOffset* level_sizes, TilePaddedSize* padded_size) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
|
||||
EXIT_IF(levels == 0 || levels > 16);
|
||||
|
||||
TileTextureElementLayout element {};
|
||||
if (static_cast<Prospero::TileMode>(tile) == Prospero::TileMode::kLinear &&
|
||||
TileGetTextureElementLayout(format, element)) {
|
||||
if (tile == Prospero::TileMode::kLinear && TileGetTextureElementLayout(format, element)) {
|
||||
uint32_t mip_pitch[16] {};
|
||||
uint32_t mip_height[16] {};
|
||||
uint32_t mip_size[16] {};
|
||||
@@ -1302,26 +1323,27 @@ void TileGetTextureSize(uint32_t format, uint32_t width, uint32_t height, uint32
|
||||
}
|
||||
if (total_size != nullptr && total_size->size == 0) {
|
||||
std::vector<std::string> list;
|
||||
list.push_back(fmt::format("format = {}", format));
|
||||
list.push_back(fmt::format("format = {}", static_cast<uint32_t>(format)));
|
||||
list.push_back(fmt::format("width = {}", width));
|
||||
list.push_back(fmt::format("height = {}", height));
|
||||
list.push_back(fmt::format("levels = {}", levels));
|
||||
list.push_back(fmt::format("tile = {}", tile));
|
||||
list.push_back(fmt::format("tile = {}", static_cast<uint32_t>(tile)));
|
||||
EXIT("unknown format:\n%s\n", Common::Concat(list, '\n').c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void TileGetTextureTotalSize(uint32_t format, uint32_t width, uint32_t height, uint32_t depth,
|
||||
uint32_t levels, uint32_t tile, bool volume_texture,
|
||||
TileSizeAlign& total_size) {
|
||||
void TileGetTextureTotalSize(Prospero::BufferFormat format, uint32_t width, uint32_t height,
|
||||
uint32_t depth, uint32_t levels, Prospero::TileMode tile,
|
||||
bool volume_texture, TileSizeAlign& total_size) {
|
||||
EXIT_NOT_IMPLEMENTED(depth == 0);
|
||||
if (volume_texture && static_cast<Prospero::TileMode>(tile) != Prospero::TileMode::kLinear) {
|
||||
if (volume_texture && tile != Prospero::TileMode::kLinear) {
|
||||
TileSurfaceLayout layout {};
|
||||
const TileSurfaceDescription description {
|
||||
format, tile, TileSurfaceDimension::Dim3D, width, height, depth, levels, 1};
|
||||
if (!TileGetTiledTextureLayout(description, layout)) {
|
||||
EXIT("unsupported 3D texture layout: format=%u tile=%u extent=%ux%ux%u levels=%u\n",
|
||||
format, tile, width, height, depth, levels);
|
||||
static_cast<uint32_t>(format), static_cast<uint32_t>(tile), width, height, depth,
|
||||
levels);
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(layout.total_size > UINT32_MAX);
|
||||
total_size.size = static_cast<uint32_t>(layout.total_size);
|
||||
@@ -1337,24 +1359,26 @@ void TileGetTextureTotalSize(uint32_t format, uint32_t width, uint32_t height, u
|
||||
total_size.size = static_cast<uint32_t>(total);
|
||||
}
|
||||
|
||||
uint32_t TileGetTexturePitch(uint32_t format, uint32_t width, uint32_t tile) {
|
||||
uint32_t TileGetTexturePitch(Prospero::BufferFormat format, uint32_t width,
|
||||
Prospero::TileMode tile) {
|
||||
uint32_t pitch = width;
|
||||
switch (static_cast<Prospero::TileMode>(tile)) {
|
||||
switch (tile) {
|
||||
case Prospero::TileMode::kLinear: {
|
||||
TileTextureElementLayout element {};
|
||||
if (TileGetTextureElementLayout(format, element) && element.texel_width == 1 &&
|
||||
element.texel_height == 1) {
|
||||
pitch = AlignUp(pitch, CalcLinearBlockWidth(element.bytes));
|
||||
pitch = Common::AlignUp(pitch, CalcLinearBlockWidth(element.bytes));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Prospero::TileMode::kStandard4KB:
|
||||
case Prospero::TileMode::kStandard64KB:
|
||||
case Prospero::TileMode::kPrt:
|
||||
case Prospero::TileMode::kDepth:
|
||||
case Prospero::TileMode::kRenderTarget: {
|
||||
TileTextureBlockLayout layout {};
|
||||
if (TileGetTextureBlockLayout(format, tile, false, layout)) {
|
||||
pitch = AlignUp(pitch, layout.block.block_width * layout.texel_width);
|
||||
pitch = Common::AlignUp(pitch, layout.block.block_width * layout.texel_width);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "common/common.h"
|
||||
#include "graphics/guest_gpu/gpu_defs.h"
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
@@ -65,14 +66,14 @@ enum class TileSurfaceDimension : uint32_t {
|
||||
};
|
||||
|
||||
struct TileSurfaceDescription {
|
||||
uint32_t format = 0;
|
||||
uint32_t tile_mode = 0;
|
||||
TileSurfaceDimension dimension = TileSurfaceDimension::Dim2D;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
uint32_t depth = 1;
|
||||
uint32_t levels = 1;
|
||||
uint32_t layers = 1;
|
||||
Prospero::BufferFormat format = Prospero::BufferFormat::kInvalid;
|
||||
Prospero::TileMode tile_mode = Prospero::TileMode::kLinear;
|
||||
TileSurfaceDimension dimension = TileSurfaceDimension::Dim2D;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
uint32_t depth = 1;
|
||||
uint32_t levels = 1;
|
||||
uint32_t layers = 1;
|
||||
};
|
||||
|
||||
// Width/height fields are measured in addressable elements, not texture texels.
|
||||
@@ -98,8 +99,8 @@ struct TileSurfaceLayout {
|
||||
|
||||
bool TileGetBlockLayout(TileBlockFamily family, uint32_t bytes_per_element,
|
||||
TileBlockLayout& layout);
|
||||
bool TileGetTextureElementLayout(uint32_t format, TileTextureElementLayout& layout);
|
||||
bool TileGetTextureBlockLayout(uint32_t format, uint32_t tile, bool volume,
|
||||
bool TileGetTextureElementLayout(Prospero::BufferFormat format, TileTextureElementLayout& layout);
|
||||
bool TileGetTextureBlockLayout(Prospero::BufferFormat format, Prospero::TileMode tile, bool volume,
|
||||
TileTextureBlockLayout& layout);
|
||||
bool TileGetTiledTextureLayout(const TileSurfaceDescription& description,
|
||||
TileSurfaceLayout& layout);
|
||||
@@ -110,10 +111,10 @@ bool TileGetBlockXor(const TileBlockLayout& layout, uint32_t block_x, uint32_t b
|
||||
bool TileGetBlockXor(const TileBlockLayout& layout, uint32_t block_x, uint32_t block_y,
|
||||
uint32_t block_z, uint32_t& byte_offset);
|
||||
|
||||
bool TileGetDepthSize(uint32_t width, uint32_t height, uint32_t pitch, uint32_t z_format,
|
||||
uint32_t stencil_format, bool htile, TileSizeAlign& stencil_size,
|
||||
TileSizeAlign& htile_size, TileSizeAlign& depth_size,
|
||||
uint32_t num_fragments_log2 = 0);
|
||||
bool TileGetDepthSize(uint32_t width, uint32_t height, uint32_t pitch,
|
||||
Prospero::DepthFormat z_format, Prospero::StencilFormat stencil_format,
|
||||
bool htile, TileSizeAlign& stencil_size, TileSizeAlign& htile_size,
|
||||
TileSizeAlign& depth_size, uint32_t num_fragments_log2 = 0);
|
||||
uint32_t TileGetRenderTargetPitch(uint32_t width, uint32_t bytes_per_element,
|
||||
uint32_t num_fragments_log2 = 0);
|
||||
uint32_t TileGetDepthPitch(uint32_t width, uint32_t bytes_per_element,
|
||||
@@ -121,17 +122,21 @@ uint32_t TileGetDepthPitch(uint32_t width, uint32_t bytes_per_element,
|
||||
bool TileGetRenderTargetSize(uint32_t width, uint32_t height, uint32_t pitch,
|
||||
uint32_t bytes_per_element, TileSizeAlign& total_size,
|
||||
uint32_t num_fragments_log2 = 0);
|
||||
bool TileGetDccSize(uint32_t width, uint32_t height, uint32_t slices,
|
||||
uint32_t bytes_per_element, uint32_t levels, Prospero::TileMode tile,
|
||||
TileSizeAlign& total_size, uint32_t num_fragments_log2 = 0);
|
||||
bool TileGetRenderTargetMipLayout(uint32_t width, uint32_t height, uint32_t pitch,
|
||||
uint32_t bytes_per_element, uint32_t levels,
|
||||
TileSizeAlign& total_size, TileSizeOffset* level_sizes,
|
||||
TilePaddedSize* padded_size);
|
||||
void TileGetTextureSize(uint32_t format, uint32_t width, uint32_t height, uint32_t levels,
|
||||
uint32_t tile, TileSizeAlign* total_size, TileSizeOffset* level_sizes,
|
||||
TilePaddedSize* padded_size);
|
||||
void TileGetTextureTotalSize(uint32_t format, uint32_t width, uint32_t height, uint32_t depth,
|
||||
uint32_t levels, uint32_t tile, bool volume_texture,
|
||||
TileSizeAlign& total_size);
|
||||
uint32_t TileGetTexturePitch(uint32_t format, uint32_t width, uint32_t tile);
|
||||
void TileGetTextureSize(Prospero::BufferFormat format, uint32_t width, uint32_t height,
|
||||
uint32_t levels, Prospero::TileMode tile, TileSizeAlign* total_size,
|
||||
TileSizeOffset* level_sizes, TilePaddedSize* padded_size);
|
||||
void TileGetTextureTotalSize(Prospero::BufferFormat format, uint32_t width, uint32_t height,
|
||||
uint32_t depth, uint32_t levels, Prospero::TileMode tile,
|
||||
bool volume_texture, TileSizeAlign& total_size);
|
||||
uint32_t TileGetTexturePitch(Prospero::BufferFormat format, uint32_t width,
|
||||
Prospero::TileMode tile);
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
|
||||
@@ -3,51 +3,113 @@
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "common/common.h"
|
||||
#include "common/threads.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h" // IWYU pragma: export
|
||||
#include "graphics/host_gpu/vulkanInstance.h"
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <vk_mem_alloc.h>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
class CommandBuffer;
|
||||
struct VulkanBuffer;
|
||||
struct VulkanImage;
|
||||
struct VulkanMemory;
|
||||
|
||||
struct GraphicContext: public VulkanInstance {
|
||||
inline constexpr uint32_t VULKAN_TARGET_API_VERSION = VK_API_VERSION_1_3;
|
||||
|
||||
struct GraphicContext {
|
||||
vk::Instance instance = nullptr;
|
||||
vk::DebugUtilsMessengerEXT debug_messenger = nullptr;
|
||||
vk::PhysicalDevice physical_device = nullptr;
|
||||
vk::PhysicalDeviceProperties physical_device_properties = {};
|
||||
vk::PhysicalDeviceMemoryProperties physical_device_memory_properties = {};
|
||||
vk::Device device = nullptr;
|
||||
VmaAllocator allocator = nullptr;
|
||||
bool memory_budget_ext_enabled = false;
|
||||
bool compute_subgroup_size_control_enabled = false;
|
||||
bool sample_rate_shading_enabled = false;
|
||||
bool attachment_feedback_loop_enabled = false;
|
||||
bool provoking_vertex_last_enabled = false;
|
||||
bool supports_block_texel_view = false;
|
||||
bool mesh_shader_enabled = false;
|
||||
vk::PhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = {};
|
||||
uint32_t subgroup_size = 0;
|
||||
uint32_t min_subgroup_size = 0;
|
||||
uint32_t max_subgroup_size = 0;
|
||||
uint32_t max_push_descriptors = 0;
|
||||
vk::ShaderStageFlags required_subgroup_size_stages = {};
|
||||
Common::Mutex queue_mutex;
|
||||
uint32_t queue_family = static_cast<uint32_t>(-1);
|
||||
vk::Queue queue = nullptr;
|
||||
|
||||
[[nodiscard]] const vk::PhysicalDeviceProperties& GetPhysicalDeviceProperties() const {
|
||||
return physical_device_properties;
|
||||
}
|
||||
|
||||
[[nodiscard]] const vk::PhysicalDeviceMemoryProperties&
|
||||
GetPhysicalDeviceMemoryProperties() const {
|
||||
return physical_device_memory_properties;
|
||||
}
|
||||
|
||||
[[nodiscard]] vk::FormatProperties GetFormatProperties(vk::Format format) const {
|
||||
std::scoped_lock lock(m_format_properties_mutex);
|
||||
auto [it, inserted] = m_format_properties.try_emplace(format);
|
||||
if (inserted) {
|
||||
physical_device.getFormatProperties(format, &it->second);
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
[[nodiscard]] vk::Result GetImageFormatProperties(vk::Format format, vk::ImageType type,
|
||||
vk::ImageTiling tiling,
|
||||
vk::ImageUsageFlags usage,
|
||||
vk::ImageCreateFlags flags,
|
||||
vk::ImageFormatProperties* properties) const {
|
||||
using Key = std::tuple<vk::Format, vk::ImageType, vk::ImageTiling, vk::ImageUsageFlags,
|
||||
vk::ImageCreateFlags>;
|
||||
std::scoped_lock lock(m_image_format_properties_mutex);
|
||||
auto [it, inserted] =
|
||||
m_image_format_properties.try_emplace(Key {format, type, tiling, usage, flags});
|
||||
if (inserted) {
|
||||
it->second.first = physical_device.getImageFormatProperties(format, type, tiling, usage,
|
||||
flags, &it->second.second);
|
||||
}
|
||||
if (properties != nullptr) {
|
||||
*properties = it->second.second;
|
||||
}
|
||||
return it->second.first;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool SupportsComputeWave64() const noexcept {
|
||||
return subgroup_size == 64u || compute_subgroup_size_control_enabled;
|
||||
}
|
||||
|
||||
[[nodiscard]] vk::DeviceSize StorageMinAlignment() const {
|
||||
const auto alignment = physical_device_properties.limits.minStorageBufferOffsetAlignment;
|
||||
return alignment != 0 ? alignment : 1;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool CreateAllocator();
|
||||
void DestroyAllocator();
|
||||
void LogMemoryBudget() const;
|
||||
[[nodiscard]] bool CanReportMemoryUsage() const noexcept { return memory_budget_ext_enabled; }
|
||||
[[nodiscard]] uint64_t GetDeviceMemoryUsage() const;
|
||||
[[nodiscard]] uint64_t GetTotalMemoryBudget() const;
|
||||
void CreateBuffer(uint64_t size, VulkanBuffer& buffer);
|
||||
void DeleteBuffer(VulkanBuffer& buffer);
|
||||
[[nodiscard]] bool CreateImage(const vk::ImageCreateInfo& info, VulkanImage& image);
|
||||
void DeleteImage(VulkanImage& image);
|
||||
void MapMemory(VulkanMemory& memory, void*& data);
|
||||
void UnmapMemory(VulkanMemory& memory);
|
||||
void AppendHardwareRayTracingDeviceExtensions(
|
||||
const std::vector<vk::ExtensionProperties>& available_extensions,
|
||||
std::vector<const char*>& device_extensions);
|
||||
void LoadHardwareRayTracingFunctions() const;
|
||||
|
||||
uint32_t screen_width = 0;
|
||||
uint32_t screen_height = 0;
|
||||
};
|
||||
|
||||
struct VulkanMemory {
|
||||
vk::MemoryRequirements requirements = {};
|
||||
vk::MemoryPropertyFlags property = {};
|
||||
vk::MemoryPropertyFlags preferred_property = {};
|
||||
vk::DeviceMemory memory = nullptr;
|
||||
VmaAllocation allocation = nullptr;
|
||||
VmaAllocationInfo allocation_info = {};
|
||||
vk::DeviceSize offset = 0;
|
||||
uint32_t type = 0;
|
||||
uint64_t unique_id = 0;
|
||||
private:
|
||||
mutable std::mutex m_format_properties_mutex;
|
||||
mutable std::map<vk::Format, vk::FormatProperties> m_format_properties;
|
||||
mutable std::mutex m_image_format_properties_mutex;
|
||||
mutable std::map<std::tuple<vk::Format, vk::ImageType, vk::ImageTiling, vk::ImageUsageFlags,
|
||||
vk::ImageCreateFlags>,
|
||||
std::pair<vk::Result, vk::ImageFormatProperties>>
|
||||
m_image_format_properties;
|
||||
};
|
||||
|
||||
struct VulkanImageState {
|
||||
@@ -63,7 +125,6 @@ struct VulkanImage {
|
||||
vk::Format format = vk::Format::eUndefined;
|
||||
vk::ImageType image_type = vk::ImageType::e2D;
|
||||
vk::Extent3D extent = {1, 1, 1};
|
||||
uint32_t guest_pitch = 0;
|
||||
uint32_t layers = 1;
|
||||
uint32_t mip_levels = 1;
|
||||
uint32_t samples = 1;
|
||||
@@ -72,15 +133,10 @@ struct VulkanImage {
|
||||
vk::Image image = nullptr;
|
||||
VulkanImageState state;
|
||||
std::vector<VulkanImageState> subresource_states;
|
||||
Graphics::VulkanMemory memory;
|
||||
VmaAllocation allocation = nullptr;
|
||||
};
|
||||
|
||||
struct VulkanBuffer {
|
||||
vk::Buffer buffer = nullptr;
|
||||
VulkanMemory memory;
|
||||
vk::BufferUsageFlags usage = {};
|
||||
uint64_t buffer_size = 0;
|
||||
};
|
||||
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "graphics/host_gpu/memoryTracker.h"
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
|
||||
namespace Libs::Graphics {
|
||||
@@ -8,9 +9,6 @@ static_assert(std::atomic<void*>::is_always_lock_free);
|
||||
|
||||
MemoryTracker::MemoryTracker(PageManager& page_manager): m_page_manager(page_manager) {
|
||||
m_regions = std::make_unique<std::atomic<RegionManager*>[]>(REGION_COUNT);
|
||||
for (size_t i = 0; i < REGION_COUNT; i++) {
|
||||
m_regions[i].store(nullptr, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
MemoryTracker::~MemoryTracker() = default;
|
||||
@@ -18,15 +16,12 @@ MemoryTracker::~MemoryTracker() = default;
|
||||
#if KYTY_BUILD == KYTY_BUILD_DEBUG
|
||||
void MemoryTracker::ValidateGpuDirtyPages(const RangeSet& dirty, uint64_t vaddr, uint64_t size,
|
||||
const char* operation) const noexcept {
|
||||
if (vaddr == 0 || size == 0 || size > UINT64_MAX - vaddr ||
|
||||
(vaddr & (TRACKER_PAGE_SIZE - 1)) != 0 || (size & (TRACKER_PAGE_SIZE - 1)) != 0) {
|
||||
if (!GuestRange {vaddr, size}.Valid() || (vaddr & (TRACKER_PAGE_SIZE - 1)) != 0 ||
|
||||
(size & (TRACKER_PAGE_SIZE - 1)) != 0) {
|
||||
EXIT("MemoryTracker: invalid dirty-page validation range\n");
|
||||
}
|
||||
for (auto page = vaddr; page < vaddr + size; page += TRACKER_PAGE_SIZE) {
|
||||
bool found = false;
|
||||
dirty.ForEachIntersection(page, TRACKER_PAGE_SIZE,
|
||||
[&found](RangeSet::Range) { found = true; });
|
||||
if (!found) {
|
||||
if (!dirty.Intersects(page, TRACKER_PAGE_SIZE)) {
|
||||
EXIT("MemoryTracker: GPU-dirty tracker page has no dirty bytes, operation=%s "
|
||||
"addr=0x%016" PRIx64 "\n",
|
||||
operation, page);
|
||||
@@ -37,15 +32,10 @@ void MemoryTracker::ValidateGpuDirtyPages(const RangeSet& dirty, uint64_t vaddr,
|
||||
void MemoryTracker::ValidateGpuDirtyOwnership(const RangeSet& dirty, uint64_t vaddr, uint64_t size,
|
||||
const char* operation) {
|
||||
ValidateRange(vaddr, size);
|
||||
if (vaddr + size > UINT64_MAX - (TRACKER_PAGE_SIZE - 1)) {
|
||||
EXIT("MemoryTracker: dirty ownership range alignment overflow\n");
|
||||
}
|
||||
const auto begin = vaddr & ~(TRACKER_PAGE_SIZE - 1);
|
||||
const auto end = (vaddr + size + TRACKER_PAGE_SIZE - 1) & ~(TRACKER_PAGE_SIZE - 1);
|
||||
const auto begin = Common::AlignDown(vaddr, TRACKER_PAGE_SIZE);
|
||||
const auto end = Common::AlignUp(vaddr + size, TRACKER_PAGE_SIZE);
|
||||
for (auto page = begin; page < end; page += TRACKER_PAGE_SIZE) {
|
||||
bool has_dirty_bytes = false;
|
||||
dirty.ForEachIntersection(page, TRACKER_PAGE_SIZE,
|
||||
[&has_dirty_bytes](RangeSet::Range) { has_dirty_bytes = true; });
|
||||
const bool has_dirty_bytes = dirty.Intersects(page, TRACKER_PAGE_SIZE);
|
||||
if (IsRegionGpuModified(page, TRACKER_PAGE_SIZE) != has_dirty_bytes) {
|
||||
EXIT("MemoryTracker: tracker and byte ownership disagree, operation=%s "
|
||||
"addr=0x%016" PRIx64 "\n",
|
||||
@@ -56,8 +46,7 @@ void MemoryTracker::ValidateGpuDirtyOwnership(const RangeSet& dirty, uint64_t va
|
||||
#endif
|
||||
|
||||
void MemoryTracker::ValidateRange(uint64_t vaddr, uint64_t size) {
|
||||
if (vaddr == 0 || size == 0 || vaddr >= TRACKER_ADDRESS_SIZE ||
|
||||
size > TRACKER_ADDRESS_SIZE - vaddr) {
|
||||
if (!GuestRange {vaddr, size}.Valid()) {
|
||||
EXIT("invalid memory tracker range\n");
|
||||
}
|
||||
}
|
||||
@@ -117,7 +106,8 @@ void MemoryTracker::UnmarkRegionAsGpuModified(uint64_t vaddr, uint64_t size) {
|
||||
});
|
||||
}
|
||||
|
||||
void MemoryTracker::UntrackMemoryImpl(uint64_t vaddr, uint64_t size) {
|
||||
void MemoryTracker::UntrackMemory(uint64_t vaddr, uint64_t size) {
|
||||
CheckNotInUploadCallback();
|
||||
std::vector<RegionManager*> managers;
|
||||
managers.reserve((vaddr % TRACKER_REGION_SIZE + size + TRACKER_REGION_SIZE - 1) /
|
||||
TRACKER_REGION_SIZE);
|
||||
@@ -138,12 +128,6 @@ void MemoryTracker::UntrackMemoryImpl(uint64_t vaddr, uint64_t size) {
|
||||
Iterate<false>(vaddr, size, [](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
||||
manager->ChangeState<DirtySource::Cpu, true>(manager->GetCpuAddr() + offset, bytes);
|
||||
});
|
||||
locks.clear();
|
||||
}
|
||||
|
||||
void MemoryTracker::UntrackMemory(uint64_t vaddr, uint64_t size) {
|
||||
CheckNotInUploadCallback();
|
||||
UntrackMemoryImpl(vaddr, size);
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
@@ -34,7 +34,6 @@ public:
|
||||
void InvalidateRegion(uint64_t vaddr, uint64_t size, Flush&& on_flush) noexcept {
|
||||
static_assert(std::is_invocable_v<Flush&>);
|
||||
CheckNotInUploadCallback();
|
||||
ValidateRange(vaddr, size);
|
||||
|
||||
Iterate<false>(vaddr, size, [&](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
||||
const bool should_flush = [&] {
|
||||
@@ -63,43 +62,18 @@ public:
|
||||
void ValidateGpuDirtyOwnership(const RangeSet&, uint64_t, uint64_t, const char*) {}
|
||||
#endif
|
||||
|
||||
template <bool clear, typename Preflight, typename Func>
|
||||
void ForEachDownloadRange(uint64_t vaddr, uint64_t size, Preflight&& preflight, Func&& func) {
|
||||
static_assert(std::is_nothrow_invocable_v<Preflight&, uint64_t, uint64_t>);
|
||||
static_assert(std::is_nothrow_invocable_v<Func&, uint64_t, uint64_t>);
|
||||
CheckNotInUploadCallback();
|
||||
std::vector<RegionManager*> managers;
|
||||
Iterate<false>(vaddr, size, [&](RegionManager* manager, uint64_t, uint64_t) {
|
||||
managers.push_back(manager);
|
||||
});
|
||||
std::vector<std::unique_lock<TrackingSpinLock>> locks;
|
||||
locks.reserve(managers.size());
|
||||
for (auto* manager: managers) {
|
||||
locks.emplace_back(manager->lock);
|
||||
}
|
||||
Iterate<false>(vaddr, size, [&](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
||||
const auto address = manager->GetCpuAddr() + offset;
|
||||
manager->template ForEachModifiedRange<DirtySource::Gpu, false>(address, bytes,
|
||||
preflight);
|
||||
});
|
||||
Iterate<false>(vaddr, size, [&](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
||||
manager->template ForEachModifiedRange<DirtySource::Gpu, false>(
|
||||
manager->GetCpuAddr() + offset, bytes, func);
|
||||
});
|
||||
if constexpr (clear) {
|
||||
Iterate<false>(vaddr, size,
|
||||
[&](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
||||
const auto address = manager->GetCpuAddr() + offset;
|
||||
manager->template ForEachModifiedRange<DirtySource::Gpu, true>(
|
||||
address, bytes, [](uint64_t, uint64_t) noexcept {});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
template <bool clear, typename Func>
|
||||
void ForEachDownloadRange(uint64_t vaddr, uint64_t size, Func&& func) {
|
||||
ForEachDownloadRange<clear>(
|
||||
vaddr, size, [](uint64_t, uint64_t) noexcept {}, std::forward<Func>(func));
|
||||
static_assert(std::is_nothrow_invocable_v<Func&, uint64_t, uint64_t>);
|
||||
CheckNotInUploadCallback();
|
||||
Iterate<false>(vaddr, size, [&](RegionManager* manager, uint64_t offset, uint64_t bytes) {
|
||||
std::scoped_lock lock(manager->lock);
|
||||
const auto address = manager->GetCpuAddr() + offset;
|
||||
manager->template ForEachModifiedRange<DirtySource::Gpu, false>(address, bytes, func);
|
||||
if constexpr (clear) {
|
||||
manager->template ChangeState<DirtySource::Gpu, false>(address, bytes);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename RangeFunc, typename UploadFunc>
|
||||
@@ -171,7 +145,6 @@ private:
|
||||
}
|
||||
|
||||
static void ValidateRange(uint64_t vaddr, uint64_t size);
|
||||
void UntrackMemoryImpl(uint64_t vaddr, uint64_t size);
|
||||
RegionManager* GetOrCreateRegion(uint64_t index);
|
||||
|
||||
std::unique_ptr<std::atomic<RegionManager*>[]> m_regions;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "graphics/host_gpu/pageManager.h"
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/virtualMemory.h"
|
||||
#include "graphics/host_gpu/regionDefinitions.h"
|
||||
#include "kernel/memory.h"
|
||||
|
||||
@@ -20,10 +22,7 @@
|
||||
#include <windows.h>
|
||||
#undef min
|
||||
#undef max
|
||||
#elif defined(__APPLE__)
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <execinfo.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
@@ -35,38 +34,12 @@ constexpr uint64_t REGION_SIZE = TRACKER_REGION_SIZE;
|
||||
constexpr uint64_t ADDRESS_SIZE = TRACKER_ADDRESS_SIZE;
|
||||
constexpr uint64_t REGION_COUNT = ADDRESS_SIZE / REGION_SIZE;
|
||||
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
// The tracker reuses Win32 memory-protection tags as internal page-state values.
|
||||
// Mirror their canonical numeric values so the shared state-machine logic is identical.
|
||||
constexpr uint32_t PAGE_NOACCESS = 0x01;
|
||||
constexpr uint32_t PAGE_READONLY = 0x02;
|
||||
constexpr uint32_t PAGE_READWRITE = 0x04;
|
||||
#endif
|
||||
constexpr uint64_t REGION_PAGES = REGION_SIZE / PAGE_SIZE;
|
||||
|
||||
constexpr uint32_t NO_ACCESS_PROTECTION = PAGE_NOACCESS;
|
||||
constexpr uint32_t READ_ONLY_PROTECTION = PAGE_READONLY;
|
||||
constexpr uint32_t READ_WRITE_PROTECTION = PAGE_READWRITE;
|
||||
|
||||
[[noreturn]] void FailFast(const char* reason = nullptr) noexcept {
|
||||
std::fputs("PageManager fail-fast: ", stderr);
|
||||
std::fputs(reason != nullptr ? reason : "invalid page state", stderr);
|
||||
std::fputc('\n', stderr);
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
void* frames[16] {};
|
||||
const auto frame_count =
|
||||
CaptureStackBackTrace(0, static_cast<DWORD>(std::size(frames)), frames, nullptr);
|
||||
const auto image_base = reinterpret_cast<uintptr_t>(GetModuleHandleW(nullptr));
|
||||
for (uint16_t i = 0; i < frame_count; i++) {
|
||||
const auto address = reinterpret_cast<uintptr_t>(frames[i]);
|
||||
std::fprintf(stderr, " frame[%u]=0x%016" PRIxPTR " image_rva=0x%016" PRIxPTR "\n", i,
|
||||
address, address >= image_base ? address - image_base : 0);
|
||||
}
|
||||
#elif !defined(__APPLE__)
|
||||
void* frames[16] {};
|
||||
const int frame_count = ::backtrace(frames, static_cast<int>(std::size(frames)));
|
||||
::backtrace_symbols_fd(frames, frame_count, STDERR_FILENO);
|
||||
#endif
|
||||
std::fflush(stderr);
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
TerminateProcess(GetCurrentProcess(), static_cast<UINT>(EXCEPTION_NONCONTINUABLE_EXCEPTION));
|
||||
@@ -85,15 +58,6 @@ constexpr uint32_t READ_WRITE_PROTECTION = PAGE_READWRITE;
|
||||
std::_Exit(322);
|
||||
}
|
||||
|
||||
Common::VirtualMemory::Mode ToMemoryMode(uint32_t protection) {
|
||||
switch (protection) {
|
||||
case NO_ACCESS_PROTECTION: return Common::VirtualMemory::Mode::NoAccess;
|
||||
case READ_ONLY_PROTECTION: return Common::VirtualMemory::Mode::Read;
|
||||
case READ_WRITE_PROTECTION: return Common::VirtualMemory::Mode::ReadWrite;
|
||||
default: Fatal("unmappable protection 0x%08" PRIx32, protection);
|
||||
}
|
||||
}
|
||||
|
||||
class SpinGuard final {
|
||||
public:
|
||||
explicit SpinGuard(std::atomic_flag& lock): m_lock(lock) {
|
||||
@@ -109,20 +73,11 @@ private:
|
||||
};
|
||||
|
||||
void ValidateRange(uint64_t vaddr, uint64_t size) {
|
||||
if (vaddr == 0 || size == 0 || vaddr >= ADDRESS_SIZE || size > ADDRESS_SIZE - vaddr) {
|
||||
if (!GuestRange {vaddr, size}.Valid()) {
|
||||
Fatal("invalid range vaddr=0x%016" PRIx64 ", size=0x%016" PRIx64, vaddr, size);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t PageStart(uint64_t vaddr) {
|
||||
return vaddr & ~(PAGE_SIZE - 1);
|
||||
}
|
||||
|
||||
uint64_t PageEnd(uint64_t vaddr, uint64_t size) {
|
||||
ValidateRange(vaddr, size);
|
||||
return PageStart(vaddr + size - 1) + PAGE_SIZE;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
struct PageManager::Impl {
|
||||
@@ -130,14 +85,14 @@ struct PageManager::Impl {
|
||||
uint8_t write_watchers : 7 = 0;
|
||||
uint8_t access_watchers : 1 = 0;
|
||||
|
||||
[[nodiscard]] uint32_t Perms() const noexcept {
|
||||
[[nodiscard]] Common::VirtualMemory::Mode Perms() const noexcept {
|
||||
if (access_watchers != 0) {
|
||||
return NO_ACCESS_PROTECTION;
|
||||
return Common::VirtualMemory::Mode::NoAccess;
|
||||
}
|
||||
if (write_watchers != 0) {
|
||||
return READ_ONLY_PROTECTION;
|
||||
return Common::VirtualMemory::Mode::Read;
|
||||
}
|
||||
return READ_WRITE_PROTECTION;
|
||||
return Common::VirtualMemory::Mode::ReadWrite;
|
||||
}
|
||||
|
||||
template <int delta, bool is_read>
|
||||
@@ -201,9 +156,6 @@ struct PageManager::Impl {
|
||||
}
|
||||
#endif
|
||||
regions = std::make_unique<std::atomic<Region*>[]>(REGION_COUNT);
|
||||
for (uint64_t i = 0; i < REGION_COUNT; i++) {
|
||||
regions[i].store(nullptr, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
~Impl() {
|
||||
@@ -238,11 +190,10 @@ struct PageManager::Impl {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void Protect(uint64_t vaddr, uint64_t size, uint32_t protection) noexcept {
|
||||
if (!Libs::LibKernel::Memory::ProtectGuestHostMemory(vaddr, size,
|
||||
ToMemoryMode(protection))) {
|
||||
Fatal("address-space protection failed at 0x%016" PRIx64 ", new=0x%08" PRIx32, vaddr,
|
||||
protection);
|
||||
void Protect(uint64_t vaddr, uint64_t size, Common::VirtualMemory::Mode mode) noexcept {
|
||||
if (!Libs::LibKernel::Memory::ProtectGuestHostMemory(vaddr, size, mode)) {
|
||||
Fatal("address-space protection failed at 0x%016" PRIx64 ", mode=0x%08" PRIx32, vaddr,
|
||||
static_cast<uint32_t>(mode));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,11 +250,12 @@ struct PageManager::Impl {
|
||||
|
||||
template <bool track, bool is_read>
|
||||
void UpdatePageWatchers(uint64_t vaddr, uint64_t size) {
|
||||
const auto begin = PageStart(vaddr);
|
||||
const auto end = PageEnd(vaddr, size);
|
||||
ValidateRange(vaddr, size);
|
||||
const auto begin = Common::AlignDown(vaddr, PAGE_SIZE);
|
||||
const auto end = Common::AlignUp(vaddr + size, PAGE_SIZE);
|
||||
for (auto chunk_begin = begin; chunk_begin < end;) {
|
||||
const auto chunk_end = std::min(end, (chunk_begin / REGION_SIZE + 1) * REGION_SIZE);
|
||||
const auto region_base = chunk_begin / REGION_SIZE * REGION_SIZE;
|
||||
const auto chunk_end = std::min(end, Common::AlignUp(chunk_begin + 1, REGION_SIZE));
|
||||
const auto region_base = Common::AlignDown(chunk_begin, REGION_SIZE);
|
||||
auto* region = track ? GetOrCreateRegion(chunk_begin) : FindRegion(chunk_begin);
|
||||
if (region == nullptr) {
|
||||
Fatal("untracking unknown page 0x%016" PRIx64, chunk_begin);
|
||||
@@ -370,8 +322,4 @@ template void PageManager::UpdatePageWatchersForRegion<true, false>(uint64_t, Re
|
||||
template void PageManager::UpdatePageWatchersForRegion<false, true>(uint64_t, RegionBits&);
|
||||
template void PageManager::UpdatePageWatchersForRegion<false, false>(uint64_t, RegionBits&);
|
||||
|
||||
void PageManager::OnGpuMap(uint64_t, uint64_t) {}
|
||||
|
||||
void PageManager::OnGpuUnmap(uint64_t, uint64_t) {}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
@@ -24,8 +24,6 @@ public:
|
||||
void UpdatePageWatchers(uint64_t vaddr, uint64_t size);
|
||||
template <bool track, bool is_read = false>
|
||||
void UpdatePageWatchersForRegion(uint64_t base_addr, RegionBits& mask);
|
||||
void OnGpuMap(uint64_t vaddr, uint64_t size);
|
||||
void OnGpuUnmap(uint64_t vaddr, uint64_t size);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
|
||||
@@ -6,17 +6,11 @@
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
class RangeSet final {
|
||||
public:
|
||||
struct Range {
|
||||
uint64_t address = 0;
|
||||
uint64_t size = 0;
|
||||
};
|
||||
|
||||
void Add(uint64_t address, uint64_t size) {
|
||||
const auto end = End(address, size);
|
||||
auto it = m_ranges.lower_bound(address);
|
||||
@@ -53,10 +47,22 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<Range> Intersections(uint64_t address, uint64_t size) const {
|
||||
std::vector<Range> result;
|
||||
ForEachIntersection(address, size, [&result](Range range) { result.push_back(range); });
|
||||
return result;
|
||||
void Clear() { m_ranges.clear(); }
|
||||
|
||||
template <typename Func>
|
||||
void ForEach(Func&& func) const {
|
||||
for (const auto& [begin, end]: m_ranges) {
|
||||
func(begin, end);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool Intersects(uint64_t address, uint64_t size) const {
|
||||
const auto end = End(address, size);
|
||||
auto it = m_ranges.lower_bound(address);
|
||||
if (it != m_ranges.begin() && std::prev(it)->second > address) {
|
||||
return true;
|
||||
}
|
||||
return it != m_ranges.end() && it->first < end;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool Contains(uint64_t address, uint64_t size) const {
|
||||
@@ -70,7 +76,7 @@ public:
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
void ForEachIntersection(uint64_t address, uint64_t size, Func&& func) const {
|
||||
void ForEachInRange(uint64_t address, uint64_t size, Func&& func) const {
|
||||
const auto end = End(address, size);
|
||||
auto it = m_ranges.upper_bound(address);
|
||||
if (it != m_ranges.begin()) {
|
||||
@@ -80,7 +86,7 @@ public:
|
||||
const auto begin = std::max(address, it->first);
|
||||
const auto last = std::min(end, it->second);
|
||||
if (begin < last) {
|
||||
func(Range {begin, last - begin});
|
||||
func(begin, last);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,7 +95,7 @@ public:
|
||||
|
||||
private:
|
||||
static uint64_t End(uint64_t address, uint64_t size) {
|
||||
if (address == 0 || size == 0 || size > UINT64_MAX - address) {
|
||||
if (size == 0 || size > UINT64_MAX - address) {
|
||||
EXIT("invalid range-set address or size\n");
|
||||
}
|
||||
return address + size;
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "common/bitArray.h"
|
||||
#include "common/common.h"
|
||||
|
||||
#include <compare>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
constexpr uint64_t TRACKER_PAGE_SIZE = 4ull * 1024ull;
|
||||
@@ -11,6 +13,20 @@ constexpr uint64_t TRACKER_REGION_SIZE = 4ull * 1024ull * 1024ull;
|
||||
constexpr uint64_t TRACKER_ADDRESS_SIZE = 1ull << 40u;
|
||||
constexpr size_t TRACKER_REGION_PAGES = TRACKER_REGION_SIZE / TRACKER_PAGE_SIZE;
|
||||
|
||||
struct GuestRange {
|
||||
uint64_t address = 0;
|
||||
uint64_t size = 0;
|
||||
|
||||
[[nodiscard]] constexpr bool Empty() const noexcept { return address == 0 && size == 0; }
|
||||
[[nodiscard]] constexpr bool Valid() const noexcept {
|
||||
return address != 0 && size != 0 && address < TRACKER_ADDRESS_SIZE &&
|
||||
size <= TRACKER_ADDRESS_SIZE - address;
|
||||
}
|
||||
[[nodiscard]] constexpr bool ValidOrEmpty() const noexcept { return Empty() || Valid(); }
|
||||
[[nodiscard]] constexpr uint64_t End() const noexcept { return address + size; }
|
||||
auto operator<=>(const GuestRange&) const = default;
|
||||
};
|
||||
|
||||
enum class DirtySource { Cpu, Gpu };
|
||||
using RegionBits = Common::BitArray<TRACKER_REGION_PAGES>;
|
||||
static_assert(sizeof(RegionBits) == TRACKER_REGION_PAGES / 8);
|
||||
|
||||
@@ -111,56 +111,43 @@ public:
|
||||
bits.UnsetRange(start, end);
|
||||
}
|
||||
if constexpr (source == DirtySource::Cpu) {
|
||||
UpdateCpuProtection<!enable>();
|
||||
UpdateProtection<!enable, false>();
|
||||
} else {
|
||||
UpdateGpuProtection<enable>();
|
||||
UpdateProtection<enable, true>();
|
||||
}
|
||||
}
|
||||
|
||||
template <DirtySource source, bool clear, typename Func>
|
||||
void ForEachModifiedRange(uint64_t vaddr, uint64_t size, Func&& func) {
|
||||
const auto [start, end] = GetPageRange(vaddr, size);
|
||||
RegionBits mask(GetBits<source>(), start, end);
|
||||
auto& bits = GetBits<source>();
|
||||
RegionBits mask(bits, start, end);
|
||||
if constexpr (clear) {
|
||||
GetBits<source>().UnsetRange(start, end);
|
||||
bits.UnsetRange(start, end);
|
||||
if constexpr (source == DirtySource::Cpu) {
|
||||
UpdateProtection<true, false>();
|
||||
} else {
|
||||
UpdateProtection<false, true>();
|
||||
}
|
||||
}
|
||||
if constexpr (source == DirtySource::Cpu && clear) {
|
||||
UpdateCpuProtection<true>();
|
||||
ForEachRange(mask, std::forward<Func>(func));
|
||||
return;
|
||||
for (const auto [first, last]: mask) {
|
||||
func(m_cpu_addr + first * TRACKER_PAGE_SIZE, (last - first) * TRACKER_PAGE_SIZE);
|
||||
}
|
||||
if constexpr (source == DirtySource::Gpu && clear) {
|
||||
UpdateGpuProtection<false>();
|
||||
}
|
||||
ForEachRange(mask, std::forward<Func>(func));
|
||||
}
|
||||
|
||||
TrackingSpinLock lock;
|
||||
|
||||
private:
|
||||
template <bool track>
|
||||
void UpdateCpuProtection() {
|
||||
auto mask = m_cpu_dirty ^ m_writable;
|
||||
m_writable = m_cpu_dirty;
|
||||
template <bool track, bool is_read>
|
||||
void UpdateProtection() {
|
||||
const auto protection = is_read ? ~m_gpu_dirty : m_cpu_dirty;
|
||||
auto& previous = is_read ? m_readable : m_writable;
|
||||
auto mask = protection ^ previous;
|
||||
if (mask.None()) {
|
||||
return;
|
||||
}
|
||||
m_page_manager.UpdatePageWatchersForRegion<track>(m_cpu_addr, mask);
|
||||
}
|
||||
|
||||
template <bool track>
|
||||
void UpdateGpuProtection() {
|
||||
auto readable = ~m_gpu_dirty;
|
||||
auto mask = readable ^ m_readable;
|
||||
m_readable = readable;
|
||||
if (mask.None()) {
|
||||
return;
|
||||
}
|
||||
if constexpr (track) {
|
||||
m_page_manager.UpdatePageWatchersForRegion<true, true>(m_cpu_addr, mask);
|
||||
} else {
|
||||
m_page_manager.UpdatePageWatchersForRegion<false, true>(m_cpu_addr, mask);
|
||||
}
|
||||
previous = protection;
|
||||
m_page_manager.UpdatePageWatchersForRegion<track, is_read>(m_cpu_addr, mask);
|
||||
}
|
||||
|
||||
template <DirtySource source>
|
||||
@@ -191,13 +178,6 @@ private:
|
||||
static_cast<size_t>((offset + size + TRACKER_PAGE_SIZE - 1) / TRACKER_PAGE_SIZE)};
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
void ForEachRange(const RegionBits& bits, Func&& func) const {
|
||||
for (const auto [start, end]: bits) {
|
||||
func(m_cpu_addr + start * TRACKER_PAGE_SIZE, (end - start) * TRACKER_PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
PageManager& m_page_manager;
|
||||
uint64_t m_cpu_addr = 0;
|
||||
RegionBits m_cpu_dirty;
|
||||
|
||||
+434
-670
File diff suppressed because it is too large.
Load diff
+75
-59
@@ -3,12 +3,15 @@
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "common/common.h"
|
||||
#include "common/lruCache.h"
|
||||
#include "common/slotVector.h"
|
||||
#include "graphics/host_gpu/memoryTracker.h"
|
||||
#include "graphics/host_gpu/rangeSet.h"
|
||||
#include "graphics/host_gpu/renderer/cache/faultManager.h"
|
||||
#include "graphics/host_gpu/renderer/cache/multiLevelPageTable.h"
|
||||
#include "graphics/host_gpu/renderer/cache/streamBuffer.h"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -16,91 +19,104 @@
|
||||
namespace Libs::Graphics {
|
||||
|
||||
struct GraphicContext;
|
||||
class CommandBuffer;
|
||||
class CommandScheduler;
|
||||
class TextureCache;
|
||||
|
||||
struct BufferBinding {
|
||||
std::shared_ptr<void> owner;
|
||||
vk::Buffer buffer = nullptr;
|
||||
uint64_t offset = 0;
|
||||
};
|
||||
|
||||
struct ImageBufferSource {
|
||||
Buffer* buffer = nullptr;
|
||||
uint64_t offset = 0;
|
||||
};
|
||||
using BufferId = Common::SlotId;
|
||||
inline constexpr BufferId NULL_BUFFER_ID {0};
|
||||
|
||||
class BufferCache {
|
||||
public:
|
||||
static constexpr uint64_t CACHING_PAGE_SIZE = 16ull * 1024ull;
|
||||
static constexpr uint64_t GetBufferOffset(uint64_t vaddr) {
|
||||
return vaddr & (CACHING_PAGE_SIZE - 1);
|
||||
}
|
||||
static constexpr uint32_t CACHING_PAGEBITS = 14;
|
||||
static constexpr uint64_t CACHING_PAGESIZE = uint64_t {1} << CACHING_PAGEBITS;
|
||||
static constexpr uint64_t CACHING_NUMPAGES = uint64_t {1} << (40 - CACHING_PAGEBITS);
|
||||
static constexpr uint64_t BDA_PAGETABLE_SIZE =
|
||||
CACHING_NUMPAGES * sizeof(vk::DeviceAddress);
|
||||
|
||||
BufferCache(GraphicContext& graphics, CommandScheduler& scheduler, PageManager& page_manager,
|
||||
TextureCache& texture_cache);
|
||||
~BufferCache();
|
||||
KYTY_CLASS_NO_COPY(BufferCache);
|
||||
|
||||
void InvalidateMemory(uint64_t vaddr, uint64_t size);
|
||||
void ReadMemory(uint64_t vaddr, uint64_t size, bool is_write = false);
|
||||
void UnmapMemory(uint64_t vaddr, uint64_t size);
|
||||
[[nodiscard]] BufferBinding ObtainBuffer(CommandBuffer& command, uint64_t vaddr, uint64_t size,
|
||||
bool is_written = false, bool is_read = true,
|
||||
bool is_formatted = false);
|
||||
[[nodiscard]] StreamBuffer& GetUtilityBuffer(MemoryUsage usage) noexcept;
|
||||
[[nodiscard]] Buffer& GetGdsBuffer() noexcept { return m_gds_buffer; }
|
||||
[[nodiscard]] const Buffer& GetGdsBuffer() const noexcept { return m_gds_buffer; }
|
||||
[[nodiscard]] BufferBinding UploadTransient(const void* data, uint64_t size,
|
||||
uint64_t alignment);
|
||||
[[nodiscard]] std::shared_ptr<Buffer> ObtainNullBuffer();
|
||||
[[nodiscard]] ImageBufferSource ObtainBufferForImage(uint64_t vaddr, uint64_t size);
|
||||
void FillBuffer(uint64_t vaddr, uint64_t size, uint32_t value, bool is_gds = false);
|
||||
void CopyBuffer(uint64_t dst_vaddr, uint64_t src_vaddr, uint64_t size, bool dst_gds = false,
|
||||
bool src_gds = false);
|
||||
void InvalidateMemory(uint64_t vaddr, uint64_t size);
|
||||
void ReadMemory(uint64_t vaddr, uint64_t size, bool is_write = false);
|
||||
[[nodiscard]] Buffer& GetBuffer(BufferId id) { return m_slot_buffers[id]; }
|
||||
[[nodiscard]] BufferId FindBuffer(uint64_t vaddr, uint64_t size);
|
||||
[[nodiscard]] std::pair<Buffer*, uint64_t> ObtainBuffer(uint64_t vaddr, uint64_t size,
|
||||
bool is_written,
|
||||
bool is_texel_buffer = false,
|
||||
BufferId id = {});
|
||||
[[nodiscard]] StreamBuffer& GetUtilityBuffer(MemoryUsage usage) noexcept {
|
||||
switch (usage) {
|
||||
case MemoryUsage::Upload: return m_staging_buffer;
|
||||
case MemoryUsage::Stream: return m_stream_buffer;
|
||||
case MemoryUsage::Download: return m_download_buffer;
|
||||
case MemoryUsage::DeviceLocal: return m_device_buffer;
|
||||
}
|
||||
EXIT("BufferCache: invalid utility-buffer usage\n");
|
||||
}
|
||||
[[nodiscard]] const Buffer* GetGdsBuffer() const noexcept { return &m_gds_buffer; }
|
||||
[[nodiscard]] Buffer* GetBdaPageTableBuffer() noexcept { return &m_bda_pagetable_buffer; }
|
||||
[[nodiscard]] Buffer* GetFaultBuffer() noexcept { return m_fault_manager.GetFaultBuffer(); }
|
||||
[[nodiscard]] std::pair<Buffer*, uint64_t> ObtainBufferForImage(uint64_t vaddr, uint64_t size);
|
||||
void FillBuffer(uint64_t vaddr, uint64_t size, uint32_t value, bool is_gds);
|
||||
void CopyBuffer(uint64_t dst_vaddr, uint64_t src_vaddr, uint64_t size, bool dst_gds,
|
||||
bool src_gds);
|
||||
// Cache-index and exact dirty-range queries require GPU-thread serialization.
|
||||
[[nodiscard]] bool IsRegionRegistered(uint64_t vaddr, uint64_t size);
|
||||
[[nodiscard]] bool HasGpuDirtyBytes(uint64_t vaddr, uint64_t size);
|
||||
[[nodiscard]] bool IsRegionCpuModified(uint64_t vaddr, uint64_t size);
|
||||
[[nodiscard]] bool IsRegionGpuModified(uint64_t vaddr, uint64_t size);
|
||||
void ProcessFaultBuffer();
|
||||
void SynchronizeBuffersInRange(uint64_t vaddr, uint64_t size);
|
||||
void RunGarbageCollector();
|
||||
|
||||
private:
|
||||
friend struct BufferCacheTestAccess;
|
||||
|
||||
struct CacheRange {
|
||||
uint64_t address = 0;
|
||||
uint64_t size = 0;
|
||||
};
|
||||
struct CachedBuffer;
|
||||
struct DownloadCopy;
|
||||
struct DownloadRange;
|
||||
struct RetiredBuffer;
|
||||
static constexpr uint64_t DOWNLOAD_ALIGNMENT = 64;
|
||||
[[nodiscard]] static uint64_t AlignDown(uint64_t value) noexcept;
|
||||
[[nodiscard]] static uint64_t AlignUp(uint64_t value);
|
||||
[[nodiscard]] static constexpr uint64_t AlignDownload(uint64_t size) noexcept {
|
||||
return (size + DOWNLOAD_ALIGNMENT - 1) & ~(DOWNLOAD_ALIGNMENT - 1);
|
||||
bool IsBufferInvalid(BufferId id) const {
|
||||
const auto* buffer = m_slot_buffers.try_get(id);
|
||||
return buffer == nullptr || buffer->is_deleted;
|
||||
}
|
||||
[[nodiscard]] static std::pair<uint64_t, uint64_t> DownloadEnvelope(const DownloadCopy& copy);
|
||||
[[nodiscard]] static bool ResolveOverlap(CacheRange& merged, CacheRange candidate) noexcept;
|
||||
void Upload(CommandBuffer& command, Buffer& destination, uint64_t destination_offset,
|
||||
const void* source, uint64_t size);
|
||||
[[nodiscard]] CachedBuffer& GetOrCreateBuffer(CommandBuffer& command, uint64_t vaddr,
|
||||
uint64_t size);
|
||||
|
||||
using BufferMap = std::map<uint64_t, BufferId>;
|
||||
struct OverlapResult {
|
||||
BufferMap::iterator first;
|
||||
BufferMap::iterator last;
|
||||
uint64_t begin;
|
||||
uint64_t end;
|
||||
bool has_stream_leap;
|
||||
};
|
||||
|
||||
using PageTable = MultiLevelPageTable<BufferId, CACHING_PAGEBITS, 40, 16>;
|
||||
static_assert(CACHING_PAGESIZE == (uint64_t {1} << PageTable::kPageBits));
|
||||
void WriteDataBuffer(Buffer& buffer, uint64_t address, const void* source, uint64_t size);
|
||||
void TouchBuffer(const Buffer& buffer);
|
||||
[[nodiscard]] OverlapResult ResolveOverlaps(uint64_t vaddr, uint64_t size);
|
||||
void JoinOverlap(BufferId new_id, BufferId overlap_id, bool accumulate_stream_score);
|
||||
[[nodiscard]] BufferId CreateBuffer(uint64_t vaddr, uint64_t size);
|
||||
void Register(BufferId id);
|
||||
void Unregister(BufferId id);
|
||||
template <bool insert>
|
||||
void ChangeRegister(BufferId id);
|
||||
void DeleteBuffer(BufferId id);
|
||||
[[nodiscard]] bool SynchronizeBuffer(Buffer& buffer, uint64_t vaddr, uint64_t size,
|
||||
bool is_written, bool is_texel_buffer);
|
||||
[[nodiscard]] vk::Buffer UploadCopies(Buffer& buffer, std::span<vk::BufferCopy> copies,
|
||||
uint64_t total_size);
|
||||
[[nodiscard]] bool SynchronizeBufferFromImage(Buffer& buffer, uint64_t vaddr, uint64_t size);
|
||||
[[nodiscard]] std::vector<DownloadRange> RecordDownloads(std::span<const DownloadCopy> copies);
|
||||
void PublishDownloads(std::span<const DownloadRange> downloads);
|
||||
void QueueGarbageDownload(std::span<const DownloadCopy> copies, RetiredBuffer retire);
|
||||
void WriteHostMemory(uint64_t vaddr, std::span<const uint8_t> data);
|
||||
void ReadMemoryOnGpu(uint64_t vaddr, uint64_t size, bool is_write);
|
||||
// Queues backing publication; callers wait before clearing dirty pages or reusing their data.
|
||||
[[nodiscard]] bool DownloadBufferMemory(Buffer& buffer, uint64_t vaddr, uint64_t size);
|
||||
|
||||
GraphicContext& m_graphics;
|
||||
CommandScheduler& m_scheduler;
|
||||
FaultManager m_fault_manager;
|
||||
Buffer m_gds_buffer;
|
||||
std::shared_ptr<Buffer> m_null_buffer;
|
||||
std::map<uint64_t, std::unique_ptr<CachedBuffer>> m_buffers;
|
||||
Buffer m_bda_pagetable_buffer;
|
||||
Common::SlotVector<Buffer> m_slot_buffers;
|
||||
Common::LeastRecentlyUsedCache<BufferId, uint64_t> m_lru_cache;
|
||||
BufferMap m_buffers;
|
||||
PageTable m_page_table;
|
||||
RangeSet m_gpu_modified_ranges;
|
||||
MemoryTracker m_memory_tracker;
|
||||
StreamBuffer m_staging_buffer;
|
||||
@@ -108,7 +124,7 @@ private:
|
||||
StreamBuffer m_download_buffer;
|
||||
StreamBuffer m_device_buffer;
|
||||
TextureCache& m_texture_cache;
|
||||
uint64_t m_total_used_memory = 0;
|
||||
uint64_t m_total_used_memory = 0;
|
||||
uint64_t m_trigger_gc_memory = 1ull * 1024 * 1024 * 1024;
|
||||
uint64_t m_critical_gc_memory = 2ull * 1024 * 1024 * 1024;
|
||||
uint64_t m_gc_tick = 0;
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
#include "graphics/host_gpu/renderer/cache/faultManager.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "gpu_tiler_shaders/fault_buffer_process_spv.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "graphics/host_gpu/renderer/cache/bufferCache.h"
|
||||
#include "graphics/host_gpu/renderer/commandScheduler.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
|
||||
#include <bit>
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr size_t MaxPageFaults = 1024;
|
||||
constexpr size_t PageFaultAreaSize = MaxPageFaults * sizeof(uint64_t);
|
||||
|
||||
} // namespace
|
||||
|
||||
FaultManager::FaultManager(GraphicContext& graphics, CommandScheduler& scheduler,
|
||||
BufferCache& buffer_cache)
|
||||
: m_graphics(graphics), m_scheduler(scheduler), m_buffer_cache(buffer_cache),
|
||||
m_fault_buffer(graphics, scheduler, MemoryUsage::DeviceLocal, 0, AllFlags,
|
||||
BufferCache::CACHING_NUMPAGES / 8),
|
||||
m_download_buffer(graphics, scheduler, MemoryUsage::Download, 0, AllFlags,
|
||||
MaxPendingFaults * PageFaultAreaSize) {
|
||||
SetVulkanObjectNameF(m_graphics.device, m_fault_buffer.Handle(), "Fault Buffer");
|
||||
|
||||
const vk::DescriptorSetLayoutBinding bindings[] {
|
||||
{0, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eCompute, nullptr},
|
||||
{1, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eCompute, nullptr},
|
||||
};
|
||||
vk::DescriptorSetLayoutCreateInfo layout_info {};
|
||||
layout_info.flags = vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR;
|
||||
layout_info.bindingCount = std::size(bindings);
|
||||
layout_info.pBindings = bindings;
|
||||
RequireVulkanSuccess(
|
||||
m_graphics.device.createDescriptorSetLayout(&layout_info, nullptr,
|
||||
&m_fault_process_desc_layout),
|
||||
"create fault-buffer descriptor layout");
|
||||
|
||||
const auto module = CompileSPV(FAULT_BUFFER_PROCESS_SPV, m_graphics.device);
|
||||
|
||||
vk::PipelineLayoutCreateInfo pipeline_layout_info {};
|
||||
pipeline_layout_info.setLayoutCount = 1;
|
||||
pipeline_layout_info.pSetLayouts = &m_fault_process_desc_layout;
|
||||
RequireVulkanSuccess(
|
||||
m_graphics.device.createPipelineLayout(&pipeline_layout_info, nullptr,
|
||||
&m_fault_process_pipeline_layout),
|
||||
"create fault-buffer pipeline layout");
|
||||
|
||||
vk::PipelineShaderStageCreateInfo stage {};
|
||||
stage.stage = vk::ShaderStageFlagBits::eCompute;
|
||||
stage.module = module;
|
||||
stage.pName = "main";
|
||||
vk::ComputePipelineCreateInfo pipeline_info {};
|
||||
pipeline_info.stage = stage;
|
||||
pipeline_info.layout = m_fault_process_pipeline_layout;
|
||||
const auto result = m_graphics.device.createComputePipelines(
|
||||
nullptr, 1, &pipeline_info, nullptr, &m_fault_process_pipeline);
|
||||
m_graphics.device.destroyShaderModule(module, nullptr);
|
||||
RequireVulkanSuccess(result, "create fault-buffer pipeline");
|
||||
SetVulkanObjectNameF(m_graphics.device, m_fault_process_pipeline, "Fault Buffer Parser");
|
||||
}
|
||||
|
||||
FaultManager::~FaultManager() {
|
||||
m_graphics.device.destroyPipeline(m_fault_process_pipeline, nullptr);
|
||||
m_graphics.device.destroyPipelineLayout(m_fault_process_pipeline_layout, nullptr);
|
||||
m_graphics.device.destroyDescriptorSetLayout(m_fault_process_desc_layout, nullptr);
|
||||
}
|
||||
|
||||
void FaultManager::ProcessFaultBuffer() {
|
||||
if (const auto wait_tick = m_fault_areas[m_current_area]; wait_tick != 0) {
|
||||
m_scheduler.Wait(wait_tick);
|
||||
m_scheduler.PopPendingOperations();
|
||||
}
|
||||
|
||||
const auto offset = m_current_area * PageFaultAreaSize;
|
||||
auto* mapped = m_download_buffer.Mapped().data() + offset;
|
||||
std::memset(mapped, 0, PageFaultAreaSize);
|
||||
m_download_buffer.Flush(offset, PageFaultAreaSize);
|
||||
|
||||
vk::BufferMemoryBarrier2 pre_barrier {};
|
||||
pre_barrier.srcStageMask = vk::PipelineStageFlagBits2::eAllCommands;
|
||||
pre_barrier.srcAccessMask = vk::AccessFlagBits2::eShaderWrite;
|
||||
pre_barrier.dstStageMask = vk::PipelineStageFlagBits2::eComputeShader;
|
||||
pre_barrier.dstAccessMask = vk::AccessFlagBits2::eShaderRead;
|
||||
pre_barrier.buffer = m_fault_buffer.Handle();
|
||||
pre_barrier.offset = 0;
|
||||
pre_barrier.size = m_fault_buffer.Size();
|
||||
auto post_barrier = pre_barrier;
|
||||
post_barrier.srcStageMask = vk::PipelineStageFlagBits2::eComputeShader;
|
||||
post_barrier.srcAccessMask = vk::AccessFlagBits2::eShaderWrite;
|
||||
post_barrier.dstStageMask = vk::PipelineStageFlagBits2::eAllCommands;
|
||||
post_barrier.dstAccessMask = vk::AccessFlagBits2::eShaderWrite;
|
||||
|
||||
const vk::DescriptorBufferInfo infos[] {
|
||||
{m_fault_buffer.Handle(), 0, m_fault_buffer.Size()},
|
||||
{m_download_buffer.Handle(), offset, PageFaultAreaSize},
|
||||
};
|
||||
std::array<vk::WriteDescriptorSet, 2> writes {};
|
||||
for (uint32_t index = 0; index < writes.size(); ++index) {
|
||||
writes[index].dstBinding = index;
|
||||
writes[index].descriptorCount = 1;
|
||||
writes[index].descriptorType = vk::DescriptorType::eStorageBuffer;
|
||||
writes[index].pBufferInfo = &infos[index];
|
||||
}
|
||||
|
||||
m_scheduler.EndRendering();
|
||||
auto command = m_scheduler.Current().Handle();
|
||||
vk::DependencyInfo dependency {};
|
||||
dependency.dependencyFlags = vk::DependencyFlagBits::eByRegion;
|
||||
dependency.bufferMemoryBarrierCount = 1;
|
||||
dependency.pBufferMemoryBarriers = &pre_barrier;
|
||||
command.pipelineBarrier2(dependency);
|
||||
command.bindPipeline(vk::PipelineBindPoint::eCompute, m_fault_process_pipeline);
|
||||
command.pushDescriptorSetKHR(vk::PipelineBindPoint::eCompute,
|
||||
m_fault_process_pipeline_layout, 0, writes);
|
||||
const auto num_threads = BufferCache::CACHING_NUMPAGES / 32;
|
||||
const auto num_workgroups = (num_threads + 63) / 64;
|
||||
command.dispatch(static_cast<uint32_t>(num_workgroups), 1, 1);
|
||||
dependency.pBufferMemoryBarriers = &post_barrier;
|
||||
command.pipelineBarrier2(dependency);
|
||||
|
||||
const auto area = m_current_area;
|
||||
m_scheduler.DeferOperation([this, mapped, offset, area] {
|
||||
m_download_buffer.Invalidate(offset, PageFaultAreaSize);
|
||||
RangeSet fault_ranges;
|
||||
const auto* faults = std::bit_cast<const uint64_t*>(mapped);
|
||||
const auto count = static_cast<uint32_t>(faults[0]);
|
||||
for (uint32_t index = 1; index <= count; ++index) {
|
||||
fault_ranges.Add(faults[index], BufferCache::CACHING_PAGESIZE);
|
||||
LOGF("Accessed non-GPU cached memory at 0x%016" PRIx64 "\n", faults[index]);
|
||||
}
|
||||
fault_ranges.ForEach([this](uint64_t start, uint64_t end) {
|
||||
EXIT_IF(end - start > std::numeric_limits<uint32_t>::max());
|
||||
(void)m_buffer_cache.FindBuffer(start, end - start);
|
||||
});
|
||||
m_fault_areas[area] = 0;
|
||||
});
|
||||
|
||||
m_fault_areas[m_current_area++] = m_scheduler.CurrentTick();
|
||||
m_current_area %= MaxPendingFaults;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifndef EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_CACHE_FAULTMANAGER_H_
|
||||
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_CACHE_FAULTMANAGER_H_
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "graphics/host_gpu/renderer/cache/streamBuffer.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
class BufferCache;
|
||||
|
||||
class FaultManager {
|
||||
static constexpr size_t MaxPendingFaults = 8;
|
||||
|
||||
public:
|
||||
FaultManager(GraphicContext& graphics, CommandScheduler& scheduler, BufferCache& buffer_cache);
|
||||
~FaultManager();
|
||||
KYTY_CLASS_NO_COPY(FaultManager);
|
||||
|
||||
[[nodiscard]] Buffer* GetFaultBuffer() noexcept { return &m_fault_buffer; }
|
||||
void ProcessFaultBuffer();
|
||||
|
||||
private:
|
||||
GraphicContext& m_graphics;
|
||||
CommandScheduler& m_scheduler;
|
||||
BufferCache& m_buffer_cache;
|
||||
Buffer m_fault_buffer;
|
||||
Buffer m_download_buffer;
|
||||
std::array<uint64_t, MaxPendingFaults> m_fault_areas {};
|
||||
uint32_t m_current_area = 0;
|
||||
vk::DescriptorSetLayout m_fault_process_desc_layout = nullptr;
|
||||
vk::Pipeline m_fault_process_pipeline = nullptr;
|
||||
vk::PipelineLayout m_fault_process_pipeline_layout = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
#endif // EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_CACHE_FAULTMANAGER_H_
|
||||
@@ -1,85 +0,0 @@
|
||||
#include "graphics/host_gpu/renderer/cache/gpuResourceManager.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "graphics/guest_gpu/graphicsRun.h"
|
||||
#include "graphics/host_gpu/renderer/commandScheduler.h"
|
||||
namespace Libs::Graphics {
|
||||
|
||||
GpuResourceManager::GpuResourceManager(GraphicContext& graphics, CommandScheduler& scheduler)
|
||||
: m_scheduler(scheduler), m_buffer_cache(graphics, scheduler, m_page_manager, m_texture_cache),
|
||||
m_texture_cache(graphics, scheduler, m_page_manager, m_buffer_cache) {}
|
||||
|
||||
GpuResourceManager::~GpuResourceManager() = default;
|
||||
|
||||
bool GpuResourceManager::HandleFault(PageFaultAccess access, uint64_t fault_vaddr) noexcept {
|
||||
constexpr uint64_t fault_size = 8;
|
||||
if (!IsMapped(fault_vaddr, fault_size)) {
|
||||
return false;
|
||||
}
|
||||
if (access == PageFaultAccess::Write) {
|
||||
m_buffer_cache.InvalidateMemory(fault_vaddr, fault_size);
|
||||
m_texture_cache.InvalidateMemory(fault_vaddr, fault_size);
|
||||
} else {
|
||||
m_buffer_cache.ReadMemory(fault_vaddr, fault_size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GpuResourceManager::InvalidateMemory(uint64_t vaddr, uint64_t size) {
|
||||
if (!IsMapped(vaddr, size)) {
|
||||
return false;
|
||||
}
|
||||
m_buffer_cache.InvalidateMemory(vaddr, size);
|
||||
m_texture_cache.InvalidateMemory(vaddr, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GpuResourceManager::IsMapped(uint64_t vaddr, uint64_t size) const noexcept {
|
||||
if (vaddr == 0 || size == 0 || vaddr >= TRACKER_ADDRESS_SIZE ||
|
||||
size > TRACKER_ADDRESS_SIZE - vaddr) {
|
||||
return false;
|
||||
}
|
||||
std::shared_lock lock(m_mapped_ranges_mutex);
|
||||
return m_mapped_ranges.Contains(vaddr, size);
|
||||
}
|
||||
|
||||
void GpuResourceManager::MapMemory(uint64_t vaddr, uint64_t size) {
|
||||
{
|
||||
std::lock_guard lock(m_mapped_ranges_mutex);
|
||||
m_mapped_ranges.Add(vaddr, size);
|
||||
}
|
||||
m_page_manager.OnGpuMap(vaddr, size);
|
||||
}
|
||||
|
||||
void GpuResourceManager::UnmapMemory(uint64_t vaddr, uint64_t size) {
|
||||
if (CommandScheduler::InDeferredOperation()) {
|
||||
EXIT("unsupported memory unmap from an asynchronous GPU completion, "
|
||||
"addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
|
||||
vaddr, size);
|
||||
}
|
||||
const auto unmap = [this, vaddr, size] {
|
||||
if (m_scheduler.Active()) {
|
||||
const auto tick = m_scheduler.CurrentTick();
|
||||
m_scheduler.FinishCurrent();
|
||||
m_scheduler.WaitPriorityOperations(tick);
|
||||
}
|
||||
m_buffer_cache.UnmapMemory(vaddr, size);
|
||||
m_texture_cache.UnmapMemory(vaddr, size);
|
||||
m_page_manager.OnGpuUnmap(vaddr, size);
|
||||
std::lock_guard lock(m_mapped_ranges_mutex);
|
||||
m_mapped_ranges.Subtract(vaddr, size);
|
||||
};
|
||||
if (m_gpu == nullptr) {
|
||||
unmap();
|
||||
return;
|
||||
}
|
||||
m_gpu->SendCommandSync(unmap);
|
||||
}
|
||||
|
||||
void GpuResourceManager::RunGarbageCollector() {
|
||||
m_texture_cache.ProcessDownloadImages();
|
||||
m_texture_cache.RunGarbageCollector();
|
||||
m_buffer_cache.RunGarbageCollector();
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
@@ -1,47 +0,0 @@
|
||||
#ifndef EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_GPURESOURCEMANAGER_H_
|
||||
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_GPURESOURCEMANAGER_H_
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "common/common.h"
|
||||
#include "graphics/host_gpu/pageManager.h"
|
||||
#include "graphics/host_gpu/renderer/cache/bufferCache.h"
|
||||
#include "graphics/host_gpu/renderer/cache/textureCache.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <shared_mutex>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
class CommandScheduler;
|
||||
class Gpu;
|
||||
|
||||
class GpuResourceManager {
|
||||
public:
|
||||
GpuResourceManager(GraphicContext& graphics, CommandScheduler& scheduler);
|
||||
~GpuResourceManager();
|
||||
KYTY_CLASS_NO_COPY(GpuResourceManager);
|
||||
|
||||
[[nodiscard]] BufferCache& GetBufferCache() { return m_buffer_cache; }
|
||||
[[nodiscard]] TextureCache& GetTextureCache() { return m_texture_cache; }
|
||||
void SetGpu(Gpu* gpu) noexcept { m_gpu = gpu; }
|
||||
|
||||
[[nodiscard]] bool HandleFault(PageFaultAccess access, uint64_t fault_vaddr) noexcept;
|
||||
[[nodiscard]] bool InvalidateMemory(uint64_t vaddr, uint64_t size);
|
||||
[[nodiscard]] bool IsMapped(uint64_t vaddr, uint64_t size) const noexcept;
|
||||
void MapMemory(uint64_t vaddr, uint64_t size);
|
||||
void UnmapMemory(uint64_t vaddr, uint64_t size);
|
||||
void RunGarbageCollector();
|
||||
|
||||
private:
|
||||
PageManager m_page_manager;
|
||||
CommandScheduler& m_scheduler;
|
||||
BufferCache m_buffer_cache;
|
||||
TextureCache m_texture_cache;
|
||||
mutable std::shared_mutex m_mapped_ranges_mutex;
|
||||
RangeSet m_mapped_ranges;
|
||||
Gpu* m_gpu = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
#endif // EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_GPURESOURCEMANAGER_H_
|
||||
@@ -256,17 +256,6 @@ private:
|
||||
size_type m_inline_size = 0;
|
||||
};
|
||||
|
||||
// Removes only the requested owner. Other owners in the same page entry remain intact.
|
||||
template <typename Container, typename Value>
|
||||
[[nodiscard]] bool EraseExact(Container& owners, const Value& owner) {
|
||||
const auto it = std::find(owners.begin(), owners.end(), owner);
|
||||
if (it == owners.end()) {
|
||||
return false;
|
||||
}
|
||||
owners.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
#endif // EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_MULTILEVELPAGETABLE_H_
|
||||
@@ -116,9 +116,6 @@ vk::Sampler SamplerCache::GetSampler(const ShaderSamplerResource& r) {
|
||||
default: EXIT("unknown border color: %d", static_cast<int>(r.BorderColorType()));
|
||||
}
|
||||
|
||||
sampler_info.sType = vk::StructureType::eSamplerCreateInfo;
|
||||
sampler_info.pNext = nullptr;
|
||||
sampler_info.flags = {};
|
||||
sampler_info.magFilter = to_vk_filter(mag_filter);
|
||||
sampler_info.minFilter = to_vk_filter(min_filter);
|
||||
sampler_info.mipmapMode =
|
||||
|
||||
+47
-70
@@ -1,13 +1,12 @@
|
||||
#include "graphics/host_gpu/renderer/cache/streamBuffer.h"
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/profiler.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "graphics/host_gpu/renderer/commandScheduler.h"
|
||||
#include "graphics/host_gpu/vma.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <vk_mem_alloc.h>
|
||||
|
||||
@@ -46,16 +45,11 @@ constexpr size_t WATCHES_RESERVE_CHUNK = 0x1000;
|
||||
result = value;
|
||||
return true;
|
||||
}
|
||||
const auto remainder = value % alignment;
|
||||
if (remainder == 0) {
|
||||
result = value;
|
||||
return true;
|
||||
}
|
||||
const auto increment = alignment - remainder;
|
||||
if (value > std::numeric_limits<uint64_t>::max() - increment) {
|
||||
const auto aligned = Common::AlignUp(value, alignment);
|
||||
if (aligned < value) {
|
||||
return false;
|
||||
}
|
||||
result = value + increment;
|
||||
result = aligned;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -64,17 +58,20 @@ constexpr size_t WATCHES_RESERVE_CHUNK = 0x1000;
|
||||
Buffer::Buffer(GraphicContext& graphics, CommandScheduler& scheduler, MemoryUsage usage,
|
||||
uint64_t cpu_address, vk::BufferUsageFlags flags, uint64_t size)
|
||||
: m_graphics(&graphics), m_scheduler(&scheduler), m_usage(usage), m_cpu_address(cpu_address),
|
||||
m_size(size), m_buffer(std::make_unique<VulkanBuffer>()) {
|
||||
m_size(size) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
EXIT_IF(graphics.allocator == nullptr || size == 0);
|
||||
|
||||
vk::BufferCreateInfo buffer_info {};
|
||||
buffer_info.size = size;
|
||||
buffer_info.usage = flags;
|
||||
buffer_info.sharingMode = vk::SharingMode::eExclusive;
|
||||
|
||||
const bool with_bda = bool(flags & vk::BufferUsageFlagBits::eShaderDeviceAddress);
|
||||
const VmaAllocationCreateFlags bda_flag =
|
||||
with_bda ? VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT : 0;
|
||||
VmaAllocationCreateInfo allocation_info {};
|
||||
allocation_info.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | AllocationFlags(usage);
|
||||
allocation_info.flags =
|
||||
VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | bda_flag | AllocationFlags(usage);
|
||||
allocation_info.usage = AllocationUsage(usage);
|
||||
allocation_info.preferredFlags = usage == MemoryUsage::DeviceLocal
|
||||
? VkMemoryPropertyFlags {}
|
||||
@@ -84,72 +81,70 @@ Buffer::Buffer(GraphicContext& graphics, CommandScheduler& scheduler, MemoryUsag
|
||||
VkBuffer native_buffer = VK_NULL_HANDLE;
|
||||
const auto result = static_cast<vk::Result>(vmaCreateBuffer(
|
||||
graphics.allocator, static_cast<const VkBufferCreateInfo*>(buffer_info), &allocation_info,
|
||||
&native_buffer, &m_buffer->memory.allocation, &allocation_result));
|
||||
&native_buffer, &m_allocation, &allocation_result));
|
||||
if (result != vk::Result::eSuccess) {
|
||||
graphics.LogMemoryBudget();
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
||||
|
||||
m_buffer->buffer = native_buffer;
|
||||
m_buffer->usage = flags;
|
||||
m_buffer->buffer_size = size;
|
||||
m_buffer->memory.allocation_info = allocation_result;
|
||||
m_buffer->memory.memory = allocation_result.deviceMemory;
|
||||
m_buffer->memory.offset = allocation_result.offset;
|
||||
m_buffer->memory.type = allocation_result.memoryType;
|
||||
m_buffer->memory.unique_id = VulkanNextMemoryUniqueId();
|
||||
graphics.device.getBufferMemoryRequirements(m_buffer->buffer, &m_buffer->memory.requirements);
|
||||
m_buffer = native_buffer;
|
||||
if (with_bda) {
|
||||
vk::BufferDeviceAddressInfo address_info {};
|
||||
address_info.buffer = m_buffer;
|
||||
m_device_address = graphics.device.getBufferAddress(address_info);
|
||||
EXIT_IF(m_device_address == 0);
|
||||
}
|
||||
|
||||
VkMemoryPropertyFlags properties = 0;
|
||||
vmaGetAllocationMemoryProperties(graphics.allocator, m_buffer->memory.allocation, &properties);
|
||||
m_buffer->memory.property = vk::MemoryPropertyFlags(properties);
|
||||
m_is_coherent = (properties & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
|
||||
vmaGetAllocationMemoryProperties(graphics.allocator, m_allocation, &properties);
|
||||
m_coherent = (properties & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
|
||||
if (allocation_result.pMappedData != nullptr) {
|
||||
m_mapped = {static_cast<uint8_t*>(allocation_result.pMappedData),
|
||||
static_cast<size_t>(size)};
|
||||
}
|
||||
VulkanTrackAllocation(m_buffer->memory);
|
||||
}
|
||||
|
||||
Buffer::~Buffer() {
|
||||
if (m_buffer->buffer != nullptr) {
|
||||
VulkanUntrackAllocation(m_buffer->memory);
|
||||
vmaDestroyBuffer(m_graphics->allocator, m_buffer->buffer, m_buffer->memory.allocation);
|
||||
if (m_buffer != nullptr) {
|
||||
vmaDestroyBuffer(m_graphics->allocator, m_buffer, m_allocation);
|
||||
}
|
||||
}
|
||||
|
||||
vk::Buffer Buffer::Handle() const noexcept {
|
||||
return m_buffer->buffer;
|
||||
vk::DeviceAddress Buffer::BufferDeviceAddress() const noexcept {
|
||||
EXIT_IF(m_device_address == 0);
|
||||
return m_device_address;
|
||||
}
|
||||
|
||||
bool Buffer::IsInBounds(uint64_t address, uint64_t size) const noexcept {
|
||||
return address >= m_cpu_address && size <= m_size && address - m_cpu_address <= m_size - size;
|
||||
}
|
||||
|
||||
void Buffer::Write(uint64_t offset, const void* source, uint64_t size) {
|
||||
EXIT_IF(source == nullptr || m_mapped.empty() || offset > m_size || size > m_size - offset);
|
||||
std::memcpy(m_mapped.data() + offset, source, static_cast<size_t>(size));
|
||||
Flush(offset, size);
|
||||
return address >= m_cpu_address && size <= Size() && address - m_cpu_address <= Size() - size;
|
||||
}
|
||||
|
||||
void Buffer::Flush(uint64_t offset, uint64_t size) {
|
||||
EXIT_IF(m_mapped.empty() || offset > m_size || size > m_size - offset);
|
||||
if (!m_is_coherent && size != 0) {
|
||||
EXIT_IF(m_mapped.empty() || offset > Size() || size > Size() - offset);
|
||||
if (!IsCoherent() && size != 0) {
|
||||
const auto result =
|
||||
vmaFlushAllocation(m_graphics->allocator, m_buffer->memory.allocation, offset, size);
|
||||
vmaFlushAllocation(m_graphics->allocator, m_allocation, offset, size);
|
||||
EXIT_NOT_IMPLEMENTED(static_cast<vk::Result>(result) != vk::Result::eSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
void Buffer::Invalidate(uint64_t offset, uint64_t size) {
|
||||
EXIT_IF(m_usage != MemoryUsage::Download || offset > Size() || size > Size() - offset);
|
||||
if (!IsCoherent() && size != 0) {
|
||||
const auto result =
|
||||
vmaInvalidateAllocation(m_graphics->allocator, m_allocation, offset, size);
|
||||
EXIT_NOT_IMPLEMENTED(static_cast<vk::Result>(result) != vk::Result::eSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
vk::BufferMemoryBarrier Buffer::Barrier(uint64_t offset, uint64_t size, vk::AccessFlags source,
|
||||
vk::AccessFlags destination) const {
|
||||
if (Handle() == nullptr || size == 0 || offset > m_size || size > m_size - offset) {
|
||||
if (Handle() == nullptr || size == 0 || offset > Size() || size > Size() - offset) {
|
||||
EXIT("Buffer: invalid DMA barrier, handle=%p offset=0x%016" PRIx64 " size=0x%016" PRIx64
|
||||
" capacity=0x%016" PRIx64 "\n",
|
||||
static_cast<const void*>(Handle()), offset, size, m_size);
|
||||
static_cast<const void*>(Handle()), offset, size, Size());
|
||||
}
|
||||
vk::BufferMemoryBarrier barrier {};
|
||||
barrier.sType = vk::StructureType::eBufferMemoryBarrier;
|
||||
barrier.srcAccessMask = source;
|
||||
barrier.dstAccessMask = destination;
|
||||
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
@@ -164,8 +159,8 @@ void Buffer::CopyFrom(CommandBuffer& command, const Buffer& source, uint64_t sou
|
||||
uint64_t destination_offset, uint64_t size, vk::AccessFlags source_before,
|
||||
vk::AccessFlags destination_before, vk::AccessFlags source_after,
|
||||
vk::AccessFlags destination_after) {
|
||||
if (size == 0 || source_offset > source.m_size || size > source.m_size - source_offset ||
|
||||
destination_offset > m_size || size > m_size - destination_offset) {
|
||||
if (size == 0 || source_offset > source.Size() || size > source.Size() - source_offset ||
|
||||
destination_offset > Size() || size > Size() - destination_offset) {
|
||||
EXIT("Buffer: invalid copy range\n");
|
||||
}
|
||||
if (source.Handle() == Handle() && source_offset < destination_offset + size &&
|
||||
@@ -222,10 +217,8 @@ void Buffer::Fill(uint64_t offset, uint64_t size, uint32_t value) {
|
||||
|
||||
StreamBuffer::StreamBuffer(GraphicContext& graphics, CommandScheduler& scheduler, MemoryUsage usage,
|
||||
uint64_t size)
|
||||
: Buffer(graphics, scheduler, usage, 0, AllFlags, size) {
|
||||
ReserveWatches(m_current_watches, WATCHES_INITIAL_RESERVE);
|
||||
ReserveWatches(m_previous_watches, WATCHES_INITIAL_RESERVE);
|
||||
}
|
||||
: Buffer(graphics, scheduler, usage, 0, AllFlags, size),
|
||||
m_current_watches(WATCHES_INITIAL_RESERVE), m_previous_watches(WATCHES_INITIAL_RESERVE) {}
|
||||
|
||||
bool StreamBuffer::NormalizeReservation(bool coherent, uint64_t atom, uint64_t& size,
|
||||
uint64_t& alignment) {
|
||||
@@ -290,10 +283,8 @@ std::pair<uint8_t*, uint64_t> StreamBuffer::Map(uint64_t size, uint64_t alignmen
|
||||
}
|
||||
|
||||
void StreamBuffer::Commit() {
|
||||
if (!IsCoherent() && Usage() != MemoryUsage::Download && m_mapped_size != 0) {
|
||||
const auto result = vmaFlushAllocation(
|
||||
Graphics().allocator, NativeBuffer().memory.allocation, m_offset, m_mapped_size);
|
||||
EXIT_NOT_IMPLEMENTED(static_cast<vk::Result>(result) != vk::Result::eSuccess);
|
||||
if (Usage() != MemoryUsage::Download && m_mapped_size != 0) {
|
||||
Flush(m_offset, m_mapped_size);
|
||||
}
|
||||
|
||||
m_offset += m_mapped_size;
|
||||
@@ -303,23 +294,13 @@ void StreamBuffer::Commit() {
|
||||
return;
|
||||
}
|
||||
if (m_current_watch_cursor + 1 >= m_current_watches.size()) {
|
||||
ReserveWatches(m_current_watches, WATCHES_RESERVE_CHUNK);
|
||||
m_current_watches.resize(m_current_watches.size() + WATCHES_RESERVE_CHUNK);
|
||||
}
|
||||
auto& watch = m_current_watches[m_current_watch_cursor++];
|
||||
watch.upper_bound = m_offset;
|
||||
watch.tick = tick;
|
||||
}
|
||||
|
||||
void StreamBuffer::Invalidate(uint64_t offset, uint64_t size) {
|
||||
EXIT_IF(Usage() != MemoryUsage::Download || offset > Size() || size > Size() - offset);
|
||||
if (IsCoherent() || size == 0) {
|
||||
return;
|
||||
}
|
||||
const auto result = vmaInvalidateAllocation(Graphics().allocator,
|
||||
NativeBuffer().memory.allocation, offset, size);
|
||||
EXIT_NOT_IMPLEMENTED(static_cast<vk::Result>(result) != vk::Result::eSuccess);
|
||||
}
|
||||
|
||||
uint64_t StreamBuffer::Copy(const void* source, uint64_t size, uint64_t alignment) {
|
||||
EXIT_IF(source == nullptr);
|
||||
const auto [data, offset] = Map(size, alignment);
|
||||
@@ -329,10 +310,6 @@ uint64_t StreamBuffer::Copy(const void* source, uint64_t size, uint64_t alignmen
|
||||
return offset;
|
||||
}
|
||||
|
||||
void StreamBuffer::ReserveWatches(std::vector<Watch>& watches, size_t grow_size) {
|
||||
watches.resize(watches.size() + grow_size);
|
||||
}
|
||||
|
||||
bool StreamBuffer::WaitPendingOperations(const std::vector<Watch>& watches,
|
||||
std::optional<size_t> invalidation_mark,
|
||||
uint64_t requested_upper_bound, bool allow_wait,
|
||||
|
||||
+18
-13
@@ -6,19 +6,19 @@
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
VK_DEFINE_HANDLE(VmaAllocation)
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
class CommandBuffer;
|
||||
class CommandScheduler;
|
||||
struct StreamBufferTestAccess;
|
||||
struct GraphicContext;
|
||||
struct VulkanBuffer;
|
||||
|
||||
enum class MemoryUsage : uint8_t {
|
||||
DeviceLocal,
|
||||
@@ -42,18 +42,21 @@ public:
|
||||
~Buffer();
|
||||
KYTY_CLASS_NO_COPY(Buffer);
|
||||
|
||||
[[nodiscard]] vk::Buffer Handle() const noexcept;
|
||||
[[nodiscard]] vk::Buffer Handle() const noexcept { return m_buffer; }
|
||||
[[nodiscard]] uint64_t Size() const noexcept { return m_size; }
|
||||
[[nodiscard]] std::span<uint8_t> Mapped() const noexcept { return m_mapped; }
|
||||
[[nodiscard]] bool IsCoherent() const noexcept { return m_is_coherent; }
|
||||
[[nodiscard]] bool IsCoherent() const noexcept { return m_coherent; }
|
||||
[[nodiscard]] MemoryUsage Usage() const noexcept { return m_usage; }
|
||||
[[nodiscard]] uint64_t CpuAddress() const noexcept { return m_cpu_address; }
|
||||
[[nodiscard]] vk::DeviceAddress BufferDeviceAddress() const noexcept;
|
||||
[[nodiscard]] uint64_t Offset(uint64_t address) const noexcept {
|
||||
return address - m_cpu_address;
|
||||
}
|
||||
[[nodiscard]] bool IsInBounds(uint64_t address, uint64_t size) const noexcept;
|
||||
void Write(uint64_t offset, const void* source, uint64_t size);
|
||||
void IncreaseStreamScore(int score) noexcept { stream_score += score; }
|
||||
[[nodiscard]] int StreamScore() const noexcept { return stream_score; }
|
||||
void Flush(uint64_t offset, uint64_t size);
|
||||
void Invalidate(uint64_t offset, uint64_t size);
|
||||
void CopyFrom(CommandBuffer& command, const Buffer& source, uint64_t source_offset,
|
||||
uint64_t destination_offset, uint64_t size,
|
||||
vk::AccessFlags source_before = vk::AccessFlagBits::eMemoryWrite,
|
||||
@@ -65,10 +68,14 @@ public:
|
||||
vk::AccessFlagBits::eMemoryWrite);
|
||||
void Fill(uint64_t offset, uint64_t size, uint32_t value);
|
||||
|
||||
// BufferCache state lives directly on the resource.
|
||||
bool is_deleted = false;
|
||||
int stream_score = 0;
|
||||
size_t lru_id = 0;
|
||||
|
||||
protected:
|
||||
[[nodiscard]] GraphicContext& Graphics() const noexcept { return *m_graphics; }
|
||||
[[nodiscard]] CommandScheduler& Scheduler() const noexcept { return *m_scheduler; }
|
||||
[[nodiscard]] VulkanBuffer& NativeBuffer() noexcept { return *m_buffer; }
|
||||
|
||||
private:
|
||||
[[nodiscard]] vk::BufferMemoryBarrier Barrier(uint64_t offset, uint64_t size,
|
||||
@@ -79,10 +86,12 @@ private:
|
||||
CommandScheduler* m_scheduler = nullptr;
|
||||
MemoryUsage m_usage = MemoryUsage::DeviceLocal;
|
||||
uint64_t m_cpu_address = 0;
|
||||
uint64_t m_size = 0;
|
||||
std::unique_ptr<VulkanBuffer> m_buffer;
|
||||
vk::DeviceAddress m_device_address = 0;
|
||||
vk::Buffer m_buffer = nullptr;
|
||||
VmaAllocation m_allocation = nullptr;
|
||||
uint64_t m_size;
|
||||
bool m_coherent = false;
|
||||
std::span<uint8_t> m_mapped;
|
||||
bool m_is_coherent = false;
|
||||
};
|
||||
|
||||
class StreamBuffer final: public Buffer {
|
||||
@@ -93,9 +102,6 @@ public:
|
||||
[[nodiscard]] std::pair<uint8_t*, uint64_t> Map(uint64_t size, uint64_t alignment = 0,
|
||||
bool allow_wait = true);
|
||||
void Commit();
|
||||
// Download mappings become visible to the CPU only after their GPU completion tick is free.
|
||||
// Call this from the scheduler's deferred completion operation before reading Mapped().
|
||||
void Invalidate(uint64_t offset, uint64_t size);
|
||||
[[nodiscard]] uint64_t Copy(const void* source, uint64_t size, uint64_t alignment = 0);
|
||||
|
||||
private:
|
||||
@@ -106,7 +112,6 @@ private:
|
||||
uint64_t upper_bound = 0;
|
||||
};
|
||||
|
||||
void ReserveWatches(std::vector<Watch>& watches, size_t grow_size);
|
||||
[[nodiscard]] static bool NormalizeReservation(bool coherent, uint64_t atom, uint64_t& size,
|
||||
uint64_t& alignment);
|
||||
[[nodiscard]] bool WaitPendingOperations(const std::vector<Watch>& watches,
|
||||
|
||||
+752
-741
File diff suppressed because it is too large.
Load diff
+70
-68
@@ -4,19 +4,18 @@
|
||||
#include "common/abi.h"
|
||||
#include "common/common.h"
|
||||
#include "common/lruCache.h"
|
||||
#include "common/slotVector.h"
|
||||
#include "graphics/host_gpu/pageManager.h"
|
||||
#include "graphics/host_gpu/regionManager.h"
|
||||
#include "graphics/host_gpu/renderer/cache/multiLevelPageTable.h"
|
||||
#include "graphics/host_gpu/renderer/image/blitHelper.h"
|
||||
#include "graphics/host_gpu/renderer/image/image.h"
|
||||
#include "graphics/host_gpu/renderer/image/tiler.h"
|
||||
|
||||
#include <compare>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
@@ -27,8 +26,6 @@ class BufferCache;
|
||||
class CommandBuffer;
|
||||
class CommandScheduler;
|
||||
class RenderExecutor;
|
||||
class StreamBuffer;
|
||||
class TileManager;
|
||||
struct TextureCacheTestAccess;
|
||||
|
||||
class TextureCache {
|
||||
@@ -41,12 +38,6 @@ public:
|
||||
BindingType type = BindingType::Texture;
|
||||
};
|
||||
|
||||
struct RegionInfo {
|
||||
bool image_pages = false;
|
||||
bool image_bytes = false;
|
||||
bool gpu_image_bytes = false;
|
||||
};
|
||||
|
||||
TextureCache(GraphicContext& graphics, CommandScheduler& scheduler, PageManager& page_manager,
|
||||
BufferCache& buffer_cache);
|
||||
~TextureCache();
|
||||
@@ -59,16 +50,18 @@ public:
|
||||
[[nodiscard]] vk::ImageView FindTexture(ImageId id, const ImageDesc& desc);
|
||||
[[nodiscard]] vk::ImageView FindRenderTarget(ImageId id, const ImageDesc& desc);
|
||||
[[nodiscard]] vk::ImageView FindDepthTarget(ImageId id, const ImageDesc& desc);
|
||||
[[nodiscard]] Image& GetImage(ImageId id);
|
||||
[[nodiscard]] const Image& GetImage(ImageId id) const;
|
||||
void MarkGpuWritten(ImageId id);
|
||||
[[nodiscard]] Image& GetImage(ImageId id) {
|
||||
auto& image = m_slot_images[id];
|
||||
TouchImage(image);
|
||||
return image;
|
||||
}
|
||||
void MarkGpuWritten(ImageId id);
|
||||
|
||||
[[nodiscard]] bool ClearImageFromBuffer(CommandBuffer& command, uint64_t address, uint64_t size,
|
||||
uint32_t packed_clear);
|
||||
void InvalidateMemory(uint64_t address, uint64_t size);
|
||||
[[nodiscard]] bool InvalidateMemoryFromGPU(uint64_t address, uint64_t size,
|
||||
bool formatted_buffer_write = false);
|
||||
[[nodiscard]] RegionInfo QueryRegion(uint64_t address, uint64_t size);
|
||||
void InvalidateMemoryFromGPU(uint64_t address, uint64_t size);
|
||||
[[nodiscard]] bool IsRegionGpuModified(uint64_t address, uint64_t size);
|
||||
|
||||
[[nodiscard]] bool IsMeta(uint64_t address);
|
||||
[[nodiscard]] bool IsMetaCleared(uint64_t address, uint32_t slice);
|
||||
@@ -81,16 +74,14 @@ public:
|
||||
|
||||
private:
|
||||
enum class TransferDirection { Upload, Download };
|
||||
struct ColorTransferPlan;
|
||||
struct DownloadPlan;
|
||||
|
||||
struct Slot {
|
||||
std::shared_ptr<Image> image;
|
||||
uint32_t generation = 1;
|
||||
};
|
||||
struct TextureTransfer;
|
||||
struct ImageDownload;
|
||||
|
||||
struct MetaDataInfo {
|
||||
uint32_t clear_mask = 0;
|
||||
enum class Type : uint8_t { CMask, FMask, HTile };
|
||||
|
||||
Type type;
|
||||
uint32_t clear_mask = UINT32_MAX;
|
||||
};
|
||||
|
||||
struct OverlapResult {
|
||||
@@ -102,25 +93,38 @@ private:
|
||||
using ImageIds = InlinePageOwnerList<ImageId, 16>;
|
||||
using ImagePageTable = MultiLevelPageTable<ImageIds, 20, 40, 10>;
|
||||
|
||||
[[nodiscard]] Image& ResolveImage(ImageId id);
|
||||
[[nodiscard]] const Image& ResolveImage(ImageId id) const;
|
||||
[[nodiscard]] std::shared_ptr<Image> ResolveOwner(ImageId id) const;
|
||||
[[nodiscard]] ImageId InsertImage(const ImageInfo& info);
|
||||
[[nodiscard]] ImageId GetNullImage(const ImageDesc& desc);
|
||||
void RegisterImage(ImageId id);
|
||||
void UnregisterImage(ImageId id);
|
||||
void DeleteImage(ImageId id);
|
||||
void DeleteImages(std::span<const ImageId> ids, std::optional<ImageId> native_source = {});
|
||||
void RetainImage(CommandBuffer& command, ImageId id);
|
||||
void TouchImage(Image& image);
|
||||
void TrackImage(ImageId id);
|
||||
void TrackImageHead(ImageId id);
|
||||
void TrackImageTail(ImageId id);
|
||||
void UntrackImage(ImageId id);
|
||||
void UntrackImageHead(ImageId id);
|
||||
void UntrackImageTail(ImageId id);
|
||||
void TrackImageDownload(ImageId id);
|
||||
void TrackImageDownloadLocked(ImageId id, Image& image);
|
||||
// Callers have validated the nonempty 40-bit range with TryGetPageRange.
|
||||
template <typename Func>
|
||||
static void ForEachPage(uint64_t address, size_t size, Func&& func) {
|
||||
using FuncReturn = typename std::invoke_result<Func, uint64_t>::type;
|
||||
static constexpr bool RETURNS_BOOL = std::is_same_v<FuncReturn, bool>;
|
||||
const uint64_t page_end = (address + size - 1) >> ImagePageTable::kPageBits;
|
||||
for (uint64_t page = address >> ImagePageTable::kPageBits; page <= page_end; ++page) {
|
||||
if constexpr (RETURNS_BOOL) {
|
||||
if (func(page)) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
func(page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] ImageId InsertImage(const ImageInfo& info);
|
||||
[[nodiscard]] ImageId GetNullImage(const ImageDesc& desc);
|
||||
void RegisterImage(ImageId id);
|
||||
void UnregisterImage(ImageId id);
|
||||
void DeleteImage(ImageId id);
|
||||
void FreeImage(ImageId id);
|
||||
void TouchImage(Image& image);
|
||||
void TrackImage(ImageId id);
|
||||
void TrackImageHead(ImageId id);
|
||||
void TrackImageTail(ImageId id);
|
||||
void UntrackImage(ImageId id);
|
||||
void UntrackImageHead(ImageId id);
|
||||
void UntrackImageTail(ImageId id);
|
||||
void MarkAsMaybeDirty(ImageId id, Image& image);
|
||||
void TrackImageDownload(ImageId id, Image& image);
|
||||
[[nodiscard]] static bool SameBacking(const ImageInfo& cached, const ImageInfo& requested,
|
||||
bool exact_format);
|
||||
[[nodiscard]] static BindingType UploadBinding(const Image& image);
|
||||
@@ -134,46 +138,44 @@ private:
|
||||
[[nodiscard]] ImageId ResolveDepthOverlap(const ImageInfo& requested, BindingType binding,
|
||||
ImageId cached);
|
||||
[[nodiscard]] ImageId ExpandImage(const ImageInfo& info, ImageId source);
|
||||
void RefreshImage(ImageId id, const ImageDesc& desc);
|
||||
void InitializeImage(ImageId id, const ImageDesc& desc);
|
||||
[[nodiscard]] ColorTransferPlan BuildColorTransfer(const Image& image, BindingType binding,
|
||||
TransferDirection direction) const;
|
||||
[[nodiscard]] DownloadPlan BuildDownload(const Image& image) const;
|
||||
void UploadImage(Image& image, const ImageDesc& desc, Buffer& source, uint64_t source_offset);
|
||||
void DownloadImageData(Image& image, Buffer& destination, uint64_t destination_offset,
|
||||
uint64_t destination_size, DownloadPlan plan);
|
||||
void RefreshImage(ImageId id);
|
||||
void MaterializeDccClear(ImageId id, const ImageDesc& desc,
|
||||
uint32_t metadata_base_layer);
|
||||
void InitializeImage(ImageId id);
|
||||
[[nodiscard]] TextureTransfer
|
||||
BuildTextureTransfer(const Image& image, BindingType binding, TransferDirection direction) const;
|
||||
[[nodiscard]] ImageDownload BuildDownload(const Image& image) const;
|
||||
void UploadImage(Image& image, Buffer& source, uint64_t source_offset);
|
||||
void DownloadImage(Image& image, Buffer& destination, uint64_t destination_offset,
|
||||
uint64_t destination_size, ImageDownload transfer);
|
||||
void DownloadDepth(Image& image, Buffer& destination, uint64_t destination_offset);
|
||||
void CommitGpuWrite(Image& image);
|
||||
// Caller holds m_lock. Volume layer ranges select depth slices.
|
||||
void ClearImage(CommandBuffer& command, ImageId id, vk::Format format,
|
||||
const vk::ImageSubresourceRange& range, const vk::ClearValue& clear);
|
||||
void PrepareImageCopy(Image& image);
|
||||
void RefreshCopySource(ImageId id);
|
||||
[[nodiscard]] bool CopyD16(Image& destination, Image& source);
|
||||
void CopyImage(ImageId destination, ImageId source);
|
||||
void AssociateStencil(ImageId depth, GuestRange stencil);
|
||||
void AssociateStencilLocked(ImageId depth, GuestRange stencil);
|
||||
void CopyImageMip(ImageId destination, ImageId source, uint32_t mip, uint32_t layer);
|
||||
void ValidateImageDesc(const ImageDesc& desc) const;
|
||||
|
||||
void InvalidateCpuAliases(uint64_t address, uint64_t size);
|
||||
void ClearGpuModified(ImageId id);
|
||||
|
||||
void DownloadImage(ImageId id);
|
||||
[[nodiscard]] bool TryDownloadImage(ImageId id);
|
||||
[[nodiscard]] std::pair<uint8_t*, uint64_t> MapDownload(uint64_t size, uint64_t alignment);
|
||||
void QueueDownload(GuestRange range, StreamBuffer& download, uint8_t* mapped, uint64_t offset);
|
||||
void InvalidateCpuAliases(uint64_t address, uint64_t size);
|
||||
[[nodiscard]] bool DownloadImageMemory(ImageId id);
|
||||
|
||||
GraphicContext& m_graphics;
|
||||
CommandScheduler& m_scheduler;
|
||||
TrackingSpinLock m_lock;
|
||||
PageManager& m_page_manager;
|
||||
BlitHelper m_blit_helper;
|
||||
std::unique_ptr<TileManager> m_tiler;
|
||||
TileManager m_tiler;
|
||||
BufferCache& m_buffer_cache;
|
||||
std::vector<Slot> m_slots;
|
||||
std::vector<uint32_t> m_free_slots;
|
||||
Common::SlotVector<Image> m_slot_images;
|
||||
ImagePageTable m_image_page_table;
|
||||
std::map<vk::Format, ImageId> m_null_images;
|
||||
std::unordered_map<vk::Format, ImageId> m_null_images;
|
||||
Common::LeastRecentlyUsedCache<ImageId, uint64_t> m_lru_cache;
|
||||
std::set<ImageId> m_download_images;
|
||||
std::unordered_set<ImageId> m_download_images;
|
||||
std::map<uint64_t, MetaDataInfo> m_surface_metas;
|
||||
uint64_t m_total_used_memory = 0;
|
||||
uint64_t m_trigger_gc_memory = 0;
|
||||
|
||||
@@ -4,42 +4,58 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/profiler.h"
|
||||
#include "graphics/guest_gpu/gpu_defs.h"
|
||||
#include "graphics/guest_gpu/gpu_format.h"
|
||||
#include "graphics/guest_gpu/hardwareContext.h"
|
||||
#include "graphics/guest_gpu/tile.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "graphics/host_gpu/renderer/debug.h"
|
||||
#include "graphics/host_gpu/renderer/image/textureCommon.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
static std::atomic<uint32_t> g_render_color_log_count = 0;
|
||||
|
||||
static bool DccAlphaOnMsb(const HW::ColorInfo& info) {
|
||||
switch (info.format) {
|
||||
case Prospero::ChannelLayout::k10_10_10_2:
|
||||
case Prospero::ChannelLayout::k10_10_10_2Float:
|
||||
case Prospero::ChannelLayout::k5_5_5_1: return true;
|
||||
case Prospero::ChannelLayout::k2_10_10_10:
|
||||
case Prospero::ChannelLayout::k1_5_5_5: return false;
|
||||
default: break;
|
||||
}
|
||||
const auto components =
|
||||
Prospero::ResolveRenderTargetFormat(info.format, info.channel_type).components;
|
||||
if (components == 1) {
|
||||
return info.channel_order != Prospero::ChannelOrder::kStandard;
|
||||
}
|
||||
return components == 3 || info.channel_order == Prospero::ChannelOrder::kStandard ||
|
||||
info.channel_order == Prospero::ChannelOrder::kAlt;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandBuffer& buffer,
|
||||
RenderColorInfo& r,
|
||||
void RenderExecutor::ResolveRenderColorTarget(CommandBuffer& buffer, RenderColorInfo& r,
|
||||
uint32_t render_target_slice_offset,
|
||||
uint32_t render_target_slot, bool ignore_target_mask,
|
||||
uint32_t rt_slot, bool ignore_target_mask,
|
||||
bool exact_format) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
|
||||
const auto rt_slot = (render_target_slot == UINT32_MAX ? render_target_first_bound_slot(buffer)
|
||||
: render_target_slot);
|
||||
const auto& rt = hw.GetRenderTarget(rt_slot);
|
||||
auto mask = render_target_mask_slot(hw.GetRenderTargetMask(), rt_slot);
|
||||
if (ignore_target_mask && rt.base.addr != 0 && mask == 0) {
|
||||
mask = 0x0f;
|
||||
}
|
||||
|
||||
r.target_slot = rt_slot;
|
||||
r.export_mapping = {};
|
||||
r = {};
|
||||
r.target_slot = rt_slot;
|
||||
|
||||
if (rt.base.addr == 0 || mask == 0) {
|
||||
if (graphics_debug_dump_enabled()) {
|
||||
@@ -54,21 +70,6 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
|
||||
}
|
||||
}
|
||||
|
||||
// No color output
|
||||
r.type = RenderColorType::NoColorOutput;
|
||||
r.desc = {};
|
||||
r.base_addr = 0;
|
||||
r.image_id = {};
|
||||
r.image_view = nullptr;
|
||||
r.format = vk::Format::eUndefined;
|
||||
r.extent = {};
|
||||
r.base_mip_level = 0;
|
||||
r.base_array_layer = 0;
|
||||
r.buffer_size = 0;
|
||||
r.samples = 1;
|
||||
r.export_mapping = {};
|
||||
r.color_clear_enable = false;
|
||||
r.color_clear_value = {};
|
||||
return;
|
||||
}
|
||||
const auto samples = render_sample_count(rt.attrib.num_fragments);
|
||||
@@ -76,8 +77,41 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
|
||||
EXIT("unsupported render-target sample configuration: samples=%u fragments=%u\n",
|
||||
rt.attrib.num_samples, rt.attrib.num_fragments);
|
||||
}
|
||||
const uint32_t levels = rt.attrib2.num_mip_levels + 1u;
|
||||
if (levels == 0 || levels > 16 || rt.view.current_mip_level >= levels) {
|
||||
EXIT("unsupported render-target mip range: current=%u levels=%u\n",
|
||||
rt.view.current_mip_level, levels);
|
||||
}
|
||||
static constexpr std::array image_types {Prospero::ImageType::kColor1D,
|
||||
Prospero::ImageType::kColor2D,
|
||||
Prospero::ImageType::kColor3D};
|
||||
if (rt.attrib3.dimension >= image_types.size()) {
|
||||
EXIT("unsupported render-target dimension: %u\n", rt.attrib3.dimension);
|
||||
}
|
||||
const auto image_type = image_types[rt.attrib3.dimension];
|
||||
const bool is_1d = image_type == Prospero::ImageType::kColor1D;
|
||||
const bool volume = image_type == Prospero::ImageType::kColor3D;
|
||||
if (is_1d && rt.attrib2.height != 0) {
|
||||
EXIT("1D render target has nonzero height: %u\n", rt.attrib2.height);
|
||||
}
|
||||
if (!volume && rt.attrib3.depth != 0) {
|
||||
EXIT("non-3D render target has nonzero depth: %u\n", rt.attrib3.depth);
|
||||
}
|
||||
if (is_1d && samples != 1) {
|
||||
EXIT("multisampled 1D render targets are unsupported\n");
|
||||
}
|
||||
if (volume && samples != 1) {
|
||||
EXIT("multisampled 3D render targets are unsupported\n");
|
||||
}
|
||||
const uint32_t depth = volume ? rt.attrib3.depth + 1u : 1u;
|
||||
// For volumes, CB_COLOR_VIEW bounds exported slices; ATTRIB3 defines storage depth.
|
||||
// The host attachment contains only the selected slices that exist in this mip.
|
||||
const uint32_t last_layer = volume
|
||||
? std::min(rt.view.last_array_slice_index,
|
||||
std::max(depth >> rt.view.current_mip_level, 1u) - 1u)
|
||||
: rt.view.last_array_slice_index;
|
||||
const auto view = ResolveTargetViewInfo(
|
||||
rt.view.base_array_slice_index, rt.view.last_array_slice_index, render_target_slice_offset);
|
||||
rt.view.base_array_slice_index, last_layer, render_target_slice_offset);
|
||||
switch (view.type) {
|
||||
case TargetViewType::Image2D:
|
||||
case TargetViewType::Image2DArray: break;
|
||||
@@ -86,12 +120,6 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
|
||||
rt.view.base_array_slice_index, rt.view.last_array_slice_index,
|
||||
render_target_slice_offset);
|
||||
}
|
||||
r.base_array_layer = view.base_layer;
|
||||
const uint32_t levels = rt.attrib2.num_mip_levels + 1u;
|
||||
if (levels == 0 || levels > 16 || rt.view.current_mip_level >= levels) {
|
||||
EXIT("unsupported render-target mip range: current=%u levels=%u\n",
|
||||
rt.view.current_mip_level, levels);
|
||||
}
|
||||
if (graphics_debug_dump_enabled()) {
|
||||
static std::atomic_uint log_count = 0;
|
||||
const auto log_id = log_count.fetch_add(1, std::memory_order_relaxed);
|
||||
@@ -101,45 +129,36 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
|
||||
" attrib3_tile=0x%08" PRIx32 " attrib3_dim=0x%08" PRIx32 " fmt=0x%08" PRIx32
|
||||
" nfmt=0x%08" PRIx32 " order=0x%08" PRIx32 "\n",
|
||||
rt_slot, rt.base.addr, mask, rt.attrib2.width, rt.attrib2.height,
|
||||
rt.attrib3.tile_mode, rt.attrib3.dimension, rt.info.format, rt.info.channel_type,
|
||||
rt.info.channel_order);
|
||||
static_cast<uint32_t>(rt.attrib3.tile_mode), rt.attrib3.dimension,
|
||||
static_cast<uint32_t>(rt.info.format), static_cast<uint32_t>(rt.info.channel_type),
|
||||
static_cast<uint32_t>(rt.info.channel_order));
|
||||
}
|
||||
}
|
||||
|
||||
// CB_COLOR_CONTROL describes the color-buffer operation / ROP3 logic op.
|
||||
// ROP3 Copy is the normal color write path, not a render-target clear.
|
||||
// SRGB clear words are still encoded as normalized component values.
|
||||
// Color-control state selects the color-buffer operation and logical blend operation.
|
||||
// The normal copy operation is a regular color write, not an attachment clear.
|
||||
// Nonlinear clear values are still stored as normalized components.
|
||||
// Fast color clears are metadata driven and must be handled explicitly when
|
||||
// that metadata path is implemented; render-pass load must preserve contents.
|
||||
r.color_clear_enable = false;
|
||||
r.color_clear_value = {};
|
||||
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
uint32_t pitch = 0;
|
||||
uint64_t size = 0;
|
||||
bool tile = false;
|
||||
const bool volume = rt.attrib3.dimension == 2;
|
||||
if (rt.attrib3.dimension != 1 && !volume) {
|
||||
EXIT("unsupported render-target dimension: %u\n", rt.attrib3.dimension);
|
||||
}
|
||||
if (!volume && rt.attrib3.depth != 0) {
|
||||
EXIT("2D render target has nonzero depth: %u\n", rt.attrib3.depth);
|
||||
}
|
||||
if (volume && samples != 1) {
|
||||
EXIT("multisampled 3D render targets are unsupported\n");
|
||||
}
|
||||
const uint32_t depth = volume ? rt.attrib3.depth + 1u : 1u;
|
||||
const bool standard64 =
|
||||
rt.attrib3.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kStandard64KB);
|
||||
const bool standard4 = rt.attrib3.tile_mode == Prospero::TileMode::kStandard4KB;
|
||||
const bool standard64 = rt.attrib3.tile_mode == Prospero::TileMode::kStandard64KB;
|
||||
const bool depth_tile = rt.attrib3.tile_mode == Prospero::TileMode::kDepth;
|
||||
const bool texture_tile = standard4 || standard64 || depth_tile;
|
||||
|
||||
switch (rt.attrib3.tile_mode) {
|
||||
case Prospero::GpuEnumValue(Prospero::TileMode::kLinear):
|
||||
case Prospero::GpuEnumValue(Prospero::TileMode::kStandard64KB):
|
||||
case Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget):
|
||||
case Prospero::TileMode::kLinear:
|
||||
case Prospero::TileMode::kStandard4KB:
|
||||
case Prospero::TileMode::kStandard64KB:
|
||||
case Prospero::TileMode::kDepth:
|
||||
case Prospero::TileMode::kRenderTarget:
|
||||
tile = !RenderIsColorTileModeLinear(rt.attrib3.tile_mode);
|
||||
break;
|
||||
default: EXIT("unknown tile mode: %u\n", rt.attrib3.tile_mode);
|
||||
default: EXIT("unknown tile mode: %u\n", static_cast<uint32_t>(rt.attrib3.tile_mode));
|
||||
}
|
||||
if (!tile && levels > 1) {
|
||||
EXIT("linear mipmapped render targets are unsupported\n");
|
||||
@@ -147,6 +166,9 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
|
||||
if (samples > 1 && (!tile || levels != 1)) {
|
||||
EXIT("multisampled render targets require a single-mip tiled surface\n");
|
||||
}
|
||||
if (texture_tile && samples != 1) {
|
||||
EXIT("texture-tiled color render targets do not support multisampling\n");
|
||||
}
|
||||
|
||||
width = rt.attrib2.width + 1;
|
||||
height = rt.attrib2.height + 1;
|
||||
@@ -157,39 +179,38 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
|
||||
EXIT("render-target format has no valid element size\n");
|
||||
}
|
||||
const auto transfer_format = ImageOps::RenderTargetTransferFormat(bytes_per_element);
|
||||
if (standard64 &&
|
||||
(rt.attrib3.dimension != 1 || rt.attrib3.depth != 0 || levels != 1 ||
|
||||
rt.view.current_mip_level != 0 || view.base_layer != 0 || view.image_layers != 1 ||
|
||||
samples != 1 || bytes_per_element != 4 || rt.pitch.pitch_div8_minus1 != 0 ||
|
||||
(rt.base.addr & 0xffffu) != 0 || rt.info.fmask_compression_enable ||
|
||||
rt.info.fmask_data_compression_disable || rt.info.fmask_one_frag_mode ||
|
||||
rt.info.cmask_fast_clear_enable || rt.info.dcc_compression_enable ||
|
||||
rt.info.cmask_is_linear != 0 || rt.info.cmask_addr_type != 0 || rt.info.alt_tile_mode ||
|
||||
rt.cmask.addr != 0 || rt.fmask.addr != 0 || rt.dcc_addr.addr != 0 ||
|
||||
rt.dcc.data_write_on_dcc_clear_to_reg)) {
|
||||
EXIT("unsupported Standard64KB render target: addr=0x%016" PRIx64
|
||||
TileTextureBlockLayout texture_tile_layout {};
|
||||
if (texture_tile &&
|
||||
(!TileGetTextureBlockLayout(transfer_format, rt.attrib3.tile_mode, volume,
|
||||
texture_tile_layout) ||
|
||||
(rt.base.addr & (texture_tile_layout.block.block_size - 1u)) != 0 ||
|
||||
rt.info.fmask_compression_enable || rt.info.fmask_data_compression_disable ||
|
||||
rt.info.fmask_one_frag_mode || rt.info.cmask_fast_clear_enable ||
|
||||
rt.info.dcc_compression_enable || rt.cmask.addr != 0 || rt.fmask.addr != 0 ||
|
||||
rt.dcc_addr.addr != 0 || rt.dcc.data_write_on_dcc_clear_to_reg)) {
|
||||
EXIT("unsupported texture-tiled render target: addr=0x%016" PRIx64 " tile=%u"
|
||||
" dimension=%u depth=%u levels=%u layer=%u/%u samples=%u fragments=%u bpe=%u"
|
||||
" cmask=0x%016" PRIx64 " fmask=0x%016" PRIx64 " dcc=0x%016" PRIx64 "\n",
|
||||
rt.base.addr, rt.attrib3.dimension, rt.attrib3.depth, levels, view.base_layer,
|
||||
view.image_layers, rt.attrib.num_samples, rt.attrib.num_fragments, bytes_per_element,
|
||||
rt.cmask.addr, rt.fmask.addr, rt.dcc_addr.addr);
|
||||
rt.base.addr, static_cast<uint32_t>(rt.attrib3.tile_mode), rt.attrib3.dimension,
|
||||
rt.attrib3.depth, levels, view.base_layer, view.image_layers, rt.attrib.num_samples,
|
||||
rt.attrib.num_fragments, bytes_per_element, rt.cmask.addr, rt.fmask.addr,
|
||||
rt.dcc_addr.addr);
|
||||
}
|
||||
if (rt.pitch.pitch_div8_minus1 != 0) {
|
||||
pitch = (rt.pitch.pitch_div8_minus1 + 1u) << 3u;
|
||||
} else if (tile) {
|
||||
if (volume) {
|
||||
pitch = TileGetTexturePitch(transfer_format, width, rt.attrib3.tile_mode);
|
||||
} else if (standard64) {
|
||||
pitch = TileGetTexturePitch(Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float),
|
||||
width, rt.attrib3.tile_mode);
|
||||
} else {
|
||||
pitch = TileGetRenderTargetPitch(width, bytes_per_element, rt.attrib.num_fragments);
|
||||
}
|
||||
if (pitch == 0) {
|
||||
EXIT("unsupported render-target pitch: width=%u bytes=%u\n", width, bytes_per_element);
|
||||
}
|
||||
if ((standard64 || depth_tile) &&
|
||||
(rt.attrib3.dimension != 1 || rt.attrib3.depth != 0 ||
|
||||
(depth_tile && (view.base_layer != 0 || view.image_layers != 1)))) {
|
||||
EXIT("unsupported 64KB texture-tiled render-target view: dimension=%u depth=%u"
|
||||
" layer=%u/%u\n",
|
||||
rt.attrib3.dimension, rt.attrib3.depth, view.base_layer, view.image_layers);
|
||||
}
|
||||
// PPSA28068: the linear EULA target and its sampled view must share the padded pitch.
|
||||
if (!tile || volume || texture_tile) {
|
||||
pitch = TileGetTexturePitch(transfer_format, width, rt.attrib3.tile_mode);
|
||||
} else {
|
||||
pitch = width;
|
||||
pitch = TileGetRenderTargetPitch(width, bytes_per_element, rt.attrib.num_fragments);
|
||||
}
|
||||
if (pitch == 0) {
|
||||
EXIT("unsupported render-target pitch: width=%u bytes=%u\n", width, bytes_per_element);
|
||||
}
|
||||
|
||||
TileSizeOffset mip_sizes[16] {};
|
||||
@@ -207,18 +228,17 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
|
||||
1};
|
||||
if (!tile || !TileGetTiledTextureLayout(description, volume_layout)) {
|
||||
EXIT("unsupported 3D render-target layout: %ux%ux%u levels=%u tile=%u\n", width, height,
|
||||
depth, levels, rt.attrib3.tile_mode);
|
||||
depth, levels, static_cast<uint32_t>(rt.attrib3.tile_mode));
|
||||
}
|
||||
size = volume_layout.block_slice_size;
|
||||
backing_size = volume_layout.total_size;
|
||||
} else if (tile) {
|
||||
TileSizeAlign layout {};
|
||||
bool valid_layout = false;
|
||||
if (standard64) {
|
||||
TileGetTextureSize(Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float), width,
|
||||
height, levels, rt.attrib3.tile_mode, &layout, mip_sizes,
|
||||
mip_padded);
|
||||
valid_layout = layout.size != 0 && layout.align == 65536;
|
||||
if (texture_tile) {
|
||||
TileGetTextureSize(transfer_format, width, height, levels, rt.attrib3.tile_mode,
|
||||
&layout, mip_sizes, mip_padded);
|
||||
valid_layout = layout.size != 0 && layout.align == texture_tile_layout.block.block_size;
|
||||
} else {
|
||||
valid_layout =
|
||||
levels == 1 ? TileGetRenderTargetSize(width, height, pitch, bytes_per_element,
|
||||
@@ -244,12 +264,6 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
|
||||
mip_sizes[0] = {static_cast<uint32_t>(size), 0, 0, 0, 0, 0};
|
||||
mip_padded[0] = {pitch, height};
|
||||
}
|
||||
if (rt.slice.slice_div64_minus1 != 0 &&
|
||||
(static_cast<uint64_t>(rt.slice.slice_div64_minus1) + 1u) * 64u != size) {
|
||||
EXIT("render-target slice span mismatch: encoded=0x%016" PRIx64 " derived=0x%016" PRIx64
|
||||
"\n",
|
||||
(static_cast<uint64_t>(rt.slice.slice_div64_minus1) + 1u) * 64u, size);
|
||||
}
|
||||
if (size == 0 || (!volume && size > UINT64_MAX / view.image_layers)) {
|
||||
EXIT("render-target memory footprint is invalid\n");
|
||||
}
|
||||
@@ -259,18 +273,12 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
|
||||
if (backing_size == 0) {
|
||||
EXIT("render-target backing is empty\n");
|
||||
}
|
||||
if (backing_size > TRACKER_ADDRESS_SIZE - rt.base.addr) {
|
||||
if (!GuestRange {rt.base.addr, backing_size}.Valid()) {
|
||||
EXIT("render-target backing range is invalid\n");
|
||||
}
|
||||
|
||||
const vk::Extent2D view_extent = {std::max(width >> rt.view.current_mip_level, 1u),
|
||||
std::max(height >> rt.view.current_mip_level, 1u)};
|
||||
const uint32_t view_depth = std::max(depth >> rt.view.current_mip_level, 1u);
|
||||
if (volume &&
|
||||
(view.base_layer >= view_depth || view.layer_count > view_depth - view.base_layer)) {
|
||||
EXIT("3D render-target view exceeds mip depth: base=%u count=%u depth=%u mip=%u\n",
|
||||
view.base_layer, view.layer_count, view_depth, rt.view.current_mip_level);
|
||||
}
|
||||
|
||||
auto decision_log_id = g_render_color_log_count.fetch_add(1);
|
||||
if (decision_log_id < 128) {
|
||||
@@ -278,8 +286,9 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
|
||||
" extent=%ux%ux%u view_mip=%u view_extent=%ux%u levels=%u pitch=%u"
|
||||
" fmt=0x%08" PRIx32 " nfmt=0x%08" PRIx32 " order=0x%08" PRIx32 " samples=%u tile=%s\n",
|
||||
rt_slot, rt.base.addr, backing_size, width, height, depth, rt.view.current_mip_level,
|
||||
view_extent.width, view_extent.height, levels, pitch, rt.info.format,
|
||||
rt.info.channel_type, rt.info.channel_order, samples, tile ? "tiled" : "linear");
|
||||
view_extent.width, view_extent.height, levels, pitch,
|
||||
static_cast<uint32_t>(rt.info.format), static_cast<uint32_t>(rt.info.channel_type),
|
||||
static_cast<uint32_t>(rt.info.channel_order), samples, tile ? "tiled" : "linear");
|
||||
}
|
||||
|
||||
TextureCache::ImageDesc desc {};
|
||||
@@ -287,13 +296,24 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
|
||||
desc.info.data = {rt.base.addr, backing_size};
|
||||
desc.info.pixel_format = target_format.format;
|
||||
desc.info.guest_format = transfer_format;
|
||||
desc.info.type = volume ? Prospero::ImageType::kColor3D : Prospero::ImageType::kColor2D;
|
||||
desc.info.type = image_type;
|
||||
desc.info.extent = {width, height, depth};
|
||||
desc.info.resources = {levels, volume ? 1u : view.image_layers};
|
||||
desc.info.pitch = pitch;
|
||||
desc.info.bytes_per_block = bytes_per_element;
|
||||
desc.info.samples = samples;
|
||||
desc.info.tile_mode = rt.attrib3.tile_mode;
|
||||
const bool has_dcc = rt.info.dcc_compression_enable && rt.dcc_addr.addr != 0;
|
||||
if (has_dcc) {
|
||||
TileSizeAlign metadata_size {};
|
||||
(void)TileGetDccSize(width, height, volume ? depth : view.image_layers, bytes_per_element,
|
||||
levels, rt.attrib3.tile_mode, metadata_size, rt.attrib.num_fragments);
|
||||
desc.info.metadata.kind = ImageMetadataKind::Dcc;
|
||||
desc.info.metadata.range = {rt.dcc_addr.addr, metadata_size.size};
|
||||
desc.info.metadata.dcc_clear_word = rt.clear_word0.word0;
|
||||
desc.info.metadata.dcc_clear_register_valid = true;
|
||||
desc.info.metadata.dcc_alpha_msb = DccAlphaOnMsb(rt.info);
|
||||
}
|
||||
for (uint32_t level = 0; level < levels; level++) {
|
||||
if (volume) {
|
||||
const auto& mip = volume_layout.mips[level];
|
||||
@@ -319,8 +339,13 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
|
||||
};
|
||||
}
|
||||
desc.view_info.format = target_format.format;
|
||||
desc.view_info.type =
|
||||
view.layer_count == 1 ? vk::ImageViewType::e2D : vk::ImageViewType::e2DArray;
|
||||
if (is_1d) {
|
||||
desc.view_info.type =
|
||||
view.layer_count == 1 ? vk::ImageViewType::e1D : vk::ImageViewType::e1DArray;
|
||||
} else {
|
||||
desc.view_info.type =
|
||||
view.layer_count == 1 ? vk::ImageViewType::e2D : vk::ImageViewType::e2DArray;
|
||||
}
|
||||
desc.view_info.aspect = vk::ImageAspectFlagBits::eColor;
|
||||
desc.view_info.base_level = rt.view.current_mip_level;
|
||||
desc.view_info.level_count = 1;
|
||||
@@ -329,18 +354,10 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB
|
||||
desc.view_info.usage = vk::ImageUsageFlagBits::eColorAttachment;
|
||||
auto& texture_cache = m_context.GetTextureCache();
|
||||
r.desc = std::move(desc);
|
||||
r.guest_mip_level = rt.view.current_mip_level;
|
||||
r.guest_array_layer = view.base_layer;
|
||||
r.image_id = texture_cache.FindImage(r.desc, exact_format);
|
||||
r.type = RenderColorType::RenderTexture;
|
||||
r.base_addr = rt.base.addr;
|
||||
r.image_view = nullptr;
|
||||
r.format = r.desc.view_info.format;
|
||||
r.extent = view_extent;
|
||||
r.base_mip_level = rt.view.current_mip_level;
|
||||
r.buffer_size = backing_size;
|
||||
r.samples = samples;
|
||||
r.export_mapping = target_format.export_mapping;
|
||||
r.color_clear_enable = false;
|
||||
r.color_clear_value = {};
|
||||
BindRenderTarget(r.image_id);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,33 +6,24 @@
|
||||
#include "graphics/host_gpu/renderer/renderTarget.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
class RenderCommandBuffer;
|
||||
|
||||
enum class RenderColorType {
|
||||
NoColorOutput,
|
||||
RenderTexture,
|
||||
};
|
||||
|
||||
struct RenderColorInfo {
|
||||
RenderColorType type = RenderColorType::NoColorOutput;
|
||||
// Discovery keeps guest image information but can remap the view into a larger cache image.
|
||||
TextureCache::ImageDesc desc;
|
||||
ImageId image_id;
|
||||
vk::ImageView image_view = nullptr;
|
||||
vk::Format format = vk::Format::eUndefined;
|
||||
vk::Extent2D extent = {};
|
||||
uint32_t base_mip_level = 0;
|
||||
uint32_t base_array_layer = 0;
|
||||
uint64_t base_addr = 0;
|
||||
uint64_t buffer_size = 0;
|
||||
uint32_t target_slot = 0;
|
||||
uint32_t samples = 1;
|
||||
uint32_t guest_mip_level = 0;
|
||||
uint32_t guest_array_layer = 0;
|
||||
Prospero::ColorComponentMapping export_mapping;
|
||||
bool color_clear_enable = false;
|
||||
vk::ClearColorValue color_clear_value {};
|
||||
|
||||
[[nodiscard]] vk::Extent2D Extent() const {
|
||||
return {std::max(desc.info.extent.width >> guest_mip_level, 1u),
|
||||
std::max(desc.info.extent.height >> guest_mip_level, 1u)};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
@@ -1,96 +1,92 @@
|
||||
#include "graphics/host_gpu/renderer/commandScheduler.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <optional>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
static thread_local CommandScheduler* g_deferred_callback_scheduler = nullptr;
|
||||
|
||||
void CommandSlot::Reset() {
|
||||
EXIT_IF(buffer == nullptr);
|
||||
const auto result = buffer.reset(vk::CommandBufferResetFlagBits::eReleaseResources);
|
||||
if (result != vk::Result::eSuccess) {
|
||||
EXIT("failed to reset Vulkan command buffer: %s (%d)\n", VulkanToString(result).c_str(),
|
||||
static_cast<int>(result));
|
||||
}
|
||||
namespace {
|
||||
|
||||
void ReportVulkanFatal(const char* what, vk::Result result, uint64_t tick, uint32_t debug_op,
|
||||
uint64_t debug_submit, uint32_t arg0, uint32_t arg1, uint32_t arg2,
|
||||
uint32_t arg3, uint64_t arg4) {
|
||||
LOGF("%s failed: %s (%d), tick=%" PRIu64 " debug_op=%u debug_submit=%" PRIu64
|
||||
" args=%u,%u,%u,%u,0x%016" PRIx64 "\n",
|
||||
what, vk::to_string(result).c_str(), static_cast<int>(result), tick, debug_op,
|
||||
debug_submit, arg0, arg1, arg2, arg3, arg4);
|
||||
std::printf("%s failed: %s (%d), tick=%" PRIu64 " debug_op=%u debug_submit=%" PRIu64
|
||||
" args=%u,%u,%u,%u,0x%016" PRIx64 "\n",
|
||||
what, vk::to_string(result).c_str(), static_cast<int>(result), tick, debug_op,
|
||||
debug_submit, arg0, arg1, arg2, arg3, arg4);
|
||||
std::fflush(stdout);
|
||||
}
|
||||
|
||||
CommandScheduler::CommandPool::~CommandPool() {
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void CommandScheduler::CommandPool::Create(GraphicContext& graphics) {
|
||||
EXIT_IF(m_pool != nullptr || m_graphics != nullptr ||
|
||||
graphics.queue_family == static_cast<uint32_t>(-1));
|
||||
m_graphics = &graphics;
|
||||
} // namespace
|
||||
|
||||
CommandScheduler::CommandPool::CommandPool(GraphicContext& graphics, MasterSemaphore& master)
|
||||
: m_graphics(graphics), m_master(master) {
|
||||
EXIT_IF(graphics.queue_family == static_cast<uint32_t>(-1));
|
||||
vk::CommandPoolCreateInfo create {};
|
||||
create.sType = vk::StructureType::eCommandPoolCreateInfo;
|
||||
create.queueFamilyIndex = graphics.queue_family;
|
||||
create.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
|
||||
create.flags = vk::CommandPoolCreateFlagBits::eTransient |
|
||||
vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
|
||||
const auto result = graphics.device.createCommandPool(&create, nullptr, &m_pool);
|
||||
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess || m_pool == nullptr);
|
||||
}
|
||||
|
||||
CommandSlot* CommandScheduler::CommandPool::CreateSlot() {
|
||||
EXIT_IF(m_graphics == nullptr);
|
||||
auto& graphics = *m_graphics;
|
||||
CommandScheduler::CommandPool::~CommandPool() {
|
||||
m_graphics.device.destroyCommandPool(m_pool, nullptr);
|
||||
}
|
||||
|
||||
size_t CommandScheduler::CommandPool::Grow() {
|
||||
const auto first = m_ticks.size();
|
||||
m_ticks.resize(first + GrowStep);
|
||||
m_buffers.resize(first + GrowStep);
|
||||
|
||||
vk::CommandBufferAllocateInfo allocate {};
|
||||
allocate.sType = vk::StructureType::eCommandBufferAllocateInfo;
|
||||
allocate.commandPool = m_pool;
|
||||
allocate.level = vk::CommandBufferLevel::ePrimary;
|
||||
allocate.commandBufferCount = 1;
|
||||
vk::CommandBuffer buffer = nullptr;
|
||||
EXIT_IF(graphics.device.allocateCommandBuffers(&allocate, &buffer) != vk::Result::eSuccess);
|
||||
|
||||
vk::FenceCreateInfo fence_create {};
|
||||
fence_create.sType = vk::StructureType::eFenceCreateInfo;
|
||||
fence_create.flags = vk::FenceCreateFlagBits::eSignaled;
|
||||
vk::Fence fence = nullptr;
|
||||
if (graphics.device.createFence(&fence_create, nullptr, &fence) != vk::Result::eSuccess) {
|
||||
graphics.device.freeCommandBuffers(m_pool, 1, &buffer);
|
||||
EXIT("failed to create command-buffer fence\n");
|
||||
}
|
||||
|
||||
auto& slot = m_slots.emplace_back();
|
||||
slot.pool_mutex = &m_mutex;
|
||||
slot.id = static_cast<uint32_t>(m_slots.size() - 1);
|
||||
slot.buffer = buffer;
|
||||
slot.fence = fence;
|
||||
return &slot;
|
||||
allocate.commandBufferCount = static_cast<uint32_t>(GrowStep);
|
||||
EXIT_IF(m_graphics.device.allocateCommandBuffers(&allocate, m_buffers.data() + first) !=
|
||||
vk::Result::eSuccess);
|
||||
return first;
|
||||
}
|
||||
|
||||
CommandSlot* CommandScheduler::CommandPool::Allocate(GraphicContext& graphics) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
if (m_pool == nullptr) {
|
||||
Create(graphics);
|
||||
}
|
||||
EXIT_IF(m_graphics != &graphics);
|
||||
auto found = std::ranges::find_if(m_slots, [](const auto& slot) { return !slot.busy; });
|
||||
auto* slot = found != m_slots.end() ? &*found : CreateSlot();
|
||||
slot->busy = true;
|
||||
slot->Reset();
|
||||
return slot;
|
||||
}
|
||||
vk::CommandBuffer CommandScheduler::CommandPool::Commit() {
|
||||
auto gpu_tick = m_master.KnownGpuTick();
|
||||
const auto search = [this, &gpu_tick](size_t begin, size_t end) -> std::optional<size_t> {
|
||||
for (size_t index = begin; index < end; ++index) {
|
||||
if (gpu_tick >= m_ticks[index]) {
|
||||
m_ticks[index] = m_master.CurrentTick();
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
};
|
||||
|
||||
void CommandScheduler::CommandPool::Destroy() {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
if (m_pool == nullptr) {
|
||||
return;
|
||||
auto found = search(m_hint, m_ticks.size());
|
||||
if (!found) {
|
||||
m_master.Refresh();
|
||||
gpu_tick = m_master.KnownGpuTick();
|
||||
found = search(m_hint, m_ticks.size());
|
||||
}
|
||||
EXIT_IF(std::ranges::any_of(m_slots, [](const auto& slot) { return slot.busy; }));
|
||||
EXIT_IF(m_graphics == nullptr);
|
||||
for (const auto& slot: m_slots) {
|
||||
m_graphics->device.destroyFence(slot.fence, nullptr);
|
||||
if (!found) {
|
||||
found = search(0, m_hint);
|
||||
}
|
||||
m_graphics->device.destroyCommandPool(m_pool, nullptr);
|
||||
m_slots.clear();
|
||||
m_pool = nullptr;
|
||||
m_graphics = nullptr;
|
||||
if (!found) {
|
||||
found = Grow();
|
||||
m_ticks[*found] = m_master.CurrentTick();
|
||||
}
|
||||
|
||||
m_hint = (*found + 1) % m_ticks.size();
|
||||
return m_buffers[*found];
|
||||
}
|
||||
|
||||
bool CommandScheduler::InDeferredOperation() noexcept {
|
||||
@@ -99,10 +95,8 @@ bool CommandScheduler::InDeferredOperation() noexcept {
|
||||
|
||||
CommandScheduler::CommandScheduler(RenderContext& context, GraphicContext& graphics)
|
||||
: m_master(graphics), m_context(context), m_graphics(graphics),
|
||||
m_priority_thread([this](std::stop_token stop) { PriorityOperationsThread(stop); }) {
|
||||
m_buffers.reserve(CommandBufferGrowStep);
|
||||
m_buffer_ticks.reserve(CommandBufferGrowStep);
|
||||
}
|
||||
m_command_pool(graphics, m_master), m_command(*this),
|
||||
m_priority_thread([this](std::stop_token stop) { PriorityOperationsThread(stop); }) {}
|
||||
|
||||
CommandScheduler::~CommandScheduler() {
|
||||
Shutdown();
|
||||
@@ -128,9 +122,11 @@ void CommandScheduler::Shutdown() {
|
||||
}
|
||||
m_operation_state = OperationState::Draining;
|
||||
}
|
||||
if (Active() && m_recording) {
|
||||
Finish();
|
||||
if (!m_command.IsInvalid()) {
|
||||
Submit();
|
||||
}
|
||||
m_master.Wait(CurrentTick() - 1);
|
||||
PopPendingOperations();
|
||||
DrainPriorityOperations();
|
||||
m_priority_thread.request_stop();
|
||||
m_operation_available.notify_all();
|
||||
@@ -152,18 +148,10 @@ void CommandScheduler::Begin(HW::Context& registers, HW::UserConfig& user_config
|
||||
std::lock_guard lock(m_operation_mutex);
|
||||
EXIT_IF(m_operation_state != OperationState::Open);
|
||||
}
|
||||
m_registers = ®isters;
|
||||
m_user_config = &user_config;
|
||||
m_shaders = &shaders;
|
||||
m_command.Bind(registers, user_config, shaders);
|
||||
|
||||
if (!Active()) {
|
||||
m_current = static_cast<int>(GrowCommandBuffers());
|
||||
}
|
||||
|
||||
BindCurrent();
|
||||
if (!m_recording) {
|
||||
Current().Begin();
|
||||
m_recording = true;
|
||||
if (m_command.IsInvalid()) {
|
||||
BeginNext();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +160,7 @@ void CommandScheduler::BeginRendering(const RenderState& state) {
|
||||
}
|
||||
|
||||
void CommandScheduler::EndRendering() {
|
||||
if (Active() && m_recording) {
|
||||
if (Active() && !m_command.IsInvalid()) {
|
||||
Current().EndRendering();
|
||||
}
|
||||
}
|
||||
@@ -183,77 +171,57 @@ void CommandScheduler::Flush() {
|
||||
}
|
||||
|
||||
void CommandScheduler::Flush(SubmitInfo& submit) {
|
||||
SubmitCurrent(submit);
|
||||
Submit(submit);
|
||||
BeginNext();
|
||||
}
|
||||
|
||||
CommandBuffer& CommandScheduler::FlushAndGetSubmitted() {
|
||||
SubmitInfo submit;
|
||||
auto& submitted = SubmitCurrent(submit);
|
||||
void CommandScheduler::FlushAndWait() {
|
||||
const auto tick = Submit();
|
||||
m_master.Wait(tick);
|
||||
BeginNext();
|
||||
return submitted;
|
||||
}
|
||||
|
||||
void CommandScheduler::Finish() {
|
||||
CheckActive();
|
||||
const auto tick = CurrentTick();
|
||||
if (m_recording) {
|
||||
SubmitInfo submit;
|
||||
SubmitCurrent(submit);
|
||||
if (!m_command.IsInvalid()) {
|
||||
Submit();
|
||||
}
|
||||
for (auto& buffer: m_buffers) {
|
||||
buffer->WaitForFenceAndReset();
|
||||
}
|
||||
m_master.Wait(tick);
|
||||
m_master.Wait(CurrentTick() - 1);
|
||||
BeginNext();
|
||||
PopPendingOperations();
|
||||
BindCurrent();
|
||||
Current().Begin();
|
||||
m_recording = true;
|
||||
}
|
||||
|
||||
void CommandScheduler::FinishCurrent() {
|
||||
SubmitInfo submit;
|
||||
auto& submitted = SubmitCurrent(submit);
|
||||
submitted.WaitForFenceAndReset();
|
||||
m_master.Refresh();
|
||||
PopPendingOperations();
|
||||
submitted.Begin();
|
||||
m_recording = true;
|
||||
}
|
||||
|
||||
void CommandScheduler::Wait(uint64_t tick) {
|
||||
CheckActive();
|
||||
EXIT_IF(tick > CurrentTick());
|
||||
if (tick >= CurrentTick()) {
|
||||
if (tick == CurrentTick()) {
|
||||
CheckActive();
|
||||
// A stream-buffer wrap can wait while a draw is being prepared through a reference to
|
||||
// Current(). Recycle the same command object so that reference remains valid.
|
||||
FinishCurrent();
|
||||
return;
|
||||
// Current(). The wrapper stays stable while its pooled Vulkan buffer is retired. Deferred
|
||||
// resources are released only at the next GPU operation boundary.
|
||||
const auto submitted_tick = Submit();
|
||||
EXIT_IF(submitted_tick != tick);
|
||||
m_master.Wait(tick);
|
||||
BeginNext();
|
||||
} else {
|
||||
m_master.Wait(tick);
|
||||
}
|
||||
m_master.Wait(tick);
|
||||
PopPendingOperations();
|
||||
}
|
||||
|
||||
void CommandScheduler::PopPendingOperations() {
|
||||
PopPendingOperations(true);
|
||||
}
|
||||
|
||||
void CommandScheduler::PopPendingOperations(bool refresh_gpu_tick) {
|
||||
if (refresh_gpu_tick) {
|
||||
m_master.Refresh();
|
||||
}
|
||||
m_master.Refresh();
|
||||
for (;;) {
|
||||
Common::UniqueFunction<void> callback;
|
||||
PendingOperation operation;
|
||||
{
|
||||
std::lock_guard lock(m_operation_mutex);
|
||||
if (m_pending_operations.empty() ||
|
||||
!m_master.IsFree(m_pending_operations.front().tick)) {
|
||||
return;
|
||||
}
|
||||
callback = std::move(m_pending_operations.front().callback);
|
||||
operation = std::move(m_pending_operations.front());
|
||||
m_pending_operations.pop();
|
||||
}
|
||||
RunOperation(std::move(callback));
|
||||
WaitPriorityOperations(operation.tick);
|
||||
RunOperation(std::move(operation.callback));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,86 +327,73 @@ bool CommandScheduler::IsFree(uint64_t tick) {
|
||||
return m_master.IsFree(tick);
|
||||
}
|
||||
|
||||
CommandSlot* CommandScheduler::AllocateCommandBuffer() {
|
||||
return m_command_pool.Allocate(m_graphics);
|
||||
}
|
||||
|
||||
uint64_t CommandScheduler::NextSubmitSequence() noexcept {
|
||||
return m_submit_sequence.fetch_add(1, std::memory_order_relaxed) + 1;
|
||||
}
|
||||
|
||||
void CommandScheduler::CheckActive() const {
|
||||
EXIT_IF(!Active() || static_cast<size_t>(m_current) >= m_buffers.size());
|
||||
EXIT_IF(!Active());
|
||||
}
|
||||
|
||||
RenderCommandBuffer& CommandScheduler::Current() const {
|
||||
CommandBuffer& CommandScheduler::Current() {
|
||||
CheckActive();
|
||||
EXIT_IF(m_buffers[m_current] == nullptr);
|
||||
return *m_buffers[m_current];
|
||||
return m_command;
|
||||
}
|
||||
|
||||
void CommandScheduler::BindCurrent() const {
|
||||
EXIT_IF(m_registers == nullptr || m_user_config == nullptr || m_shaders == nullptr);
|
||||
Current().Bind(*m_registers, *m_user_config, *m_shaders);
|
||||
CommandBuffer& CommandScheduler::BeginCommand() {
|
||||
EXIT_IF(!m_command.IsInvalid());
|
||||
m_command.m_buffer = m_command_pool.Commit();
|
||||
m_command.Begin();
|
||||
return m_command;
|
||||
}
|
||||
|
||||
CommandBuffer& CommandScheduler::SubmitCurrent(SubmitInfo& submit) {
|
||||
CheckActive();
|
||||
EXIT_IF(!m_recording);
|
||||
auto& submitted = Current();
|
||||
submitted.End();
|
||||
const auto signal_tick = m_master.NextTick();
|
||||
submit.AddSignal(m_master.Handle(), signal_tick);
|
||||
submitted.Execute(submit);
|
||||
m_buffer_ticks[static_cast<size_t>(m_current)] = signal_tick;
|
||||
m_recording = false;
|
||||
return submitted;
|
||||
}
|
||||
uint64_t CommandScheduler::Submit(SubmitInfo submit) {
|
||||
EXIT_IF(m_command.IsInvalid());
|
||||
EXIT_IF(submit.num_wait_semaphores > SubmitInfo::MaxSemaphores ||
|
||||
submit.num_signal_semaphores >= SubmitInfo::MaxSemaphores);
|
||||
|
||||
int CommandScheduler::FindReusableBuffer(uint64_t gpu_tick) const {
|
||||
EXIT_IF(m_buffer_ticks.size() != m_buffers.size());
|
||||
const auto buffer_count = m_buffers.size();
|
||||
for (size_t offset = 1; offset <= buffer_count; ++offset) {
|
||||
const auto candidate = (static_cast<size_t>(m_current) + offset) % buffer_count;
|
||||
if (gpu_tick >= m_buffer_ticks[candidate]) {
|
||||
return static_cast<int>(candidate);
|
||||
}
|
||||
m_command.End();
|
||||
const auto buffer = m_command.m_buffer;
|
||||
auto& graphics = m_graphics;
|
||||
EXIT_IF(graphics.queue == nullptr);
|
||||
|
||||
vk::Result result;
|
||||
uint64_t tick;
|
||||
{
|
||||
Common::LockGuard lock(graphics.queue_mutex);
|
||||
tick = m_master.NextTick();
|
||||
submit.AddSignal(m_master.Handle(), tick);
|
||||
|
||||
vk::TimelineSemaphoreSubmitInfo timeline_info {};
|
||||
timeline_info.waitSemaphoreValueCount = submit.num_wait_semaphores;
|
||||
timeline_info.pWaitSemaphoreValues = submit.wait_ticks.data();
|
||||
timeline_info.signalSemaphoreValueCount = submit.num_signal_semaphores;
|
||||
timeline_info.pSignalSemaphoreValues = submit.signal_ticks.data();
|
||||
|
||||
vk::SubmitInfo submit_info {};
|
||||
submit_info.pNext = &timeline_info;
|
||||
submit_info.waitSemaphoreCount = submit.num_wait_semaphores;
|
||||
submit_info.pWaitSemaphores = submit.wait_semaphores.data();
|
||||
submit_info.pWaitDstStageMask = submit.wait_stages.data();
|
||||
submit_info.commandBufferCount = 1;
|
||||
submit_info.pCommandBuffers = &buffer;
|
||||
submit_info.signalSemaphoreCount = submit.num_signal_semaphores;
|
||||
submit_info.pSignalSemaphores = submit.signal_semaphores.data();
|
||||
|
||||
result = graphics.queue.submit(1, &submit_info, nullptr);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t CommandScheduler::GrowCommandBuffers() {
|
||||
const auto first = m_buffers.size();
|
||||
const auto end = first + CommandBufferGrowStep;
|
||||
m_buffers.reserve(end);
|
||||
m_buffer_ticks.reserve(end);
|
||||
for (size_t i = first; i < end; ++i) {
|
||||
m_buffers.emplace_back(std::make_unique<RenderCommandBuffer>(*this));
|
||||
m_buffer_ticks.push_back(0);
|
||||
if (result != vk::Result::eSuccess) {
|
||||
ReportVulkanFatal("vkQueueSubmit", result, tick, m_command.m_debug_op,
|
||||
m_command.m_debug_submit_id, m_command.m_debug_arg0,
|
||||
m_command.m_debug_arg1, m_command.m_debug_arg2, m_command.m_debug_arg3,
|
||||
m_command.m_debug_arg4);
|
||||
}
|
||||
return first;
|
||||
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
||||
|
||||
m_command.m_buffer = nullptr;
|
||||
return tick;
|
||||
}
|
||||
|
||||
void CommandScheduler::BeginNext() {
|
||||
EXIT_IF(m_recording);
|
||||
|
||||
auto candidate = FindReusableBuffer(m_master.KnownGpuTick());
|
||||
bool refreshed = false;
|
||||
if (candidate < 0) {
|
||||
m_master.Refresh();
|
||||
refreshed = true;
|
||||
candidate = FindReusableBuffer(m_master.KnownGpuTick());
|
||||
}
|
||||
if (candidate < 0) {
|
||||
candidate = static_cast<int>(GrowCommandBuffers());
|
||||
}
|
||||
|
||||
m_current = candidate;
|
||||
Current().WaitForFenceAndReset();
|
||||
PopPendingOperations(!refreshed);
|
||||
BindCurrent();
|
||||
Current().Begin();
|
||||
m_recording = true;
|
||||
CheckActive();
|
||||
BeginCommand();
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
@@ -6,11 +6,7 @@
|
||||
#include "graphics/host_gpu/renderer/masterSemaphore.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include <queue>
|
||||
@@ -20,16 +16,6 @@
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
struct CommandSlot {
|
||||
Common::Mutex* pool_mutex = nullptr;
|
||||
uint32_t id = 0;
|
||||
vk::CommandBuffer buffer = nullptr;
|
||||
vk::Fence fence = nullptr;
|
||||
bool busy = false;
|
||||
|
||||
void Reset();
|
||||
};
|
||||
|
||||
class CommandScheduler {
|
||||
public:
|
||||
CommandScheduler(RenderContext& context, GraphicContext& graphics);
|
||||
@@ -41,9 +27,10 @@ public:
|
||||
void EndRendering();
|
||||
void Flush();
|
||||
void Flush(SubmitInfo& submit);
|
||||
CommandBuffer& FlushAndGetSubmitted();
|
||||
void FlushAndWait();
|
||||
void Finish();
|
||||
void FinishCurrent();
|
||||
CommandBuffer& BeginCommand();
|
||||
uint64_t Submit(SubmitInfo submit = {});
|
||||
// Deferred callbacks can observe an externally owned drain, but cannot initiate shutdown:
|
||||
// the priority runner cannot join itself.
|
||||
void Shutdown();
|
||||
@@ -55,34 +42,35 @@ public:
|
||||
void DeferPriorityOperation(Common::UniqueFunction<void>&& operation);
|
||||
[[nodiscard]] static bool InDeferredOperation() noexcept;
|
||||
|
||||
[[nodiscard]] bool Active() const noexcept { return m_current >= 0; }
|
||||
void CheckActive() const;
|
||||
RenderCommandBuffer& Current() const;
|
||||
[[nodiscard]] uint64_t CurrentTick() const noexcept { return m_master.CurrentTick(); }
|
||||
[[nodiscard]] bool IsFree(uint64_t tick);
|
||||
[[nodiscard]] RenderContext& Context() const noexcept { return m_context; }
|
||||
[[nodiscard]] GraphicContext& Graphics() const noexcept { return m_graphics; }
|
||||
[[nodiscard]] bool Active() const noexcept { return m_command.m_registers != nullptr; }
|
||||
void CheckActive() const;
|
||||
CommandBuffer& Current();
|
||||
[[nodiscard]] uint64_t CurrentTick() const noexcept { return m_master.CurrentTick(); }
|
||||
[[nodiscard]] bool IsFree(uint64_t tick);
|
||||
[[nodiscard]] MasterSemaphore& GetMasterSemaphore() noexcept { return m_master; }
|
||||
[[nodiscard]] RenderContext& Context() const noexcept { return m_context; }
|
||||
[[nodiscard]] GraphicContext& Graphics() const noexcept { return m_graphics; }
|
||||
|
||||
private:
|
||||
static constexpr size_t CommandBufferGrowStep = 4;
|
||||
|
||||
class CommandPool {
|
||||
public:
|
||||
CommandPool() = default;
|
||||
CommandPool(GraphicContext& graphics, MasterSemaphore& master);
|
||||
~CommandPool();
|
||||
KYTY_CLASS_NO_COPY(CommandPool);
|
||||
|
||||
CommandSlot* Allocate(GraphicContext& graphics);
|
||||
vk::CommandBuffer Commit();
|
||||
|
||||
private:
|
||||
void Create(GraphicContext& graphics);
|
||||
CommandSlot* CreateSlot();
|
||||
void Destroy();
|
||||
static constexpr size_t GrowStep = 4;
|
||||
|
||||
GraphicContext* m_graphics = nullptr;
|
||||
Common::Mutex m_mutex;
|
||||
vk::CommandPool m_pool = nullptr;
|
||||
std::deque<CommandSlot> m_slots;
|
||||
size_t Grow();
|
||||
|
||||
GraphicContext& m_graphics;
|
||||
MasterSemaphore& m_master;
|
||||
vk::CommandPool m_pool = nullptr;
|
||||
std::vector<vk::CommandBuffer> m_buffers;
|
||||
std::vector<uint64_t> m_ticks;
|
||||
size_t m_hint = 0;
|
||||
};
|
||||
|
||||
enum class OperationState { Open, Draining, Closed };
|
||||
@@ -92,39 +80,23 @@ private:
|
||||
uint64_t tick = 0;
|
||||
};
|
||||
|
||||
void BindCurrent() const;
|
||||
CommandBuffer& SubmitCurrent(SubmitInfo& submit);
|
||||
void BeginNext();
|
||||
void PopPendingOperations(bool refresh_gpu_tick);
|
||||
[[nodiscard]] int FindReusableBuffer(uint64_t gpu_tick) const;
|
||||
[[nodiscard]] size_t GrowCommandBuffers();
|
||||
void PriorityOperationsThread(std::stop_token stop);
|
||||
void RunOperation(Common::UniqueFunction<void>&& operation);
|
||||
[[nodiscard]] CommandSlot* AllocateCommandBuffer();
|
||||
[[nodiscard]] uint64_t NextSubmitSequence() noexcept;
|
||||
void BeginNext();
|
||||
void PriorityOperationsThread(std::stop_token stop);
|
||||
void RunOperation(Common::UniqueFunction<void>&& operation);
|
||||
|
||||
MasterSemaphore m_master;
|
||||
RenderContext& m_context;
|
||||
GraphicContext& m_graphics;
|
||||
CommandPool m_command_pool;
|
||||
std::vector<std::unique_ptr<RenderCommandBuffer>> m_buffers;
|
||||
std::vector<uint64_t> m_buffer_ticks;
|
||||
std::queue<PendingOperation> m_pending_operations;
|
||||
std::queue<PendingOperation> m_priority_operations;
|
||||
std::mutex m_operation_mutex;
|
||||
std::condition_variable m_operation_available;
|
||||
std::jthread m_priority_thread;
|
||||
bool m_priority_active = false;
|
||||
uint64_t m_priority_active_tick = 0;
|
||||
OperationState m_operation_state = OperationState::Open;
|
||||
int m_current = -1;
|
||||
bool m_recording = false;
|
||||
HW::Context* m_registers = nullptr;
|
||||
HW::UserConfig* m_user_config = nullptr;
|
||||
HW::Shader* m_shaders = nullptr;
|
||||
std::atomic<uint64_t> m_submit_sequence = 0;
|
||||
|
||||
friend class CommandBuffer;
|
||||
MasterSemaphore m_master;
|
||||
RenderContext& m_context;
|
||||
GraphicContext& m_graphics;
|
||||
CommandPool m_command_pool;
|
||||
CommandBuffer m_command;
|
||||
std::queue<PendingOperation> m_pending_operations;
|
||||
std::queue<PendingOperation> m_priority_operations;
|
||||
std::mutex m_operation_mutex;
|
||||
std::condition_variable m_operation_available;
|
||||
std::jthread m_priority_thread;
|
||||
bool m_priority_active = false;
|
||||
uint64_t m_priority_active_tick = 0;
|
||||
OperationState m_operation_state = OperationState::Open;
|
||||
};
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common.h"
|
||||
#include "common/emulatorConfig.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/profiler.h"
|
||||
#include "common/threads.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
@@ -9,108 +7,33 @@
|
||||
#include "graphics/host_gpu/renderer/debug.h"
|
||||
#include "graphics/host_gpu/renderer/depthRenderTarget.h"
|
||||
#include "graphics/host_gpu/renderer/image/imageView.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/host_gpu/vma.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
namespace Libs::Graphics {
|
||||
|
||||
FenceResourceRetainer::~FenceResourceRetainer() {
|
||||
if (!m_resources.empty()) {
|
||||
EXIT("fence resource retainer destroyed before release\n");
|
||||
}
|
||||
}
|
||||
|
||||
void FenceResourceRetainer::Retain(std::shared_ptr<void> resource) {
|
||||
if (resource == nullptr) {
|
||||
EXIT("cannot retain a null fence resource\n");
|
||||
}
|
||||
if (std::ranges::none_of(m_resources, [&resource](const auto& retained) {
|
||||
return retained.get() == resource.get();
|
||||
})) {
|
||||
m_resources.push_back(std::move(resource));
|
||||
}
|
||||
}
|
||||
|
||||
void FenceResourceRetainer::ReleaseAfterFence() noexcept {
|
||||
m_resources.clear();
|
||||
}
|
||||
|
||||
CommandBuffer::CommandBuffer(CommandScheduler& scheduler)
|
||||
: m_context(scheduler.Context()), m_scheduler(scheduler), m_graphics(scheduler.Graphics()),
|
||||
m_slot(scheduler.AllocateCommandBuffer()) {}
|
||||
|
||||
CommandBuffer::~CommandBuffer() {
|
||||
Release();
|
||||
}
|
||||
: m_context(scheduler.Context()), m_graphics(scheduler.Graphics()) {}
|
||||
|
||||
bool CommandBuffer::IsInvalid() const {
|
||||
return m_slot == nullptr;
|
||||
return m_buffer == nullptr;
|
||||
}
|
||||
|
||||
vk::CommandBuffer CommandBuffer::Handle() const {
|
||||
EXIT_IF(IsInvalid());
|
||||
|
||||
const auto handle = m_slot->buffer;
|
||||
EXIT_IF(handle == nullptr);
|
||||
return handle;
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
void CommandBuffer::Release() {
|
||||
EXIT_IF(IsInvalid());
|
||||
|
||||
Common::LockGuard lock(*m_slot->pool_mutex);
|
||||
|
||||
WaitForFence();
|
||||
|
||||
m_slot->busy = false;
|
||||
m_slot->Reset();
|
||||
ReleaseResourcesAfterFence();
|
||||
m_slot = nullptr;
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(!IsInvalid());
|
||||
}
|
||||
|
||||
void CommandBuffer::RetireBufferAfterFence(std::unique_ptr<VulkanBuffer> buffer) {
|
||||
if (IsInvalid() || m_execute || buffer == nullptr || buffer->buffer == nullptr) {
|
||||
EXIT("cannot retire a buffer on an invalid or submitted command buffer\n");
|
||||
}
|
||||
m_retired_buffers.push_back(std::move(buffer));
|
||||
}
|
||||
|
||||
void CommandBuffer::RetainResourceUntilFence(std::shared_ptr<void> resource) {
|
||||
if (IsInvalid() || m_execute) {
|
||||
EXIT("cannot retain a resource on an invalid or submitted command buffer\n");
|
||||
}
|
||||
m_fence_resources.Retain(std::move(resource));
|
||||
}
|
||||
|
||||
void CommandBuffer::RecycleDescriptorAfterFence(VulkanDescriptorSet& set) {
|
||||
m_descriptor_sets_after_fence.push_back(&set);
|
||||
}
|
||||
|
||||
void CommandBuffer::RecycleDescriptorsAfterFence() {
|
||||
for (auto* set: m_descriptor_sets_after_fence) {
|
||||
m_context.GetDescriptorCache().Recycle(*set);
|
||||
}
|
||||
m_descriptor_sets_after_fence.clear();
|
||||
}
|
||||
|
||||
void CommandBuffer::Begin() const {
|
||||
EXIT_IF(m_rendering);
|
||||
void CommandBuffer::Begin() {
|
||||
EXIT_IF(m_rendering || IsInvalid());
|
||||
auto buffer = Handle();
|
||||
|
||||
vk::CommandBufferBeginInfo begin_info {};
|
||||
begin_info.sType = vk::StructureType::eCommandBufferBeginInfo;
|
||||
begin_info.pNext = nullptr;
|
||||
begin_info.flags = {};
|
||||
begin_info.pInheritanceInfo = nullptr;
|
||||
begin_info.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
|
||||
|
||||
auto result = buffer.begin(&begin_info);
|
||||
|
||||
@@ -137,137 +60,17 @@ void CommandBuffer::SetDebugInfo(uint32_t op, uint64_t submit_id, uint32_t arg0,
|
||||
m_debug_arg4 = arg4;
|
||||
}
|
||||
|
||||
void CommandBuffer::Execute(const SubmitInfo& submit) {
|
||||
EXIT_IF(IsInvalid());
|
||||
EXIT_IF(m_execute);
|
||||
EXIT_IF(submit.num_wait_semaphores > SubmitInfo::MaxSemaphores ||
|
||||
submit.num_signal_semaphores > SubmitInfo::MaxSemaphores);
|
||||
|
||||
auto buffer = Handle();
|
||||
auto fence = m_slot->fence;
|
||||
|
||||
vk::TimelineSemaphoreSubmitInfo timeline_info {};
|
||||
timeline_info.sType = vk::StructureType::eTimelineSemaphoreSubmitInfo;
|
||||
timeline_info.waitSemaphoreValueCount = submit.num_wait_semaphores;
|
||||
timeline_info.pWaitSemaphoreValues = submit.wait_ticks.data();
|
||||
timeline_info.signalSemaphoreValueCount = submit.num_signal_semaphores;
|
||||
timeline_info.pSignalSemaphoreValues = submit.signal_ticks.data();
|
||||
|
||||
vk::SubmitInfo submit_info {};
|
||||
submit_info.sType = vk::StructureType::eSubmitInfo;
|
||||
submit_info.pNext = &timeline_info;
|
||||
submit_info.waitSemaphoreCount = submit.num_wait_semaphores;
|
||||
submit_info.pWaitSemaphores = submit.wait_semaphores.data();
|
||||
submit_info.pWaitDstStageMask = submit.wait_stages.data();
|
||||
submit_info.commandBufferCount = 1;
|
||||
submit_info.pCommandBuffers = &buffer;
|
||||
submit_info.signalSemaphoreCount = submit.num_signal_semaphores;
|
||||
submit_info.pSignalSemaphores = submit.signal_semaphores.data();
|
||||
|
||||
auto& graphics = m_graphics;
|
||||
EXIT_IF(graphics.queue == nullptr);
|
||||
|
||||
auto result = graphics.device.resetFences(1, &fence);
|
||||
if (result != vk::Result::eSuccess) {
|
||||
LOGF("vkResetFences failed before submit: %s (%d)\n", VulkanToString(result).c_str(),
|
||||
static_cast<int>(result));
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
||||
|
||||
if (Config::GraphicsDebugDumpEnabled()) {
|
||||
LOGF("vkQueueSubmit begin: slot=%u waits=%u signals=%u debug_op=%u debug_submit=%" PRIu64
|
||||
" args=%u,%u,%u,%u,0x%016" PRIx64 "\n",
|
||||
m_slot->id, submit.num_wait_semaphores, submit.num_signal_semaphores, m_debug_op,
|
||||
m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3,
|
||||
m_debug_arg4);
|
||||
}
|
||||
|
||||
{
|
||||
Common::LockGuard lock(graphics.queue_mutex);
|
||||
m_submit_seq = m_scheduler.NextSubmitSequence();
|
||||
result = graphics.queue.submit(1, &submit_info, fence);
|
||||
}
|
||||
|
||||
m_execute = true;
|
||||
m_fence_waited = false;
|
||||
|
||||
if (result != vk::Result::eSuccess) {
|
||||
LOGF("vkQueueSubmit failed: %s (%d), slot=%u submit_seq=%" PRIu64
|
||||
" debug_op=%u debug_submit=%" PRIu64 " args=%u,%u,%u,%u,0x%016" PRIx64 "\n",
|
||||
VulkanToString(result).c_str(), static_cast<int>(result), m_slot->id, m_submit_seq,
|
||||
m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3,
|
||||
m_debug_arg4);
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
||||
}
|
||||
|
||||
void CommandBuffer::WaitForFence() {
|
||||
FinalizeFence(false);
|
||||
}
|
||||
|
||||
void CommandBuffer::WaitForFenceOnly() {
|
||||
EXIT_IF(IsInvalid());
|
||||
if (!m_execute || m_fence_waited) {
|
||||
return;
|
||||
}
|
||||
auto device = m_graphics.device;
|
||||
auto result = device.waitForFences(1, &m_slot->fence, VK_TRUE, UINT64_MAX);
|
||||
if (result != vk::Result::eSuccess) {
|
||||
LOGF("vkWaitForFences failed: %s (%d), slot=%u submit_seq=%" PRIu64
|
||||
" debug_op=%u debug_submit=%" PRIu64 " args=%u,%u,%u,%u,0x%016" PRIx64 "\n",
|
||||
VulkanToString(result).c_str(), static_cast<int>(result), m_slot->id, m_submit_seq,
|
||||
m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3,
|
||||
m_debug_arg4);
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
||||
m_fence_waited = true;
|
||||
}
|
||||
|
||||
void CommandBuffer::WaitForFenceAndReset() {
|
||||
FinalizeFence(true);
|
||||
}
|
||||
|
||||
void CommandBuffer::FinalizeFence(bool reset_recording) {
|
||||
const bool was_executed = m_execute;
|
||||
WaitForFenceOnly();
|
||||
if (was_executed) {
|
||||
m_execute = false;
|
||||
m_fence_waited = false;
|
||||
if (reset_recording) {
|
||||
Common::LockGuard lock(*m_slot->pool_mutex);
|
||||
m_slot->Reset();
|
||||
}
|
||||
}
|
||||
if (was_executed) {
|
||||
ReleaseResourcesAfterFence();
|
||||
}
|
||||
DeleteBuffersAfterFence();
|
||||
}
|
||||
|
||||
void CommandBuffer::ReleaseResourcesAfterFence() {
|
||||
RecycleDescriptorsAfterFence();
|
||||
m_fence_resources.ReleaseAfterFence();
|
||||
}
|
||||
|
||||
void CommandBuffer::DeleteBuffersAfterFence() {
|
||||
for (const auto& buffer: m_retired_buffers) {
|
||||
m_graphics.DeleteBuffer(*buffer);
|
||||
}
|
||||
m_retired_buffers.clear();
|
||||
}
|
||||
|
||||
void CommandBuffer::BeginRendering(const RenderState& state) const {
|
||||
EXIT_IF(state.width == 0 || state.height == 0 || state.num_layers == 0 ||
|
||||
state.num_color_attachments > RENDER_COLOR_ATTACHMENTS_MAX);
|
||||
if (m_rendering && m_render_state == state) {
|
||||
return;
|
||||
}
|
||||
EXIT_IF(state.width == 0 || state.height == 0 || state.num_layers == 0 ||
|
||||
state.num_color_attachments > RENDER_COLOR_ATTACHMENTS_MAX);
|
||||
EndRendering();
|
||||
|
||||
std::array<vk::RenderingAttachmentInfo, RENDER_COLOR_ATTACHMENTS_MAX> colors {};
|
||||
for (uint32_t i = 0; i < state.num_color_attachments; i++) {
|
||||
const auto& attachment = state.color_attachments[i];
|
||||
colors[i].sType = vk::StructureType::eRenderingAttachmentInfo;
|
||||
colors[i].imageView = attachment.image_view;
|
||||
colors[i].imageLayout = attachment.image_layout;
|
||||
colors[i].loadOp =
|
||||
@@ -278,7 +81,6 @@ void CommandBuffer::BeginRendering(const RenderState& state) const {
|
||||
|
||||
const auto& depth_stencil = state.depth_stencil_attachment;
|
||||
vk::RenderingAttachmentInfo depth {};
|
||||
depth.sType = vk::StructureType::eRenderingAttachmentInfo;
|
||||
depth.imageView = depth_stencil.image_view;
|
||||
depth.imageLayout = depth_stencil.image_layout;
|
||||
depth.loadOp =
|
||||
@@ -287,7 +89,6 @@ void CommandBuffer::BeginRendering(const RenderState& state) const {
|
||||
depth.clearValue.depthStencil.depth = std::bit_cast<float>(depth_stencil.clear_value[0]);
|
||||
|
||||
vk::RenderingAttachmentInfo stencil {};
|
||||
stencil.sType = vk::StructureType::eRenderingAttachmentInfo;
|
||||
stencil.imageView = depth_stencil.image_view;
|
||||
stencil.imageLayout = depth_stencil.image_layout;
|
||||
stencil.loadOp =
|
||||
@@ -296,7 +97,6 @@ void CommandBuffer::BeginRendering(const RenderState& state) const {
|
||||
stencil.clearValue.depthStencil.stencil = depth_stencil.clear_value[1];
|
||||
|
||||
vk::RenderingInfo rendering {};
|
||||
rendering.sType = vk::StructureType::eRenderingInfo;
|
||||
rendering.renderArea.extent = {state.width, state.height};
|
||||
rendering.layerCount = state.num_layers;
|
||||
rendering.colorAttachmentCount = state.num_color_attachments;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "common/common.h"
|
||||
#include "common/emulatorConfig.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "graphics/guest_gpu/gpu_defs.h"
|
||||
#include "graphics/guest_gpu/hardwareContext.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
@@ -17,8 +16,6 @@
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
static std::atomic<uint32_t> g_scissor_default_log_count = 0;
|
||||
|
||||
uint32_t render_target_mask_slot(uint32_t mask, uint32_t slot) {
|
||||
return (mask >> (slot * 4u)) & 0x0fu;
|
||||
}
|
||||
@@ -27,7 +24,7 @@ static bool RenderTargetMaskHasMrt(uint32_t mask) {
|
||||
return (mask & ~0x0fu) != 0;
|
||||
}
|
||||
|
||||
static bool RenderTargetMaskHasBoundMrt(const RenderCommandBuffer& buffer) {
|
||||
static bool RenderTargetMaskHasBoundMrt(const CommandBuffer& buffer) {
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
const auto mask = hw.GetRenderTargetMask();
|
||||
|
||||
@@ -45,7 +42,7 @@ static bool RenderTargetMaskHasBoundMrt(const RenderCommandBuffer& buffer) {
|
||||
return bound_targets > 1;
|
||||
}
|
||||
|
||||
uint32_t render_target_first_bound_slot(const RenderCommandBuffer& buffer) {
|
||||
uint32_t render_target_first_bound_slot(const CommandBuffer& buffer) {
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
const auto mask = hw.GetRenderTargetMask();
|
||||
for (uint32_t i = 0; i < 8; i++) {
|
||||
@@ -59,7 +56,7 @@ uint32_t render_target_first_bound_slot(const RenderCommandBuffer& buffer) {
|
||||
|
||||
bool graphics_debug_dump_enabled() {
|
||||
return Config::GraphicsDebugDumpEnabled() &&
|
||||
Config::GetPrintfDirection() != Config::OutputDirection::Silent;
|
||||
Config::GetPrintfDirection() != Config::LogDirection::Silent;
|
||||
}
|
||||
|
||||
void uc_print(const char* func, const HW::UserConfig& uc) {
|
||||
@@ -71,163 +68,115 @@ void uc_print(const char* func, const HW::UserConfig& uc) {
|
||||
LOGF("\t GetPrimType() = 0x%08" PRIx32 "\n"
|
||||
"\t GetIndexOffset() = 0x%08" PRIx32 "\n"
|
||||
"\t GetObjectId() = 0x%08" PRIx32 "\n"
|
||||
"\t primitive_reset = 0x%08" PRIx32 "\n"
|
||||
"\t primitive_group_size = 0x%04" PRIx16 "\n"
|
||||
"\t vertex_group_size = 0x%04" PRIx16 "\n"
|
||||
"\t en_user_vgpr1 = %s\n"
|
||||
"\t en_user_vgpr2 = %s\n"
|
||||
"\t en_user_vgpr3 = %s\n",
|
||||
uc.GetPrimType(), uc.GetIndexOffset(), uc.GetObjectId(), ge_cntl.primitive_group_size,
|
||||
ge_cntl.vertex_group_size, user_en.vgpr1 ? "true" : "false",
|
||||
user_en.vgpr2 ? "true" : "false", user_en.vgpr3 ? "true" : "false");
|
||||
static_cast<uint32_t>(uc.GetPrimType()), uc.GetIndexOffset(), uc.GetObjectId(),
|
||||
uc.GetPrimitiveResetControl(), ge_cntl.primitive_group_size, ge_cntl.vertex_group_size,
|
||||
user_en.vgpr1 ? "true" : "false", user_en.vgpr2 ? "true" : "false",
|
||||
user_en.vgpr3 ? "true" : "false");
|
||||
}
|
||||
|
||||
void uc_check(const HW::UserConfig& uc) {
|
||||
const auto& ge_cntl = uc.GetGeControl();
|
||||
const auto& user_en = uc.GetGeUserVgprEn();
|
||||
|
||||
if (ge_cntl.primitive_group_size > 0x0040) {
|
||||
LOGF("\t warning: unsupported GE_CNTL primitive_group_size = 0x%04" PRIx16 "\n",
|
||||
ge_cntl.primitive_group_size);
|
||||
}
|
||||
if (ge_cntl.vertex_group_size > 0x0040) {
|
||||
LOGF("\t warning: unsupported GE_CNTL vertex_group_size = 0x%04" PRIx16 "\n",
|
||||
ge_cntl.vertex_group_size);
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(user_en.vgpr1 != false);
|
||||
EXIT_NOT_IMPLEMENTED(user_en.vgpr2 != false);
|
||||
EXIT_NOT_IMPLEMENTED(user_en.vgpr3 != false);
|
||||
}
|
||||
|
||||
void sh_print(const char* func, const HW::Shader& /*uc*/) {
|
||||
LOGF("%s\n", func);
|
||||
}
|
||||
std::string rt_print(const char* func, const HW::RenderTarget& rt) {
|
||||
std::string dst;
|
||||
dst.reserve(4096);
|
||||
|
||||
std::vector<std::string> rt_print(const char* func, const HW::RenderTarget& rt) {
|
||||
std::vector<std::string> dst;
|
||||
dst.reserve(53);
|
||||
dst += fmt::format("{}\n", func);
|
||||
|
||||
dst.push_back(fmt::format("{}\n", func));
|
||||
dst += fmt::format("\t base.addr = 0x{:016x}\n", rt.base.addr);
|
||||
dst += fmt::format("\t view.base_array_slice_index = 0x{:08x}\n",
|
||||
rt.view.base_array_slice_index);
|
||||
dst += fmt::format("\t view.last_array_slice_index = 0x{:08x}\n",
|
||||
rt.view.last_array_slice_index);
|
||||
dst += fmt::format("\t view.current_mip_level = 0x{:08x}\n", rt.view.current_mip_level);
|
||||
dst += fmt::format("\t info.fmask_compression_enable = {}\n",
|
||||
rt.info.fmask_compression_enable ? "true" : "false");
|
||||
|
||||
dst.push_back(fmt::format("\t base.addr = 0x{:016x}\n", rt.base.addr));
|
||||
dst.push_back(
|
||||
fmt::format("\t pitch.pitch_div8_minus1 = 0x{:08x}\n", rt.pitch.pitch_div8_minus1));
|
||||
dst.push_back(fmt::format("\t pitch.fmask_pitch_div8_minus1 = 0x{:08x}\n",
|
||||
rt.pitch.fmask_pitch_div8_minus1));
|
||||
dst.push_back(fmt::format("\t slice.slice_div64_minus1 = 0x{:08x}\n",
|
||||
rt.slice.slice_div64_minus1));
|
||||
dst.push_back(fmt::format("\t view.base_array_slice_index = 0x{:08x}\n",
|
||||
rt.view.base_array_slice_index));
|
||||
dst.push_back(fmt::format("\t view.last_array_slice_index = 0x{:08x}\n",
|
||||
rt.view.last_array_slice_index));
|
||||
dst.push_back(
|
||||
fmt::format("\t view.current_mip_level = 0x{:08x}\n", rt.view.current_mip_level));
|
||||
dst.push_back(fmt::format("\t info.fmask_compression_enable = {}\n",
|
||||
rt.info.fmask_compression_enable ? "true" : "false"));
|
||||
dst += fmt::format("\t info.fmask_data_compression_disable = {}\n",
|
||||
rt.info.fmask_data_compression_disable ? "true" : "false");
|
||||
dst += fmt::format("\t info.fmask_one_frag_mode = {}\n",
|
||||
rt.info.fmask_one_frag_mode ? "true" : "false");
|
||||
|
||||
// dst.push_back(fmt::format("\t info.fmask_compression_mode = 0x{:08x}\n", //
|
||||
// rt.info.fmask_compression_mode));
|
||||
dst.push_back(fmt::format("\t info.fmask_data_compression_disable = {}\n",
|
||||
rt.info.fmask_data_compression_disable ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t info.fmask_one_frag_mode = {}\n",
|
||||
rt.info.fmask_one_frag_mode ? "true" : "false"));
|
||||
|
||||
dst.push_back(fmt::format("\t info.cmask_fast_clear_enable = {}\n",
|
||||
rt.info.cmask_fast_clear_enable ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t info.dcc_compression_enable = {}\n",
|
||||
rt.info.dcc_compression_enable ? "true" : "false"));
|
||||
dst.push_back(
|
||||
fmt::format("\t info.cmask_is_linear = 0x{:08x}\n", rt.info.cmask_is_linear));
|
||||
dst.push_back(
|
||||
fmt::format("\t info.cmask_addr_type = 0x{:08x}\n", rt.info.cmask_addr_type));
|
||||
dst.push_back(fmt::format("\t info.alt_tile_mode = {}\n",
|
||||
rt.info.alt_tile_mode ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t info.format = 0x{:08x}\n", rt.info.format));
|
||||
dst.push_back(
|
||||
fmt::format("\t info.channel_type = 0x{:08x}\n", rt.info.channel_type));
|
||||
dst.push_back(
|
||||
fmt::format("\t info.channel_order = 0x{:08x}\n", rt.info.channel_order));
|
||||
dst.push_back(fmt::format("\t info.blend_bypa = {}\n",
|
||||
rt.info.blend_bypass ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t info.blend_clamp = {}\n",
|
||||
rt.info.blend_clamp ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t info.round_mode = {}\n",
|
||||
rt.info.round_mode ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t attrib.force_dest_alpha_to_one = {}\n",
|
||||
rt.attrib.force_dest_alpha_to_one ? "true" : "false"));
|
||||
dst.push_back(
|
||||
fmt::format("\t attrib.tile_mode = 0x{:08x}\n", rt.attrib.tile_mode));
|
||||
dst.push_back(
|
||||
fmt::format("\t attrib.fmask_tile_mode = 0x{:08x}\n", rt.attrib.fmask_tile_mode));
|
||||
dst.push_back(
|
||||
fmt::format("\t attrib.num_samples = 0x{:08x}\n", rt.attrib.num_samples));
|
||||
dst.push_back(
|
||||
fmt::format("\t attrib.num_fragments = 0x{:08x}\n", rt.attrib.num_fragments));
|
||||
dst.push_back(fmt::format("\t attrib2.width = 0x{:08x}\n", rt.attrib2.width));
|
||||
dst.push_back(
|
||||
fmt::format("\t attrib2.height = 0x{:08x}\n", rt.attrib2.height));
|
||||
dst.push_back(
|
||||
fmt::format("\t attrib2.num_mip_levels = 0x{:08x}\n", rt.attrib2.num_mip_levels));
|
||||
dst.push_back(fmt::format("\t attrib3.depth = 0x{:08x}\n", rt.attrib3.depth));
|
||||
dst.push_back(
|
||||
fmt::format("\t attrib3.tile_mode = 0x{:08x}\n", rt.attrib3.tile_mode));
|
||||
dst.push_back(
|
||||
fmt::format("\t attrib3.dimension = 0x{:08x}\n", rt.attrib3.dimension));
|
||||
dst.push_back(fmt::format("\t attrib3.cmask_pipe_aligned = {}\n",
|
||||
rt.attrib3.cmask_pipe_aligned ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t attrib3.dcc_pipe_aligned = {}\n",
|
||||
rt.attrib3.dcc_pipe_aligned ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t dcc.max_uncompressed_block_size = 0x{:08x}\n",
|
||||
rt.dcc.max_uncompressed_block_size));
|
||||
dst.push_back(fmt::format("\t dcc.max_compressed_block_size = 0x{:08x}\n",
|
||||
rt.dcc.max_compressed_block_size));
|
||||
dst.push_back(fmt::format("\t dcc.min_compressed_block_size = 0x{:08x}\n",
|
||||
rt.dcc.min_compressed_block_size));
|
||||
dst.push_back(
|
||||
fmt::format("\t dcc.color_transform = 0x{:08x}\n", rt.dcc.color_transform));
|
||||
dst.push_back(fmt::format("\t dcc.overwrite_combiner_disable = {}\n",
|
||||
rt.dcc.overwrite_combiner_disable ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t dcc.independent_64b_blocks = {}\n",
|
||||
rt.dcc.independent_64b_blocks ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t dcc.independent_128b_blocks = {}\n",
|
||||
rt.dcc.independent_128b_blocks ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t data_write_on_dcc_clear_to_reg = {}\n",
|
||||
rt.dcc.data_write_on_dcc_clear_to_reg ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t dcc.dcc_clear_key_enable = {}\n",
|
||||
rt.dcc.dcc_clear_key_enable ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t cmask.addr = 0x{:016x}\n", rt.cmask.addr));
|
||||
dst.push_back(fmt::format("\t cmask_slice.slice_minus1 = 0x{:08x}\n",
|
||||
rt.cmask_slice.slice_minus1));
|
||||
dst.push_back(fmt::format("\t fmask.addr = 0x{:016x}\n", rt.fmask.addr));
|
||||
dst.push_back(fmt::format("\t fmask_slice.slice_minus1 = 0x{:08x}\n",
|
||||
rt.fmask_slice.slice_minus1));
|
||||
dst.push_back(
|
||||
fmt::format("\t clear_word0.word0 = 0x{:08x}\n", rt.clear_word0.word0));
|
||||
dst.push_back(
|
||||
fmt::format("\t clear_word1.word1 = 0x{:08x}\n", rt.clear_word1.word1));
|
||||
dst.push_back(
|
||||
fmt::format("\t dcc_addr.addr = 0x{:016x}\n", rt.dcc_addr.addr));
|
||||
dst.push_back(fmt::format("\t size.width = 0x{:08x}\n", rt.size.width));
|
||||
dst.push_back(fmt::format("\t size.height = 0x{:08x}\n", rt.size.height));
|
||||
dst += fmt::format("\t info.cmask_fast_clear_enable = {}\n",
|
||||
rt.info.cmask_fast_clear_enable ? "true" : "false");
|
||||
dst += fmt::format("\t info.dcc_compression_enable = {}\n",
|
||||
rt.info.dcc_compression_enable ? "true" : "false");
|
||||
dst += fmt::format("\t info.format = 0x{:08x}\n",
|
||||
static_cast<uint32_t>(rt.info.format));
|
||||
dst += fmt::format("\t info.channel_type = 0x{:08x}\n",
|
||||
static_cast<uint32_t>(rt.info.channel_type));
|
||||
dst += fmt::format("\t info.channel_order = 0x{:08x}\n",
|
||||
static_cast<uint32_t>(rt.info.channel_order));
|
||||
dst += fmt::format("\t info.blend_bypa = {}\n",
|
||||
rt.info.blend_bypass ? "true" : "false");
|
||||
dst += fmt::format("\t info.blend_clamp = {}\n",
|
||||
rt.info.blend_clamp ? "true" : "false");
|
||||
dst += fmt::format("\t info.round_mode = {}\n",
|
||||
rt.info.round_mode ? "true" : "false");
|
||||
dst += fmt::format("\t attrib.force_dest_alpha_to_one = {}\n",
|
||||
rt.attrib.force_dest_alpha_to_one ? "true" : "false");
|
||||
dst += fmt::format("\t attrib.num_samples = 0x{:08x}\n", rt.attrib.num_samples);
|
||||
dst += fmt::format("\t attrib.num_fragments = 0x{:08x}\n", rt.attrib.num_fragments);
|
||||
dst += fmt::format("\t attrib2.width = 0x{:08x}\n", rt.attrib2.width);
|
||||
dst += fmt::format("\t attrib2.height = 0x{:08x}\n", rt.attrib2.height);
|
||||
dst += fmt::format("\t attrib2.num_mip_levels = 0x{:08x}\n", rt.attrib2.num_mip_levels);
|
||||
dst += fmt::format("\t attrib3.depth = 0x{:08x}\n", rt.attrib3.depth);
|
||||
dst += fmt::format("\t attrib3.tile_mode = 0x{:08x}\n",
|
||||
static_cast<uint32_t>(rt.attrib3.tile_mode));
|
||||
dst += fmt::format("\t attrib3.dimension = 0x{:08x}\n", rt.attrib3.dimension);
|
||||
dst += fmt::format("\t attrib3.metadata_pipe_aligned = {}\n",
|
||||
rt.attrib3.metadata_pipe_aligned ? "true" : "false");
|
||||
dst += fmt::format("\t attrib3.write_vrs_rate_hint_to_cmask = {}\n",
|
||||
rt.attrib3.write_vrs_rate_hint_to_cmask ? "true" : "false");
|
||||
dst += fmt::format("\t dcc.max_uncompressed_block_size = 0x{:08x}\n",
|
||||
rt.dcc.max_uncompressed_block_size);
|
||||
dst += fmt::format("\t dcc.max_compressed_block_size = 0x{:08x}\n",
|
||||
rt.dcc.max_compressed_block_size);
|
||||
dst += fmt::format("\t dcc.color_transform = 0x{:08x}\n", rt.dcc.color_transform);
|
||||
dst += fmt::format("\t dcc.overwrite_combiner_disable = {}\n",
|
||||
rt.dcc.overwrite_combiner_disable ? "true" : "false");
|
||||
dst += fmt::format("\t dcc.independent_block_size = 0x{:02x}\n",
|
||||
static_cast<uint8_t>(rt.dcc.independent_block_size));
|
||||
dst += fmt::format("\t data_write_on_dcc_clear_to_reg = {}\n",
|
||||
rt.dcc.data_write_on_dcc_clear_to_reg ? "true" : "false");
|
||||
dst += fmt::format("\t dcc.dcc_clear_key_enable = {}\n",
|
||||
rt.dcc.dcc_clear_key_enable ? "true" : "false");
|
||||
dst += fmt::format("\t cmask.addr = 0x{:016x}\n", rt.cmask.addr);
|
||||
dst += fmt::format("\t fmask.addr = 0x{:016x}\n", rt.fmask.addr);
|
||||
dst += fmt::format("\t clear_word0.word0 = 0x{:08x}\n", rt.clear_word0.word0);
|
||||
dst += fmt::format("\t clear_word1.word1 = 0x{:08x}\n", rt.clear_word1.word1);
|
||||
dst += fmt::format("\t dcc_addr.addr = 0x{:016x}\n", rt.dcc_addr.addr);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
static bool RenderIsColorTileMode(uint32_t tile_mode) {
|
||||
static bool RenderIsColorTileMode(Prospero::TileMode tile_mode) {
|
||||
// AGC CxRenderTarget::TileMode shifted by CB_COLOR*_ATTRIB3.COLOR_SW_MODE.
|
||||
switch (tile_mode) {
|
||||
case Prospero::GpuEnumValue(Prospero::TileMode::kLinear):
|
||||
case Prospero::GpuEnumValue(Prospero::TileMode::kStandard256B):
|
||||
case Prospero::GpuEnumValue(Prospero::TileMode::kStandard4KB):
|
||||
case Prospero::GpuEnumValue(Prospero::TileMode::kStandard64KB):
|
||||
case Prospero::GpuEnumValue(Prospero::TileMode::kPrt):
|
||||
case Prospero::GpuEnumValue(Prospero::TileMode::kDepth):
|
||||
case Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget): return true;
|
||||
case Prospero::TileMode::kLinear:
|
||||
case Prospero::TileMode::kStandard256B:
|
||||
case Prospero::TileMode::kStandard4KB:
|
||||
case Prospero::TileMode::kStandard64KB:
|
||||
case Prospero::TileMode::kPrt:
|
||||
case Prospero::TileMode::kDepth:
|
||||
case Prospero::TileMode::kRenderTarget: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderIsColorTileModeLinear(uint32_t tile_mode) {
|
||||
return tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kLinear);
|
||||
bool RenderIsColorTileModeLinear(Prospero::TileMode tile_mode) {
|
||||
return tile_mode == Prospero::TileMode::kLinear;
|
||||
}
|
||||
|
||||
static bool RenderIsColorDimension(uint32_t dimension) {
|
||||
@@ -237,13 +186,8 @@ static bool RenderIsColorDimension(uint32_t dimension) {
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
static void RtCheck(const HW::RenderTarget& rt) {
|
||||
if (rt.base.addr != 0) {
|
||||
// bool render_to_texture = (rt.attrib.tile_mode == 0x0d);
|
||||
// EXIT_NOT_IMPLEMENTED(rt.base_addr == 0);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(rt.pitch.pitch_div8_minus1 != 0);
|
||||
EXIT_NOT_IMPLEMENTED(rt.pitch.fmask_pitch_div8_minus1 != 0);
|
||||
EXIT_NOT_IMPLEMENTED(rt.slice.slice_div64_minus1 != 0);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(rt.view.base_array_slice_index > rt.view.last_array_slice_index);
|
||||
if (rt.view.base_array_slice_index != 0x00000000 ||
|
||||
rt.view.last_array_slice_index != 0x00000000) {
|
||||
@@ -267,16 +211,14 @@ static void RtCheck(const HW::RenderTarget& rt) {
|
||||
EXIT_NOT_IMPLEMENTED(rt.attrib.num_samples == 0 && rt.attrib.num_fragments == 0);
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
LOGF("RenderTarget: temporary: ignoring PS5 FMASK metadata for single-sample MSAA "
|
||||
"fallback, fmask=0x%016" PRIx64 "\n",
|
||||
LOGF("RenderTarget: using native Vulkan MSAA without guest FMASK metadata, "
|
||||
"fmask=0x%016" PRIx64 "\n",
|
||||
rt.fmask.addr);
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
|
||||
// EXIT_NOT_IMPLEMENTED(rt.info.fmask_compression_mode != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(rt.info.fmask_data_compression_disable != false);
|
||||
EXIT_NOT_IMPLEMENTED(rt.info.fmask_one_frag_mode != false);
|
||||
|
||||
if (rt.info.cmask_fast_clear_enable || rt.info.dcc_compression_enable) {
|
||||
static bool logged = false;
|
||||
@@ -289,22 +231,6 @@ static void RtCheck(const HW::RenderTarget& rt) {
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
if (rt.info.cmask_is_linear != 0x00000000 || rt.info.cmask_addr_type != 0x00000000) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
LOGF("RenderTarget: temporary: ignoring PS5 CMASK metadata fields "
|
||||
"is_linear=%u addr_type=%u\n",
|
||||
rt.info.cmask_is_linear, rt.info.cmask_addr_type);
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
if (rt.info.alt_tile_mode) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
LOGF("RenderTarget: temporary: ignoring alternate tile mode flag\n");
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
if (rt.info.blend_bypass) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
@@ -331,8 +257,6 @@ static void RtCheck(const HW::RenderTarget& rt) {
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
// EXIT_NOT_IMPLEMENTED(rt.tile_mode != 0x0000000a);
|
||||
// EXIT_NOT_IMPLEMENTED(rt.fmask_tile_mode != 0x0000000a);
|
||||
if (rt.attrib.num_samples != 0x00000000 || rt.attrib.num_fragments != 0x00000000) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
@@ -361,39 +285,28 @@ static void RtCheck(const HW::RenderTarget& rt) {
|
||||
}
|
||||
}
|
||||
if (!RenderIsColorTileMode(rt.attrib3.tile_mode)) {
|
||||
EXIT("unknown PS5 render-target tile mode: 0x%08" PRIx32 "\n", rt.attrib3.tile_mode);
|
||||
EXIT("unknown PS5 render-target tile mode: 0x%08" PRIx32 "\n",
|
||||
static_cast<uint32_t>(rt.attrib3.tile_mode));
|
||||
}
|
||||
if (!RenderIsColorDimension(rt.attrib3.dimension)) {
|
||||
EXIT("unknown PS5 render-target dimension: 0x%08" PRIx32 "\n", rt.attrib3.dimension);
|
||||
}
|
||||
if (!rt.attrib3.cmask_pipe_aligned) {
|
||||
if (!rt.attrib3.metadata_pipe_aligned) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
LOGF("RenderTarget: temporary: accepting unaligned PS5 CMASK pipe flag\n");
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
if (!rt.attrib3.dcc_pipe_aligned) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
LOGF("RenderTarget: temporary: accepting unaligned PS5 DCC pipe flag\n");
|
||||
LOGF("RenderTarget: temporary: accepting unaligned PS5 metadata pipe flag\n");
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(rt.attrib3.write_vrs_rate_hint_to_cmask);
|
||||
|
||||
// EXIT_NOT_IMPLEMENTED(rt.dcc_max_uncompressed_block_size != 0x00000002);
|
||||
// EXIT_NOT_IMPLEMENTED(rt.dcc.max_compressed_block_size != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(rt.dcc.min_compressed_block_size != 0x00000000);
|
||||
// EXIT_NOT_IMPLEMENTED(rt.dcc.color_transform != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(rt.dcc.overwrite_combiner_disable != false);
|
||||
// EXIT_NOT_IMPLEMENTED(rt.dcc.force_independent_blocks != false);
|
||||
// EXIT_NOT_IMPLEMENTED(rt.dcc.independent_128b_blocks != false);
|
||||
// EXIT_NOT_IMPLEMENTED(rt.dcc.data_write_on_dcc_clear_to_reg != false);
|
||||
EXIT_NOT_IMPLEMENTED(rt.dcc.dcc_clear_key_enable != false);
|
||||
if (rt.cmask.addr != 0x0000000000000000 || rt.cmask_slice.slice_minus1 != 0x00000000 ||
|
||||
rt.fmask.addr != 0x0000000000000000 ||
|
||||
(rt.fmask_slice.slice_minus1 != 0x00000000 &&
|
||||
rt.fmask_slice.slice_minus1 != rt.slice.slice_div64_minus1) ||
|
||||
if (rt.cmask.addr != 0x0000000000000000 || rt.fmask.addr != 0x0000000000000000 ||
|
||||
rt.dcc_addr.addr != 0x0000000000000000) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
@@ -403,9 +316,6 @@ static void RtCheck(const HW::RenderTarget& rt) {
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(rt.size.width != 0);
|
||||
EXIT_NOT_IMPLEMENTED(rt.size.height != 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,160 +335,37 @@ static void ZPrint(const char* func, const HW::DepthRenderTarget& z) {
|
||||
"\t stencil_info.htile_stencil_disabled = %s\n"
|
||||
"\t stencil_info.expclear_enabled = %s\n"
|
||||
"\t stencil_info.partially_resident = %s\n"
|
||||
"\t depth_info.addr5_swizzle_mask = 0x%08" PRIx32 "\n"
|
||||
"\t depth_info.array_mode = 0x%08" PRIx32 "\n"
|
||||
"\t depth_info.pipe_config = 0x%08" PRIx32 "\n"
|
||||
"\t depth_info.bank_width = 0x%08" PRIx32 "\n"
|
||||
"\t depth_info.bank_height = 0x%08" PRIx32 "\n"
|
||||
"\t depth_info.macro_tile_aspect = 0x%08" PRIx32 "\n"
|
||||
"\t depth_info.num_banks = 0x%08" PRIx32 "\n"
|
||||
"\t depth_view.slice_start = 0x%08" PRIx32 "\n"
|
||||
"\t depth_view.slice_max = 0x%08" PRIx32 "\n"
|
||||
"\t depth_view.current_mip_level = 0x%02" PRIx8 "\n"
|
||||
"\t depth_view.depth_write_disable = %s\n"
|
||||
"\t depth_view.stencil_write_disable = %s\n"
|
||||
"\t htile_surface.linear = 0x%08" PRIx32 "\n"
|
||||
"\t htile_surface.full_cache = 0x%08" PRIx32 "\n"
|
||||
"\t htile_surface.htile_uses_preload_win = 0x%08" PRIx32 "\n"
|
||||
"\t htile_surface.preload = 0x%08" PRIx32 "\n"
|
||||
"\t htile_surface.prefetch_width = 0x%08" PRIx32 "\n"
|
||||
"\t htile_surface.prefetch_height = 0x%08" PRIx32 "\n"
|
||||
"\t htile_surface.dst_outside_zero_to_one = 0x%08" PRIx32 "\n"
|
||||
"\t z_read_base_addr = 0x%016" PRIx64 "\n"
|
||||
"\t stencil_read_base_addr = 0x%016" PRIx64 "\n"
|
||||
"\t z_write_base_addr = 0x%016" PRIx64 "\n"
|
||||
"\t stencil_write_base_addr = 0x%016" PRIx64 "\n"
|
||||
"\t pitch_div8_minus1 = 0x%08" PRIx32 "\n"
|
||||
"\t height_div8_minus1 = 0x%08" PRIx32 "\n"
|
||||
"\t slice_div64_minus1 = 0x%08" PRIx32 "\n"
|
||||
"\t htile_data_base_addr = 0x%016" PRIx64 "\n"
|
||||
"\t width = 0x%08" PRIx32 "\n"
|
||||
"\t height = 0x%08" PRIx32 "\n"
|
||||
"\t shading_rate_encoding = 0x%02" PRIx8 "\n"
|
||||
"\t size.x_max = 0x%04" PRIx16 "\n"
|
||||
"\t size.y_max = 0x%04" PRIx16 "\n",
|
||||
z.z_info.format, z.z_info.num_samples,
|
||||
Prospero::GpuEnumValue(z.z_info.texture_compatibility),
|
||||
"\t size.y_max = 0x%04" PRIx16 "\n"
|
||||
"\t size.valid = %s\n",
|
||||
static_cast<uint32_t>(z.z_info.format), z.z_info.num_samples,
|
||||
static_cast<uint32_t>(z.z_info.texture_compatibility),
|
||||
z.z_info.htile_acceleration ? "true" : "false",
|
||||
z.z_info.expclear_enabled ? "true" : "false",
|
||||
Prospero::GpuEnumValue(z.z_info.z_compare_base),
|
||||
static_cast<uint32_t>(z.z_info.z_compare_base),
|
||||
z.z_info.partially_resident ? "true" : "false", z.z_info.max_mip_level,
|
||||
z.stencil_info.format, Prospero::GpuEnumValue(z.stencil_info.texture_compatibility),
|
||||
static_cast<uint32_t>(z.stencil_info.format),
|
||||
static_cast<uint32_t>(z.stencil_info.texture_compatibility),
|
||||
z.stencil_info.htile_stencil_disabled ? "true" : "false",
|
||||
z.stencil_info.expclear_enabled ? "true" : "false",
|
||||
z.stencil_info.partially_resident ? "true" : "false", z.depth_info.addr5_swizzle_mask,
|
||||
z.depth_info.array_mode, z.depth_info.pipe_config, z.depth_info.bank_width,
|
||||
z.depth_info.bank_height, z.depth_info.macro_tile_aspect, z.depth_info.num_banks,
|
||||
z.depth_view.slice_start, z.depth_view.slice_max, z.depth_view.current_mip_level,
|
||||
z.stencil_info.partially_resident ? "true" : "false", z.depth_view.slice_start,
|
||||
z.depth_view.slice_max, z.depth_view.current_mip_level,
|
||||
z.depth_view.depth_write_disable ? "true" : "false",
|
||||
z.depth_view.stencil_write_disable ? "true" : "false", z.htile_surface.linear,
|
||||
z.htile_surface.full_cache, z.htile_surface.htile_uses_preload_win,
|
||||
z.htile_surface.preload, z.htile_surface.prefetch_width, z.htile_surface.prefetch_height,
|
||||
z.htile_surface.dst_outside_zero_to_one, z.z_read_base_addr, z.stencil_read_base_addr,
|
||||
z.z_write_base_addr, z.stencil_write_base_addr, z.pitch_div8_minus1, z.height_div8_minus1,
|
||||
z.slice_div64_minus1, z.htile_data_base_addr, z.width, z.height, z.size.x_max,
|
||||
z.size.y_max);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
static void ZCheck(const HW::DepthRenderTarget& z, const HW::DepthControl& dc,
|
||||
const HW::RenderControl& rc) {
|
||||
const bool depth_active =
|
||||
dc.z_enable || dc.z_write_enable || dc.depth_bounds_enable || rc.depth_clear_enable;
|
||||
const bool stencil_active = dc.stencil_enable || rc.stencil_clear_enable;
|
||||
if (!depth_active && !stencil_active) {
|
||||
return;
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(!z.z_info.HasValidTextureCompatibility());
|
||||
EXIT_NOT_IMPLEMENTED(!z.stencil_info.HasValidTextureCompatibility());
|
||||
if (z.z_info.format == 0) {
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.format != 0);
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.num_samples != 0);
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.htile_acceleration != false);
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.expclear_enabled != false);
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.partially_resident != false);
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.max_mip_level != 0);
|
||||
} else {
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.format != 0x00000001 && z.z_info.format != 0x00000003);
|
||||
if (z.z_info.num_samples != 0x00000000) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
LOGF("DepthTarget: using native num_samples=0x%08" PRIx32 "\n",
|
||||
z.z_info.num_samples);
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.expclear_enabled != false);
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.partially_resident != false);
|
||||
EXIT_NOT_IMPLEMENTED(z.z_info.max_mip_level != 0);
|
||||
}
|
||||
|
||||
if (z.stencil_info.format == 0) {
|
||||
EXIT_NOT_IMPLEMENTED(z.stencil_info.expclear_enabled != false);
|
||||
EXIT_NOT_IMPLEMENTED(z.stencil_info.partially_resident != false);
|
||||
} else {
|
||||
EXIT_NOT_IMPLEMENTED(z.stencil_info.format != 0x00000001);
|
||||
EXIT_NOT_IMPLEMENTED(z.stencil_info.expclear_enabled != false);
|
||||
EXIT_NOT_IMPLEMENTED(z.stencil_info.partially_resident != false);
|
||||
}
|
||||
|
||||
if (z.z_info.format != 0 || z.stencil_info.format != 0) {
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(z.depth_info.addr5_swizzle_mask != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.depth_info.array_mode != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.depth_info.pipe_config != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.depth_info.bank_width != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.depth_info.bank_height != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.depth_info.macro_tile_aspect != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.depth_info.num_banks != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.htile_surface.linear != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.htile_surface.full_cache != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.htile_surface.htile_uses_preload_win != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.htile_surface.preload != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.htile_surface.prefetch_width != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.htile_surface.prefetch_height != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.htile_surface.dst_outside_zero_to_one != 0x00000000);
|
||||
|
||||
if (z.depth_view.current_mip_level != 0x00000000) {
|
||||
static std::atomic<uint32_t> log_count {0};
|
||||
if (log_count.fetch_add(1, std::memory_order_relaxed) < 16) {
|
||||
LOGF("DepthTarget: temporary: ignoring PS5 current mip level=0x%08" PRIx32 "\n",
|
||||
z.depth_view.current_mip_level);
|
||||
}
|
||||
}
|
||||
if (z.depth_view.depth_write_disable || z.depth_view.stencil_write_disable) {
|
||||
static std::atomic<uint32_t> log_count {0};
|
||||
if (log_count.fetch_add(1, std::memory_order_relaxed) < 16) {
|
||||
LOGF("DepthTarget: honoring write disable depth=%s, stencil=%s\n",
|
||||
z.depth_view.depth_write_disable ? "true" : "false",
|
||||
z.depth_view.stencil_write_disable ? "true" : "false");
|
||||
}
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(z.z_read_base_addr != z.z_write_base_addr);
|
||||
EXIT_NOT_IMPLEMENTED(z.stencil_read_base_addr != z.stencil_write_base_addr);
|
||||
EXIT_NOT_IMPLEMENTED(z.z_write_base_addr == 0);
|
||||
if (z.stencil_info.format != 0 && z.stencil_write_base_addr == 0) {
|
||||
LOGF("\t warning: stencil format is set without a stencil base address, continuing "
|
||||
"without stencil attachment\n");
|
||||
}
|
||||
// EXIT_NOT_IMPLEMENTED(z.pitch_div8_minus1 != 0x000000ff);
|
||||
// EXIT_NOT_IMPLEMENTED(z.height_div8_minus1 != 0x0000008f);
|
||||
// EXIT_NOT_IMPLEMENTED(z.slice_div64_minus1 != 0x00008fff);
|
||||
// EXIT_NOT_IMPLEMENTED(z.htile_data_base_addr == 0);
|
||||
// EXIT_NOT_IMPLEMENTED(z.width != 0x00000780);
|
||||
// EXIT_NOT_IMPLEMENTED(z.height != 0x00000438);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(z.width != 0);
|
||||
EXIT_NOT_IMPLEMENTED(z.height != 0);
|
||||
if (z.size.x_max == 0 || z.size.y_max == 0) {
|
||||
static std::atomic<uint32_t> log_count {0};
|
||||
if (log_count.fetch_add(1) < 16) {
|
||||
LOGF("DepthTarget: temporary: inferring PS5 default depth extent x_max=0x%04" PRIx16
|
||||
", y_max=0x%04" PRIx16 "\n",
|
||||
z.size.x_max, z.size.y_max);
|
||||
}
|
||||
}
|
||||
}
|
||||
z.depth_view.stencil_write_disable ? "true" : "false", z.z_read_base_addr,
|
||||
z.stencil_read_base_addr, z.z_write_base_addr, z.stencil_write_base_addr,
|
||||
z.htile_data_base_addr, z.shading_rate_encoding, z.size.x_max, z.size.y_max,
|
||||
z.size.valid ? "true" : "false");
|
||||
}
|
||||
|
||||
static void ClipPrint(const char* func, const HW::ClipControl& c) {
|
||||
@@ -609,8 +396,7 @@ static void ClipCheck(const HW::ClipControl& c) {
|
||||
// dx_linear_attr_clip_enable preserves linear (noperspective) attributes at clip-generated
|
||||
// vertices, which Vulkan provides as part of clipping and interpolation.
|
||||
EXIT_NOT_IMPLEMENTED(c.user_clip_planes != 0 || c.user_clip_plane_mode != 0 ||
|
||||
c.vertex_kill_any || !c.IsZClipModeRepresentable() ||
|
||||
c.user_clip_plane_negate_y || c.clip_disable ||
|
||||
c.vertex_kill_any || c.user_clip_plane_negate_y ||
|
||||
c.user_clip_plane_cull_only || c.cull_on_clipping_error_disable ||
|
||||
c.force_viewport_index_from_vs_enable);
|
||||
}
|
||||
@@ -634,17 +420,6 @@ static void RcPrint(const char* func, const HW::RenderControl& c) {
|
||||
static void RcCheck(const HW::RenderControl& c) {
|
||||
// EXIT_NOT_IMPLEMENTED(c.depth_clear_enable != false);
|
||||
// EXIT_NOT_IMPLEMENTED(c.stencil_clear_enable != false);
|
||||
if ((c.resummarize_enable || c.copy_centroid || c.copy_sample != 0)) {
|
||||
static std::atomic<uint32_t> log_count {0};
|
||||
if (log_count.fetch_add(1) < 16) {
|
||||
LOGF("\t warning: unsupported PS5 render-control metadata state, continuing: "
|
||||
"resummarize=%s copy_centroid=%s copy_sample=%" PRIu8 "\n",
|
||||
c.resummarize_enable ? "true" : "false", c.copy_centroid ? "true" : "false",
|
||||
c.copy_sample);
|
||||
}
|
||||
return;
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(c.resummarize_enable != false);
|
||||
// EXIT_NOT_IMPLEMENTED(c.stencil_compress_disable != false);
|
||||
// EXIT_NOT_IMPLEMENTED(c.depth_compress_disable != false);
|
||||
EXIT_NOT_IMPLEMENTED(c.copy_centroid != false);
|
||||
@@ -677,16 +452,6 @@ static void McCheck(const HW::ModeControl& c) {
|
||||
// EXIT_NOT_IMPLEMENTED(c.cull_front != false);
|
||||
// EXIT_NOT_IMPLEMENTED(c.cull_back != false);
|
||||
// EXIT_NOT_IMPLEMENTED(c.face != false);
|
||||
if (c.poly_mode != 0) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
LOGF("\t temporary: PA_SU_SC_MODE_CNTL.POLY_MODE is not fully implemented; continuing "
|
||||
"with filled polygons\n");
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(c.polymode_front_ptype != 0 && c.polymode_front_ptype != 2);
|
||||
EXIT_NOT_IMPLEMENTED(c.polymode_back_ptype != 0 && c.polymode_back_ptype != 2);
|
||||
if (c.vtx_window_offset_enable) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
@@ -696,7 +461,6 @@ static void McCheck(const HW::ModeControl& c) {
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(c.provoking_vtx_last != false);
|
||||
EXIT_NOT_IMPLEMENTED(c.persp_corr_dis != false);
|
||||
}
|
||||
|
||||
@@ -723,16 +487,7 @@ static void BcPrint(const char* func, const HW::BlendControl& c, const HW::Blend
|
||||
color.red, color.green, color.blue, color.alpha, cc.mode, cc.op);
|
||||
}
|
||||
|
||||
static void BcCheck(const HW::BlendControl& /*c*/, const HW::BlendColor& color,
|
||||
const HW::ColorControl& cc) {
|
||||
// EXIT_NOT_IMPLEMENTED(c.color_srcblend != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(c.color_comb_fcn != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(c.color_destblend != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(c.alpha_srcblend != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(c.alpha_comb_fcn != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(c.alpha_destblend != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(c.separate_alpha_blend != false);
|
||||
// EXIT_NOT_IMPLEMENTED(c.enable != false);
|
||||
static void BcCheck(const HW::BlendColor& color, const HW::ColorControl& cc) {
|
||||
if (color.red != 0.0f || color.green != 0.0f || color.blue != 0.0f || color.alpha != 0.0f) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
@@ -772,8 +527,6 @@ static void DPrint(const char* func, const HW::DepthControl& c, const HW::Stenci
|
||||
"\t backface_enable = %s\n"
|
||||
"\t stencilfunc = %" PRIu8 "\n"
|
||||
"\t stencilfunc_bf = %" PRIu8 "\n"
|
||||
"\t color_writes_on_depth_fail_enable = %s\n"
|
||||
"\t color_writes_on_depth_pass_disable = %s\n"
|
||||
"\t stencil_fail = %" PRIu8 "\n"
|
||||
"\t stencil_zpass = %" PRIu8 "\n"
|
||||
"\t stencil_zfail = %" PRIu8 "\n"
|
||||
@@ -790,37 +543,11 @@ static void DPrint(const char* func, const HW::DepthControl& c, const HW::Stenci
|
||||
"\t stencil_opval_bf = %" PRIu8 "\n",
|
||||
c.stencil_enable ? "true" : "false", c.z_enable ? "true" : "false",
|
||||
c.z_write_enable ? "true" : "false", c.depth_bounds_enable ? "true" : "false", c.zfunc,
|
||||
c.backface_enable ? "true" : "false", c.stencilfunc, c.stencilfunc_bf,
|
||||
c.color_writes_on_depth_fail_enable ? "true" : "false",
|
||||
c.color_writes_on_depth_pass_disable ? "true" : "false", s.stencil_fail, s.stencil_zpass,
|
||||
s.stencil_zfail, s.stencil_fail_bf, s.stencil_zpass_bf, s.stencil_zfail_bf,
|
||||
sm.stencil_testval, sm.stencil_mask, sm.stencil_writemask, sm.stencil_opval,
|
||||
sm.stencil_testval_bf, sm.stencil_mask_bf, sm.stencil_writemask_bf, sm.stencil_opval_bf);
|
||||
}
|
||||
|
||||
static void DCheck(const HW::DepthControl& c, const HW::StencilControl& s,
|
||||
const HW::StencilMask& /*sm*/) {
|
||||
// EXIT_NOT_IMPLEMENTED(c.stencil_enable != false);
|
||||
// EXIT_NOT_IMPLEMENTED(c.z_enable != false);
|
||||
// EXIT_NOT_IMPLEMENTED(c.z_write_enable != false);
|
||||
// EXIT_NOT_IMPLEMENTED(c.zfunc != 0);
|
||||
// Back-face stencil state is handled separately when enabled.
|
||||
// EXIT_NOT_IMPLEMENTED(c.stencilfunc != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(c.stencilfunc_bf != 0);
|
||||
EXIT_NOT_IMPLEMENTED(c.color_writes_on_depth_fail_enable != false);
|
||||
EXIT_NOT_IMPLEMENTED(c.color_writes_on_depth_pass_disable != false);
|
||||
// EXIT_NOT_IMPLEMENTED(s.stencil_fail != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(s.stencil_zpass != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(s.stencil_zfail != 0);
|
||||
// Back-face stencil ops may legitimately differ from the front-face ops.
|
||||
// EXIT_NOT_IMPLEMENTED(sm.stencil_testval != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(sm.stencil_mask != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(sm.stencil_writemask != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(sm.stencil_opval != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(sm.stencil_testval_bf != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(sm.stencil_mask_bf != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(sm.stencil_writemask_bf != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(sm.stencil_opval_bf != 0);
|
||||
c.backface_enable ? "true" : "false", c.stencilfunc, c.stencilfunc_bf, s.stencil_fail,
|
||||
s.stencil_zpass, s.stencil_zfail, s.stencil_fail_bf, s.stencil_zpass_bf,
|
||||
s.stencil_zfail_bf, sm.stencil_testval, sm.stencil_mask, sm.stencil_writemask,
|
||||
sm.stencil_opval, sm.stencil_testval_bf, sm.stencil_mask_bf, sm.stencil_writemask_bf,
|
||||
sm.stencil_opval_bf);
|
||||
}
|
||||
|
||||
static void EqaaPrint(const char* func, const HW::EqaaControl& c) {
|
||||
@@ -840,7 +567,12 @@ static void EqaaPrint(const char* func, const HW::EqaaControl& c) {
|
||||
c.static_anchor_associations ? "true" : "false");
|
||||
}
|
||||
|
||||
static void EqaaCheck(const HW::EqaaControl& c) {
|
||||
static void EqaaCheck(const HW::EqaaControl& c, const HW::AaConfig& cf) {
|
||||
// The EQAA controls only take effect while multisampling is on, so a single-sample
|
||||
// target leaves them inert and has nothing to warn about.
|
||||
if (cf.msaa_num_samples == 0) {
|
||||
return;
|
||||
}
|
||||
if (c.max_anchor_samples != 0 || c.ps_iter_samples != 0 || c.mask_export_num_samples != 0 ||
|
||||
c.alpha_to_mask_num_samples != 0 || c.high_quality_intersections ||
|
||||
c.incoherent_eqaa_reads || c.interpolate_comp_z || c.static_anchor_associations) {
|
||||
@@ -892,12 +624,10 @@ void LogDrawPhase(const char* draw_name, const char* phase) {
|
||||
}
|
||||
}
|
||||
|
||||
void LogPipelineTrace(const char* phase, uint32_t vs_hash0, uint32_t vs_crc32, uint32_t ps_hash0,
|
||||
uint32_t ps_crc32) {
|
||||
void LogPipelineTrace(const char* phase, uint64_t vertex_program_id, uint64_t pixel_program_id) {
|
||||
if (graphics_debug_dump_enabled()) {
|
||||
LOGF("PipelineTrace: %s VS=0x%08" PRIx32 "/0x%08" PRIx32 " PS=0x%08" PRIx32 "/0x%08" PRIx32
|
||||
"\n",
|
||||
phase, vs_hash0, vs_crc32, ps_hash0, ps_crc32);
|
||||
LOGF("PipelineTrace: %s VS=%" PRIu64 " PS=%" PRIu64 "\n", phase, vertex_program_id,
|
||||
pixel_program_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -978,15 +708,6 @@ static void VpCheck(const HW::ScreenViewport& vp, const HW::ScanModeControl& smc
|
||||
// EXIT_NOT_IMPLEMENTED(smc.vport_scissor_enable);
|
||||
EXIT_NOT_IMPLEMENTED(smc.line_stipple_enable);
|
||||
|
||||
if (vp.viewports[0].zmin > 0.000000 || vp.viewports[0].zmax != 1.000000) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
LOGF("\t warning: non-default viewport depth clamp zmin = %f, zmax = %f; using "
|
||||
"viewport scale/offset for Vulkan depth range\n",
|
||||
vp.viewports[0].zmin, vp.viewports[0].zmax);
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
// EXIT_NOT_IMPLEMENTED(vp.viewports[0].xscale != 960.000000);
|
||||
// EXIT_NOT_IMPLEMENTED(vp.viewports[0].xoffset != 960.000000);
|
||||
// EXIT_NOT_IMPLEMENTED(vp.viewports[0].yscale != -540.000000);
|
||||
@@ -1006,7 +727,7 @@ static void VpCheck(const HW::ScreenViewport& vp, const HW::ScanModeControl& smc
|
||||
// EXIT_NOT_IMPLEMENTED(fabsf(vp.guard_band_horz_clip - 33.133327f) > 0.001f);
|
||||
// EXIT_NOT_IMPLEMENTED(fabsf(vp.guard_band_vert_clip - 59.629623f) > 0.001f);
|
||||
|
||||
if (vp.guard_band_horz_discard != 0.0f || vp.guard_band_vert_discard != 0.0f) {
|
||||
if (vp.guard_band_horz_discard != 1.0f || vp.guard_band_vert_discard != 1.0f) {
|
||||
static std::atomic<uint32_t> log_count {0};
|
||||
if (log_count.fetch_add(1) < 16) {
|
||||
LOGF("\t warning: unsupported PS5 guard band discard = %f, %f, continuing\n",
|
||||
@@ -1026,10 +747,6 @@ static bool ScissorRectValid(const ScissorRect& r) {
|
||||
return r.right > r.left && r.bottom > r.top;
|
||||
}
|
||||
|
||||
static bool ScissorRectSet(const ScissorRect& r) {
|
||||
return r.left != 0 || r.top != 0 || r.right != 0 || r.bottom != 0;
|
||||
}
|
||||
|
||||
static ScissorRect ScissorRectOffset(ScissorRect r, int x, int y) {
|
||||
r.left += x;
|
||||
r.right += x;
|
||||
@@ -1039,8 +756,8 @@ static ScissorRect ScissorRectOffset(ScissorRect r, int x, int y) {
|
||||
}
|
||||
|
||||
static ScissorRect ScissorRectIntersect(const ScissorRect& a, const ScissorRect& b) {
|
||||
return {a.left > b.left ? a.left : b.left, a.top > b.top ? a.top : b.top,
|
||||
a.right < b.right ? a.right : b.right, a.bottom < b.bottom ? a.bottom : b.bottom};
|
||||
return {std::max(a.left, b.left), std::max(a.top, b.top),
|
||||
std::min(a.right, b.right), std::min(a.bottom, b.bottom)};
|
||||
}
|
||||
|
||||
static ScissorRect ScissorRectClamp(ScissorRect r, uint32_t width, uint32_t height) {
|
||||
@@ -1087,49 +804,26 @@ static bool ScissorClipRuleToIntersectionMask(uint16_t rule, uint8_t* mask) {
|
||||
}
|
||||
|
||||
ScissorRect calc_final_scissor(const HW::ScreenViewport& vp, const HW::ScanModeControl& smc,
|
||||
vk::Extent2D extent) {
|
||||
ScissorRect screen {vp.screen_scissor_left, vp.screen_scissor_top, vp.screen_scissor_right,
|
||||
vp.screen_scissor_bottom};
|
||||
ScissorRect final = screen;
|
||||
|
||||
if (!ScissorRectSet(screen)) {
|
||||
final = {0, 0, static_cast<int>(extent.width), static_cast<int>(extent.height)};
|
||||
|
||||
auto log_id = g_scissor_default_log_count.fetch_add(1);
|
||||
if (log_id < 32) {
|
||||
LOGF("temporary: default unset screen scissor to framebuffer extent %ux%u\n",
|
||||
extent.width, extent.height);
|
||||
vk::Extent2D extent, uint32_t viewport_index) {
|
||||
EXIT_IF(viewport_index >= std::size(vp.viewports));
|
||||
ScissorRect final {vp.screen_scissor_left, vp.screen_scissor_top, vp.screen_scissor_right,
|
||||
vp.screen_scissor_bottom};
|
||||
const auto intersect = [&](ScissorRect rect, bool window_offset) {
|
||||
if (window_offset) {
|
||||
rect = ScissorRectOffset(rect, vp.window_offset_x, vp.window_offset_y);
|
||||
}
|
||||
}
|
||||
final = ScissorRectIntersect(final, rect);
|
||||
};
|
||||
intersect({vp.window_scissor_left, vp.window_scissor_top, vp.window_scissor_right,
|
||||
vp.window_scissor_bottom}, vp.window_scissor_window_offset_enable);
|
||||
intersect({vp.generic_scissor_left, vp.generic_scissor_top, vp.generic_scissor_right,
|
||||
vp.generic_scissor_bottom}, vp.generic_scissor_window_offset_enable);
|
||||
|
||||
ScissorRect window {vp.window_scissor_left, vp.window_scissor_top, vp.window_scissor_right,
|
||||
vp.window_scissor_bottom};
|
||||
if (ScissorRectSet(window)) {
|
||||
if (vp.window_scissor_window_offset_enable) {
|
||||
window = ScissorRectOffset(window, vp.window_offset_x, vp.window_offset_y);
|
||||
}
|
||||
final = ScissorRectIntersect(final, window);
|
||||
}
|
||||
|
||||
ScissorRect generic {vp.generic_scissor_left, vp.generic_scissor_top, vp.generic_scissor_right,
|
||||
vp.generic_scissor_bottom};
|
||||
if (ScissorRectSet(generic)) {
|
||||
if (vp.generic_scissor_window_offset_enable) {
|
||||
generic = ScissorRectOffset(generic, vp.window_offset_x, vp.window_offset_y);
|
||||
}
|
||||
final = ScissorRectIntersect(final, generic);
|
||||
}
|
||||
|
||||
const auto& viewport = vp.viewports[0];
|
||||
ScissorRect viewport_scissor {viewport.viewport_scissor_left, viewport.viewport_scissor_top,
|
||||
viewport.viewport_scissor_right,
|
||||
viewport.viewport_scissor_bottom};
|
||||
if (smc.vport_scissor_enable && ScissorRectSet(viewport_scissor)) {
|
||||
if (viewport.viewport_scissor_window_offset_enable) {
|
||||
viewport_scissor =
|
||||
ScissorRectOffset(viewport_scissor, vp.window_offset_x, vp.window_offset_y);
|
||||
}
|
||||
final = ScissorRectIntersect(final, viewport_scissor);
|
||||
const auto& viewport = vp.viewports[viewport_index];
|
||||
if (smc.vport_scissor_enable) {
|
||||
intersect({viewport.viewport_scissor_left, viewport.viewport_scissor_top,
|
||||
viewport.viewport_scissor_right, viewport.viewport_scissor_bottom},
|
||||
viewport.viewport_scissor_window_offset_enable);
|
||||
}
|
||||
|
||||
if (vp.clip_rect_rule == 0) {
|
||||
@@ -1142,12 +836,8 @@ ScissorRect calc_final_scissor(const HW::ScreenViewport& vp, const HW::ScanModeC
|
||||
continue;
|
||||
}
|
||||
|
||||
ScissorRect clip {vp.clip_rect_left[i], vp.clip_rect_top[i], vp.clip_rect_right[i],
|
||||
vp.clip_rect_bottom[i]};
|
||||
if (vp.clip_rect_window_offset_enable[i]) {
|
||||
clip = ScissorRectOffset(clip, vp.window_offset_x, vp.window_offset_y);
|
||||
}
|
||||
final = ScissorRectIntersect(final, clip);
|
||||
intersect({vp.clip_rect_left[i], vp.clip_rect_top[i], vp.clip_rect_right[i],
|
||||
vp.clip_rect_bottom[i]}, vp.clip_rect_window_offset_enable[i]);
|
||||
}
|
||||
} else {
|
||||
static std::atomic<uint32_t> log_count {0};
|
||||
@@ -1161,19 +851,14 @@ ScissorRect calc_final_scissor(const HW::ScreenViewport& vp, const HW::ScanModeC
|
||||
return ScissorRectClamp(final, extent.width, extent.height);
|
||||
}
|
||||
|
||||
void hw_check(const RenderCommandBuffer& buffer) {
|
||||
void hw_check(const CommandBuffer& buffer) {
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
const auto rt_slot = render_target_first_bound_slot(buffer);
|
||||
const auto& rt = hw.GetRenderTarget(rt_slot);
|
||||
const auto& bc = hw.GetBlendControl(rt_slot);
|
||||
const auto& bclr = hw.GetBlendColor();
|
||||
const auto& vp = hw.GetScreenViewport();
|
||||
const auto& z = hw.GetDepthRenderTarget();
|
||||
const auto& c = hw.GetClipControl();
|
||||
const auto& rc = hw.GetRenderControl();
|
||||
const auto& d = hw.GetDepthControl();
|
||||
const auto& s = hw.GetStencilControl();
|
||||
const auto& sm = hw.GetStencilMask();
|
||||
const auto& mc = hw.GetModeControl();
|
||||
const auto& eqaa = hw.GetEqaaControl();
|
||||
const auto& cc = hw.GetColorControl();
|
||||
@@ -1194,32 +879,30 @@ void hw_check(const RenderCommandBuffer& buffer) {
|
||||
RtCheck(rt);
|
||||
log_phase("vp");
|
||||
VpCheck(vp, smc);
|
||||
log_phase("z");
|
||||
ZCheck(z, d, rc);
|
||||
log_phase("clip");
|
||||
ClipCheck(c);
|
||||
log_phase("rc");
|
||||
RcCheck(rc);
|
||||
log_phase("depth");
|
||||
DCheck(d, s, sm);
|
||||
log_phase("mode");
|
||||
McCheck(mc);
|
||||
log_phase("blend");
|
||||
BcCheck(bc, bclr, cc);
|
||||
BcCheck(bclr, cc);
|
||||
log_phase("eqaa");
|
||||
EqaaCheck(eqaa);
|
||||
EqaaCheck(eqaa, ac);
|
||||
log_phase("aa");
|
||||
AaCheck(aa, ac);
|
||||
log_phase("done");
|
||||
|
||||
if (RenderTargetMaskHasBoundMrt(buffer)) {
|
||||
if (graphics_debug_dump_enabled() && RenderTargetMaskHasBoundMrt(buffer)) {
|
||||
LOGF("MRT render target mask: 0x%08" PRIx32 "\n", hw.GetRenderTargetMask());
|
||||
for (uint32_t i = 0; i < 8; i++) {
|
||||
const auto& mrt = hw.GetRenderTarget(i);
|
||||
LOGF("\t RT%u addr=0x%010" PRIx64 " mask=0x%x fmt=0x%08" PRIx32
|
||||
" width=%u height=%u tile=%u\n",
|
||||
i, mrt.base.addr, (hw.GetRenderTargetMask() >> (i * 4u)) & 0x0fu, mrt.info.format,
|
||||
mrt.attrib2.width + 1, mrt.attrib2.height + 1, mrt.attrib3.tile_mode);
|
||||
i, mrt.base.addr, (hw.GetRenderTargetMask() >> (i * 4u)) & 0x0fu,
|
||||
static_cast<uint32_t>(mrt.info.format), mrt.attrib2.width + 1,
|
||||
mrt.attrib2.height + 1, static_cast<uint32_t>(mrt.attrib3.tile_mode));
|
||||
}
|
||||
}
|
||||
if (rc.depth_clear_enable && hw.GetDepthClearValue() != 0.0f &&
|
||||
@@ -1233,7 +916,7 @@ void hw_check(const RenderCommandBuffer& buffer) {
|
||||
// EXIT_NOT_IMPLEMENTED(hw.GetStencilClearValue() != 0);
|
||||
}
|
||||
|
||||
void hw_print(const RenderCommandBuffer& buffer) {
|
||||
void hw_print(const CommandBuffer& buffer) {
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
const auto rt_slot = render_target_first_bound_slot(buffer);
|
||||
const auto& rt = hw.GetRenderTarget(rt_slot);
|
||||
@@ -1258,11 +941,12 @@ void hw_print(const RenderCommandBuffer& buffer) {
|
||||
"\t GetRenderTargetMask() = 0x%08" PRIx32 "\n"
|
||||
"\t GetDepthClearValue() = %f\n"
|
||||
"\t GetStencilClearValue() = %" PRIu8 "\n"
|
||||
"\t GetLineWidth() = %f\n",
|
||||
"\t GetLineWidth() = %f\n"
|
||||
"\t primitive_reset_index = 0x%08" PRIx32 "\n",
|
||||
hw.GetRenderTargetMask(), hw.GetDepthClearValue(), hw.GetStencilClearValue(),
|
||||
hw.GetLineWidth());
|
||||
hw.GetLineWidth(), hw.GetPrimitiveResetIndex());
|
||||
|
||||
LOGF("%s", Common::Concat(rt_print("RenderTraget:", rt), "").c_str());
|
||||
LOGF("%s", rt_print("RenderTraget:", rt).c_str());
|
||||
|
||||
ZPrint("DepthRenderTraget:", z);
|
||||
VpPrint("ScreenViewport:", vp, smc);
|
||||
|
||||
@@ -5,15 +5,13 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
class RenderCommandBuffer;
|
||||
class CommandBuffer;
|
||||
|
||||
namespace HW {
|
||||
class Context;
|
||||
class Shader;
|
||||
class UserConfig;
|
||||
struct RenderTarget;
|
||||
struct ScanModeControl;
|
||||
@@ -28,18 +26,17 @@ struct ScissorRect {
|
||||
};
|
||||
|
||||
uint32_t render_target_mask_slot(uint32_t mask, uint32_t slot);
|
||||
uint32_t render_target_first_bound_slot(const RenderCommandBuffer& buffer);
|
||||
uint32_t render_target_first_bound_slot(const CommandBuffer& buffer);
|
||||
bool graphics_debug_dump_enabled();
|
||||
void uc_print(const char* func, const HW::UserConfig& uc);
|
||||
void uc_check(const HW::UserConfig& uc);
|
||||
void sh_print(const char* func, const HW::Shader& uc);
|
||||
std::vector<std::string> rt_print(const char* func, const HW::RenderTarget& rt);
|
||||
bool RenderIsColorTileModeLinear(uint32_t tile_mode);
|
||||
void hw_print(const RenderCommandBuffer& buffer);
|
||||
void hw_check(const RenderCommandBuffer& buffer);
|
||||
std::string rt_print(const char* func, const HW::RenderTarget& rt);
|
||||
bool RenderIsColorTileModeLinear(Prospero::TileMode tile_mode);
|
||||
void hw_print(const CommandBuffer& buffer);
|
||||
void hw_check(const CommandBuffer& buffer);
|
||||
void LogDrawPhase(const char* draw_name, const char* phase);
|
||||
ScissorRect calc_final_scissor(const HW::ScreenViewport& vp, const HW::ScanModeControl& smc,
|
||||
vk::Extent2D extent);
|
||||
vk::Extent2D extent, uint32_t viewport_index);
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
|
||||
@@ -10,15 +10,16 @@
|
||||
#include "graphics/guest_gpu/hardwareContext.h"
|
||||
#include "graphics/guest_gpu/tile.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "graphics/host_gpu/renderer/commandScheduler.h"
|
||||
#include "graphics/host_gpu/renderer/debug.h"
|
||||
#include "graphics/host_gpu/renderer/image/imageView.h"
|
||||
#include "graphics/host_gpu/renderer/image/textureCommon.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <cstdarg>
|
||||
@@ -45,8 +46,12 @@ static vk::StencilOp ConvertStencilOp(uint8_t value, uint8_t write_mask, uint8_t
|
||||
switch (static_cast<Prospero::StencilOp>(value)) {
|
||||
case Prospero::StencilOp::kKeep: return vk::StencilOp::eKeep;
|
||||
case Prospero::StencilOp::kZero: return vk::StencilOp::eZero;
|
||||
case Prospero::StencilOp::kReplaceTest:
|
||||
case Prospero::StencilOp::kReplaceOp: return vk::StencilOp::eReplace;
|
||||
case Prospero::StencilOp::kReplaceTest: return vk::StencilOp::eReplace;
|
||||
case Prospero::StencilOp::kReplaceOp:
|
||||
if ((op_value & write_mask) == 0) {
|
||||
return vk::StencilOp::eZero;
|
||||
}
|
||||
return vk::StencilOp::eReplace;
|
||||
case Prospero::StencilOp::kAddClamp: return vk::StencilOp::eIncrementAndClamp;
|
||||
case Prospero::StencilOp::kSubClamp: return vk::StencilOp::eDecrementAndClamp;
|
||||
case Prospero::StencilOp::kInvert: return vk::StencilOp::eInvert;
|
||||
@@ -66,14 +71,42 @@ static vk::StencilOp ConvertStencilOp(uint8_t value, uint8_t write_mask, uint8_t
|
||||
}
|
||||
}
|
||||
|
||||
static bool UsesStencilOpValue(uint8_t fail, uint8_t pass, uint8_t depth_fail) {
|
||||
return fail == Prospero::GpuEnumValue(Prospero::StencilOp::kReplaceOp) ||
|
||||
pass == Prospero::GpuEnumValue(Prospero::StencilOp::kReplaceOp) ||
|
||||
depth_fail == Prospero::GpuEnumValue(Prospero::StencilOp::kReplaceOp);
|
||||
static vk::StencilOpState ConvertStencilState(
|
||||
uint8_t compare, const std::array<uint8_t, 3>& operations, uint8_t op_value,
|
||||
const vk::StencilOpState& state) {
|
||||
const auto test_value = state.reference;
|
||||
auto reference = test_value;
|
||||
auto required_bits = state.compareMask;
|
||||
if (compare == static_cast<uint8_t>(vk::CompareOp::eAlways) ||
|
||||
compare == static_cast<uint8_t>(vk::CompareOp::eNever)) {
|
||||
required_bits = 0;
|
||||
}
|
||||
std::array<vk::StencilOp, 3> converted {};
|
||||
for (size_t i = 0; i < operations.size(); i++) {
|
||||
converted[i] = ConvertStencilOp(operations[i], state.writeMask, op_value);
|
||||
if (converted[i] != vk::StencilOp::eReplace) {
|
||||
continue;
|
||||
}
|
||||
auto replacement = test_value;
|
||||
if (static_cast<Prospero::StencilOp>(operations[i]) == Prospero::StencilOp::kReplaceOp) {
|
||||
replacement = op_value;
|
||||
}
|
||||
if (((reference ^ replacement) & required_bits & state.writeMask) != 0) {
|
||||
DepthFatal("unsupported stencil replacement: compare=%u, compare mask=0x%02" PRIx32
|
||||
", write mask=0x%02" PRIx32 ", operation value=0x%02" PRIx8
|
||||
", test value=0x%02" PRIx32,
|
||||
compare, state.compareMask, state.writeMask, op_value, test_value);
|
||||
}
|
||||
// Vulkan shares one reference between comparison and every replacement on this face.
|
||||
reference = (reference & ~state.writeMask) | (replacement & state.writeMask);
|
||||
required_bits |= state.writeMask;
|
||||
}
|
||||
return {converted[0], converted[1], converted[2], static_cast<vk::CompareOp>(compare),
|
||||
state.compareMask, state.writeMask, reference};
|
||||
}
|
||||
|
||||
[[nodiscard]] static vk::Format ResolveHostDepthAttachmentFormat(const RenderCommandBuffer& buffer,
|
||||
const DepthFormatPolicy& policy,
|
||||
[[nodiscard]] static vk::Format ResolveHostDepthAttachmentFormat(const CommandBuffer& buffer,
|
||||
const DepthFormatPolicy& policy,
|
||||
bool has_stencil,
|
||||
uint32_t samples) {
|
||||
auto& graphics = buffer.GetGraphics();
|
||||
@@ -98,63 +131,20 @@ static bool UsesStencilOpValue(uint8_t fail, uint8_t pass, uint8_t depth_fail) {
|
||||
return vk::Format::eUndefined;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, RenderCommandBuffer& buffer,
|
||||
RenderDepthInfo& r) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
(void)submit_id;
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
const auto& z = hw.GetDepthRenderTarget();
|
||||
const auto& rc = hw.GetRenderControl();
|
||||
const auto& dc = hw.GetDepthControl();
|
||||
const auto& sc = hw.GetStencilControl();
|
||||
const auto& sm = hw.GetStencilMask();
|
||||
const bool has_stencil =
|
||||
z.stencil_info.format != Prospero::GpuEnumValue(Prospero::StencilFormat::kInvalid);
|
||||
const bool depth_active =
|
||||
dc.z_enable || dc.z_write_enable || dc.depth_bounds_enable || rc.depth_clear_enable;
|
||||
const bool stencil_active = has_stencil && (dc.stencil_enable || rc.stencil_clear_enable);
|
||||
if (!depth_active && !stencil_active) {
|
||||
return;
|
||||
}
|
||||
static TextureCache::ImageDesc MakeDepthTargetDesc(const CommandBuffer& buffer,
|
||||
const HW::DepthRenderTarget& z,
|
||||
bool write_buffer = false) {
|
||||
const bool has_stencil = z.stencil_info.format != Prospero::StencilFormat::kInvalid;
|
||||
const auto depth_address = write_buffer ? z.z_write_base_addr : z.z_read_base_addr;
|
||||
const auto stencil_address =
|
||||
write_buffer ? z.stencil_write_base_addr : z.stencil_read_base_addr;
|
||||
if (!z.z_info.HasValidTextureCompatibility() ||
|
||||
!z.stencil_info.HasValidTextureCompatibility()) {
|
||||
DepthFatal("invalid PS5 depth texture-compatibility encoding");
|
||||
}
|
||||
const bool attachment_unbound =
|
||||
z.z_info.format == Prospero::GpuEnumValue(Prospero::DepthFormat::kInvalid) &&
|
||||
z.stencil_info.format == Prospero::GpuEnumValue(Prospero::StencilFormat::kInvalid) &&
|
||||
z.z_info.num_samples == 0 &&
|
||||
z.z_info.texture_compatibility == Prospero::TextureCompatiblePlaneCompression::kDisable &&
|
||||
!z.z_info.expclear_enabled && !z.z_info.partially_resident && z.z_info.max_mip_level == 0 &&
|
||||
z.stencil_info.texture_compatibility == Prospero::TextureCompatibleStencil::kDisable &&
|
||||
!z.stencil_info.expclear_enabled && !z.stencil_info.partially_resident &&
|
||||
z.depth_view.slice_start == 0 && z.depth_view.slice_max == 0 &&
|
||||
z.depth_view.current_mip_level == 0 && !z.depth_view.depth_write_disable &&
|
||||
!z.depth_view.stencil_write_disable && z.depth_info.addr5_swizzle_mask == 0 &&
|
||||
z.depth_info.array_mode == 0 && z.depth_info.pipe_config == 0 &&
|
||||
z.depth_info.bank_width == 0 && z.depth_info.bank_height == 0 &&
|
||||
z.depth_info.macro_tile_aspect == 0 && z.depth_info.num_banks == 0 &&
|
||||
z.htile_surface.linear == 0 && z.htile_surface.full_cache == 0 &&
|
||||
z.htile_surface.htile_uses_preload_win == 0 && z.htile_surface.preload == 0 &&
|
||||
z.htile_surface.prefetch_width == 0 && z.htile_surface.prefetch_height == 0 &&
|
||||
z.htile_surface.dst_outside_zero_to_one == 0 && z.z_read_base_addr == 0 &&
|
||||
z.z_write_base_addr == 0 && z.stencil_read_base_addr == 0 &&
|
||||
z.stencil_write_base_addr == 0 && z.htile_data_base_addr == 0 &&
|
||||
// DB_DEPTH_SIZE_XY is independent state and may remain programmed after the attachment
|
||||
// formats and addresses are unbound. A zero encoding is the valid 1x1 value, so its
|
||||
// presence alone must not manufacture a depth attachment.
|
||||
!z.z_info.htile_acceleration && !z.width_height_valid && !z.pitch_height_valid &&
|
||||
z.size.x_max == 0 && z.size.y_max == 0 && z.pitch_div8_minus1 == 0 &&
|
||||
z.height_div8_minus1 == 0 && z.slice_div64_minus1 == 0 && z.width == 0 && z.height == 0;
|
||||
if (attachment_unbound) {
|
||||
static std::atomic_bool logged = false;
|
||||
if (!logged.exchange(true, std::memory_order_relaxed)) {
|
||||
LOGF("DepthTarget: ignoring enabled depth/stencil state without a bound attachment\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
const bool has_htile = z.z_info.htile_acceleration;
|
||||
const bool unsupported_shading_rate_encoding =
|
||||
z.shading_rate_encoding > 1 || (z.shading_rate_encoding != 0 && !has_htile);
|
||||
const auto samples = render_sample_count(z.z_info.num_samples);
|
||||
if (samples == 0) {
|
||||
DepthFatal("unsupported depth fragment count: %u", z.z_info.num_samples);
|
||||
@@ -169,33 +159,19 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, RenderCommandB
|
||||
DepthFatal("invalid depth view: base=%u last=%u", z.depth_view.slice_start,
|
||||
z.depth_view.slice_max);
|
||||
}
|
||||
if (rc.resummarize_enable || rc.copy_centroid || rc.copy_sample != 0 ||
|
||||
z.z_info.expclear_enabled || z.stencil_info.expclear_enabled ||
|
||||
z.z_info.partially_resident || z.stencil_info.partially_resident ||
|
||||
z.z_info.max_mip_level != 0 || z.depth_view.current_mip_level != 0 ||
|
||||
z.depth_info.addr5_swizzle_mask != 0 || z.depth_info.array_mode != 0 ||
|
||||
z.depth_info.pipe_config != 0 || z.depth_info.bank_width != 0 ||
|
||||
z.depth_info.bank_height != 0 || z.depth_info.macro_tile_aspect != 0 ||
|
||||
z.depth_info.num_banks != 0 || z.htile_surface.linear != 0 ||
|
||||
z.htile_surface.full_cache != 0 || z.htile_surface.htile_uses_preload_win != 0 ||
|
||||
z.htile_surface.preload != 0 || z.htile_surface.prefetch_width != 0 ||
|
||||
z.htile_surface.prefetch_height != 0 || z.htile_surface.dst_outside_zero_to_one != 0 ||
|
||||
z.z_read_base_addr == 0 || z.z_write_base_addr != z.z_read_base_addr ||
|
||||
(z.z_read_base_addr & 0xffffu) != 0 ||
|
||||
dc.zfunc > static_cast<uint8_t>(vk::CompareOp::eAlways)) {
|
||||
if (z.z_info.expclear_enabled || z.stencil_info.expclear_enabled ||
|
||||
z.z_info.partially_resident ||
|
||||
z.stencil_info.partially_resident || z.z_info.max_mip_level != 0 ||
|
||||
z.depth_view.current_mip_level != 0 || unsupported_shading_rate_encoding ||
|
||||
depth_address == 0 || (depth_address & 0xffffu) != 0) {
|
||||
DepthFatal("unsupported depth register state");
|
||||
}
|
||||
if (has_stencil) {
|
||||
if (z.stencil_info.format != Prospero::GpuEnumValue(Prospero::StencilFormat::k8UInt) ||
|
||||
!htile_stencil_compat || z.stencil_read_base_addr == 0 ||
|
||||
z.stencil_write_base_addr != z.stencil_read_base_addr ||
|
||||
(z.stencil_read_base_addr & 0xffffu) != 0 || z.depth_view.stencil_write_disable) {
|
||||
if (z.stencil_info.format != Prospero::StencilFormat::k8UInt || !htile_stencil_compat ||
|
||||
stencil_address == 0 || (stencil_address & 0xffffu) != 0) {
|
||||
DepthFatal("unsupported stencil attachment state");
|
||||
}
|
||||
} else if (z.stencil_read_base_addr != 0 || z.stencil_write_base_addr != 0 ||
|
||||
!htile_stencil_compat ||
|
||||
z.stencil_info.texture_compatibility !=
|
||||
Prospero::TextureCompatibleStencil::kDisable) {
|
||||
} else if (z.stencil_read_base_addr != 0 || z.stencil_write_base_addr != 0) {
|
||||
DepthFatal("stencil state without an active stencil attachment");
|
||||
}
|
||||
if (has_htile) {
|
||||
@@ -206,37 +182,27 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, RenderCommandB
|
||||
DepthFatal("HTile clear tracking supports at most 32 slices");
|
||||
}
|
||||
}
|
||||
const bool size_xy_valid = z.size.valid;
|
||||
const bool wh_valid = z.width_height_valid && z.width != 0 && z.height != 0;
|
||||
if (!size_xy_valid && !wh_valid) {
|
||||
if (!z.size.valid) {
|
||||
DepthFatal("missing depth extent");
|
||||
}
|
||||
const uint32_t width = size_xy_valid ? static_cast<uint32_t>(z.size.x_max) + 1u : z.width;
|
||||
const uint32_t height = size_xy_valid ? static_cast<uint32_t>(z.size.y_max) + 1u : z.height;
|
||||
if (width > 16384 || height > 16384 ||
|
||||
(size_xy_valid && wh_valid && (width != z.width || height != z.height)) ||
|
||||
(!z.pitch_height_valid &&
|
||||
(z.pitch_div8_minus1 != 0 || z.height_div8_minus1 != 0 || z.slice_div64_minus1 != 0))) {
|
||||
DepthFatal("inconsistent depth extent or encoded layout");
|
||||
const uint32_t width = static_cast<uint32_t>(z.size.x_max) + 1u;
|
||||
const uint32_t height = static_cast<uint32_t>(z.size.y_max) + 1u;
|
||||
if (width > 16384 || height > 16384) {
|
||||
DepthFatal("invalid depth extent");
|
||||
}
|
||||
const auto* policy = FindDepthFormatPolicy(z.z_info.format);
|
||||
if (policy == nullptr) {
|
||||
DepthFatal("unsupported depth/stencil format pair");
|
||||
}
|
||||
const auto ideal_format = DepthAttachmentFormat(*policy, has_stencil);
|
||||
r.format = ResolveHostDepthAttachmentFormat(buffer, *policy, has_stencil, samples);
|
||||
if (r.format == vk::Format::eUndefined) {
|
||||
const auto format = ResolveHostDepthAttachmentFormat(buffer, *policy, has_stencil, samples);
|
||||
if (format == vk::Format::eUndefined) {
|
||||
DepthFatal("no host depth/stencil format supports required usage for %s",
|
||||
VulkanToString(ideal_format).c_str());
|
||||
vk::to_string(ideal_format).c_str());
|
||||
}
|
||||
const uint32_t guest_format = Prospero::GpuEnumValue(policy->guest_format);
|
||||
const auto guest_format = policy->guest_format;
|
||||
const uint32_t bytes = policy->bytes_per_element;
|
||||
const auto pitch = TileGetDepthPitch(width, bytes, z.z_info.num_samples);
|
||||
if (z.pitch_height_valid && ((static_cast<uint64_t>(z.pitch_div8_minus1) + 1u) * 8u != pitch ||
|
||||
(static_cast<uint64_t>(z.height_div8_minus1) + 1u) * 8u !=
|
||||
((static_cast<uint64_t>(height) + 7u) & ~7ull))) {
|
||||
DepthFatal("encoded depth pitch or height mismatch");
|
||||
}
|
||||
TileSizeAlign depth_size {};
|
||||
TileSizeAlign stencil_size {};
|
||||
TileSizeAlign htile_size {};
|
||||
@@ -247,14 +213,6 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, RenderCommandB
|
||||
(has_htile != (htile_size.align == 32768 && htile_size.size != 0))) {
|
||||
DepthFatal("unsupported depth/stencil/HTile footprint");
|
||||
}
|
||||
if (z.pitch_height_valid &&
|
||||
(static_cast<uint64_t>(z.slice_div64_minus1) + 1u) * 64u != depth_size.size) {
|
||||
DepthFatal("depth footprint mismatch: extent=%ux%u pitch=%u expected=0x%016" PRIx64
|
||||
" align=0x%016" PRIx64 " encoded_valid=%u encoded=0x%016" PRIx64,
|
||||
width, height, pitch, depth_size.size, depth_size.align,
|
||||
z.pitch_height_valid ? 1u : 0u,
|
||||
(static_cast<uint64_t>(z.slice_div64_minus1) + 1u) * 64u);
|
||||
}
|
||||
if (depth_size.size > UINT64_MAX / view.image_layers ||
|
||||
stencil_size.size > UINT64_MAX / view.image_layers ||
|
||||
htile_size.size > UINT64_MAX / view.image_layers) {
|
||||
@@ -263,79 +221,17 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, RenderCommandB
|
||||
const auto depth_backing_size = depth_size.size * view.image_layers;
|
||||
const auto stencil_backing_size = stencil_size.size * view.image_layers;
|
||||
const auto htile_backing_size = htile_size.size * view.image_layers;
|
||||
if (depth_backing_size > TRACKER_ADDRESS_SIZE - z.z_read_base_addr ||
|
||||
(has_stencil && stencil_backing_size > TRACKER_ADDRESS_SIZE - z.stencil_read_base_addr) ||
|
||||
(has_htile && htile_backing_size > TRACKER_ADDRESS_SIZE - z.htile_data_base_addr)) {
|
||||
if (!GuestRange {depth_address, depth_backing_size}.Valid() ||
|
||||
(has_stencil && !GuestRange {stencil_address, stencil_backing_size}.Valid()) ||
|
||||
(has_htile && !GuestRange {z.htile_data_base_addr, htile_backing_size}.Valid())) {
|
||||
DepthFatal("layered depth backing range is invalid");
|
||||
}
|
||||
r.htile = has_htile;
|
||||
r.width = width;
|
||||
r.height = height;
|
||||
r.samples = samples;
|
||||
r.depth_buffer_size = depth_backing_size;
|
||||
r.depth_buffer_vaddr = z.z_read_base_addr;
|
||||
r.stencil_buffer_size = has_stencil ? stencil_backing_size : 0;
|
||||
r.stencil_buffer_vaddr = has_stencil ? z.stencil_read_base_addr : 0;
|
||||
r.htile_buffer_size = has_htile ? htile_backing_size : 0;
|
||||
r.htile_buffer_vaddr = has_htile ? z.htile_data_base_addr : 0;
|
||||
r.depth_clear_enable = rc.depth_clear_enable;
|
||||
r.depth_meta_clear_enable = false;
|
||||
r.depth_load_clear_enable = r.depth_clear_enable;
|
||||
r.depth_clear_value = hw.GetDepthClearValue();
|
||||
r.depth_test_enable = dc.z_enable;
|
||||
r.depth_write_enable = dc.z_write_enable && !z.depth_view.depth_write_disable;
|
||||
r.depth_compare_op = static_cast<vk::CompareOp>(dc.zfunc);
|
||||
|
||||
r.depth_bounds_test_enable = dc.depth_bounds_enable;
|
||||
r.depth_min_bounds = hw.GetDepthBoundsMin();
|
||||
r.depth_max_bounds = hw.GetDepthBoundsMax();
|
||||
|
||||
r.stencil_clear_enable = has_stencil && rc.stencil_clear_enable;
|
||||
r.stencil_clear_value = hw.GetStencilClearValue();
|
||||
r.stencil_test_enable = has_stencil && dc.stencil_enable;
|
||||
if (r.stencil_test_enable) {
|
||||
const uint8_t front_write_mask = rc.stencil_clear_enable ? 0 : sm.stencil_writemask;
|
||||
const uint8_t back_write_mask = rc.stencil_clear_enable ? 0 : sm.stencil_writemask_bf;
|
||||
if (dc.stencilfunc > static_cast<uint8_t>(vk::CompareOp::eAlways) ||
|
||||
(dc.backface_enable &&
|
||||
dc.stencilfunc_bf > static_cast<uint8_t>(vk::CompareOp::eAlways)) ||
|
||||
(UsesStencilOpValue(sc.stencil_fail, sc.stencil_zpass, sc.stencil_zfail) &&
|
||||
sm.stencil_opval != sm.stencil_testval) ||
|
||||
(dc.backface_enable &&
|
||||
UsesStencilOpValue(sc.stencil_fail_bf, sc.stencil_zpass_bf, sc.stencil_zfail_bf) &&
|
||||
sm.stencil_opval_bf != sm.stencil_testval_bf)) {
|
||||
DepthFatal("unsupported stencil compare or replacement state");
|
||||
}
|
||||
r.stencil_static_front = {
|
||||
ConvertStencilOp(sc.stencil_fail, front_write_mask, sm.stencil_opval),
|
||||
ConvertStencilOp(sc.stencil_zpass, front_write_mask, sm.stencil_opval),
|
||||
ConvertStencilOp(sc.stencil_zfail, front_write_mask, sm.stencil_opval),
|
||||
static_cast<vk::CompareOp>(dc.stencilfunc)};
|
||||
r.stencil_dynamic_front = {sm.stencil_mask, front_write_mask, sm.stencil_testval};
|
||||
if (dc.backface_enable) {
|
||||
r.stencil_static_back = {
|
||||
ConvertStencilOp(sc.stencil_fail_bf, back_write_mask, sm.stencil_opval_bf),
|
||||
ConvertStencilOp(sc.stencil_zpass_bf, back_write_mask, sm.stencil_opval_bf),
|
||||
ConvertStencilOp(sc.stencil_zfail_bf, back_write_mask, sm.stencil_opval_bf),
|
||||
static_cast<vk::CompareOp>(dc.stencilfunc_bf)};
|
||||
r.stencil_dynamic_back = {sm.stencil_mask_bf, back_write_mask, sm.stencil_testval_bf};
|
||||
} else {
|
||||
r.stencil_static_back = r.stencil_static_front;
|
||||
r.stencil_dynamic_back = r.stencil_dynamic_front;
|
||||
}
|
||||
}
|
||||
r.vaddr_num = has_stencil ? 2 : 1;
|
||||
r.vaddr[0] = r.depth_buffer_vaddr;
|
||||
r.size[0] = r.depth_buffer_size;
|
||||
if (has_stencil) {
|
||||
r.vaddr[1] = r.stencil_buffer_vaddr;
|
||||
r.size[1] = r.stencil_buffer_size;
|
||||
}
|
||||
TextureCache::ImageDesc desc {};
|
||||
desc.type = TextureCache::BindingType::DepthTarget;
|
||||
desc.info.data = {r.depth_buffer_vaddr, r.depth_buffer_size};
|
||||
desc.info.stencil = {r.stencil_buffer_vaddr, r.stencil_buffer_size};
|
||||
desc.info.pixel_format = r.format;
|
||||
desc.info.data = {depth_address, depth_backing_size};
|
||||
desc.info.stencil =
|
||||
has_stencil ? GuestRange {stencil_address, stencil_backing_size} : GuestRange {};
|
||||
desc.info.pixel_format = format;
|
||||
desc.info.guest_format = guest_format;
|
||||
desc.info.type = Prospero::ImageType::kColor2D;
|
||||
desc.info.extent = {width, height, 1};
|
||||
@@ -343,29 +239,182 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, RenderCommandB
|
||||
desc.info.pitch = pitch;
|
||||
desc.info.bytes_per_block = bytes;
|
||||
desc.info.samples = samples;
|
||||
desc.info.tile_mode = Prospero::GpuEnumValue(Prospero::TileMode::kDepth);
|
||||
desc.info.mip_layout[0] = {0, r.depth_buffer_size, pitch, height};
|
||||
desc.info.metadata.range = {r.htile_buffer_vaddr, r.htile_buffer_size};
|
||||
desc.info.tile_mode = Prospero::TileMode::kDepth;
|
||||
desc.info.mip_layout[0] = {0, depth_backing_size, pitch, height};
|
||||
desc.info.metadata.range =
|
||||
has_htile ? GuestRange {z.htile_data_base_addr, htile_backing_size} : GuestRange {};
|
||||
desc.info.metadata.kind = has_htile ? ImageMetadataKind::Htile : ImageMetadataKind::None;
|
||||
desc.info.metadata.stencil_compressed =
|
||||
has_stencil && has_htile && !z.stencil_info.htile_stencil_disabled;
|
||||
desc.view_info.format = r.format;
|
||||
desc.view_info.format = format;
|
||||
desc.view_info.type =
|
||||
view.layer_count == 1 ? vk::ImageViewType::e2D : vk::ImageViewType::e2DArray;
|
||||
desc.view_info.aspect = ImageViewOps::DepthAspectMask(r.format);
|
||||
desc.view_info.aspect = ImageViewOps::DepthAspectMask(format);
|
||||
desc.view_info.base_level = 0;
|
||||
desc.view_info.level_count = 1;
|
||||
desc.view_info.base_layer = view.base_layer;
|
||||
desc.view_info.layer_count = view.layer_count;
|
||||
desc.view_info.usage = vk::ImageUsageFlagBits::eDepthStencilAttachment;
|
||||
r.desc = std::move(desc);
|
||||
auto& cache = m_context.GetTextureCache();
|
||||
r.image_id = cache.FindImage(r.desc);
|
||||
r.image_view = nullptr;
|
||||
return desc;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
void RenderExecutor::ResolveRenderDepthTarget(CommandBuffer& buffer, RenderDepthInfo& r) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
const auto& z = hw.GetDepthRenderTarget();
|
||||
const auto& rc = hw.GetRenderControl();
|
||||
const auto& dc = hw.GetDepthControl();
|
||||
const auto& sc = hw.GetStencilControl();
|
||||
const auto& sm = hw.GetStencilMask();
|
||||
const bool has_stencil = z.stencil_info.format != Prospero::StencilFormat::kInvalid;
|
||||
const bool depth_active = dc.z_enable || dc.depth_bounds_enable ||
|
||||
rc.depth_clear_enable || rc.copy_depth_to_color;
|
||||
const bool stencil_active =
|
||||
has_stencil && (dc.stencil_enable || rc.stencil_clear_enable || rc.copy_stencil_to_color);
|
||||
if (!depth_active && !stencil_active) {
|
||||
return;
|
||||
}
|
||||
const bool attachment_unbound =
|
||||
z.z_info.format == Prospero::DepthFormat::kInvalid &&
|
||||
z.stencil_info.format == Prospero::StencilFormat::kInvalid && z.z_info.num_samples == 0 &&
|
||||
z.z_info.texture_compatibility == Prospero::TextureCompatiblePlaneCompression::kDisable &&
|
||||
!z.z_info.expclear_enabled && !z.z_info.partially_resident && z.z_info.max_mip_level == 0 &&
|
||||
z.stencil_info.texture_compatibility == Prospero::TextureCompatibleStencil::kDisable &&
|
||||
!z.stencil_info.expclear_enabled && !z.stencil_info.partially_resident &&
|
||||
z.depth_view.slice_start == 0 && z.depth_view.slice_max == 0 &&
|
||||
z.depth_view.current_mip_level == 0 && !z.depth_view.depth_write_disable &&
|
||||
!z.depth_view.stencil_write_disable && z.z_read_base_addr == 0 && z.z_write_base_addr == 0 &&
|
||||
z.stencil_read_base_addr == 0 && z.stencil_write_base_addr == 0 &&
|
||||
z.htile_data_base_addr == 0 &&
|
||||
// DB_DEPTH_SIZE_XY is independent state and may remain programmed after the attachment
|
||||
// formats and addresses are unbound. A zero encoding is the valid 1x1 value, so its
|
||||
// presence alone must not manufacture a depth attachment.
|
||||
!z.z_info.htile_acceleration && z.shading_rate_encoding == 0 && z.size.x_max == 0 &&
|
||||
z.size.y_max == 0;
|
||||
if (attachment_unbound) {
|
||||
static std::atomic_bool logged = false;
|
||||
if (!logged.exchange(true, std::memory_order_relaxed)) {
|
||||
LOGF("DepthTarget: ignoring enabled depth/stencil state without a bound attachment\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (rc.copy_depth_to_color || rc.copy_stencil_to_color || rc.copy_centroid ||
|
||||
rc.copy_sample != 0 || dc.zfunc > static_cast<uint8_t>(vk::CompareOp::eAlways) ||
|
||||
(!z.depth_view.depth_write_disable && z.z_write_base_addr != z.z_read_base_addr) ||
|
||||
(has_stencil && !z.depth_view.stencil_write_disable &&
|
||||
z.stencil_write_base_addr != z.stencil_read_base_addr)) {
|
||||
DepthFatal("unsupported depth register state");
|
||||
}
|
||||
r.desc = MakeDepthTargetDesc(buffer, z);
|
||||
r.depth_clear_enable = rc.depth_clear_enable;
|
||||
r.depth_load_clear_enable = r.depth_clear_enable;
|
||||
r.depth_clear_value = hw.GetDepthClearValue();
|
||||
r.depth_test_enable = dc.z_enable;
|
||||
r.depth_write_enable = r.depth_test_enable && dc.z_write_enable &&
|
||||
!z.depth_view.depth_write_disable && !r.depth_clear_enable;
|
||||
r.depth_compare_op = static_cast<vk::CompareOp>(dc.zfunc);
|
||||
|
||||
r.depth_bounds_test_enable = dc.depth_bounds_enable;
|
||||
r.depth_min_bounds = hw.GetDepthBoundsMin();
|
||||
r.depth_max_bounds = hw.GetDepthBoundsMax();
|
||||
|
||||
r.stencil_clear_enable =
|
||||
has_stencil && rc.stencil_clear_enable && !z.depth_view.stencil_write_disable;
|
||||
r.stencil_clear_value = hw.GetStencilClearValue();
|
||||
r.stencil_test_enable = has_stencil && dc.stencil_enable;
|
||||
if (r.stencil_test_enable) {
|
||||
const bool stencil_ops_disabled =
|
||||
rc.stencil_clear_enable || z.depth_view.stencil_write_disable;
|
||||
const uint8_t front_write_mask = stencil_ops_disabled ? 0 : sm.stencil_writemask;
|
||||
const uint8_t back_write_mask = stencil_ops_disabled ? 0 : sm.stencil_writemask_bf;
|
||||
if (dc.stencilfunc > static_cast<uint8_t>(vk::CompareOp::eAlways) ||
|
||||
(dc.backface_enable &&
|
||||
dc.stencilfunc_bf > static_cast<uint8_t>(vk::CompareOp::eAlways))) {
|
||||
DepthFatal("unsupported stencil compare state");
|
||||
}
|
||||
r.stencil_front = ConvertStencilState(
|
||||
dc.stencilfunc, {sc.stencil_fail, sc.stencil_zpass, sc.stencil_zfail},
|
||||
sm.stencil_opval, {.compareMask = sm.stencil_mask, .writeMask = front_write_mask,
|
||||
.reference = sm.stencil_testval});
|
||||
if (dc.backface_enable) {
|
||||
r.stencil_back = ConvertStencilState(
|
||||
dc.stencilfunc_bf, {sc.stencil_fail_bf, sc.stencil_zpass_bf, sc.stencil_zfail_bf},
|
||||
sm.stencil_opval_bf, {.compareMask = sm.stencil_mask_bf, .writeMask = back_write_mask,
|
||||
.reference = sm.stencil_testval_bf});
|
||||
} else {
|
||||
r.stencil_back = r.stencil_front;
|
||||
}
|
||||
}
|
||||
auto& cache = m_context.GetTextureCache();
|
||||
r.image_id = cache.FindImage(r.desc);
|
||||
BindRenderTarget(r.image_id);
|
||||
}
|
||||
|
||||
bool RenderExecutor::DepthStencilCopy(CommandBuffer& buffer) {
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
const auto& z = hw.GetDepthRenderTarget();
|
||||
const auto& override = hw.GetDepthRenderOverride();
|
||||
if (hw.GetColorControl().mode != 0) {
|
||||
return false;
|
||||
}
|
||||
const bool depth_copy = override.force_z_dirty && override.force_z_valid &&
|
||||
z.z_info.format != Prospero::DepthFormat::kInvalid &&
|
||||
z.z_read_base_addr != 0 && z.z_write_base_addr != 0 &&
|
||||
z.z_read_base_addr != z.z_write_base_addr;
|
||||
const bool stencil_copy = override.force_stencil_dirty && override.force_stencil_valid &&
|
||||
z.stencil_info.format != Prospero::StencilFormat::kInvalid &&
|
||||
z.stencil_read_base_addr != 0 && z.stencil_write_base_addr != 0 &&
|
||||
z.stencil_read_base_addr != z.stencil_write_base_addr;
|
||||
if (!depth_copy && !stencil_copy) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto read_desc = MakeDepthTargetDesc(buffer, z);
|
||||
auto write_desc = MakeDepthTargetDesc(buffer, z, true);
|
||||
auto& cache = m_context.GetTextureCache();
|
||||
const auto read_id = cache.FindImage(read_desc);
|
||||
BindRenderTarget(read_id);
|
||||
const auto write_id = cache.FindImage(write_desc);
|
||||
BindRenderTarget(write_id);
|
||||
cache.UpdateImage(read_id);
|
||||
cache.UpdateImage(write_id);
|
||||
cache.MarkGpuWritten(write_id);
|
||||
auto& source = cache.GetImage(read_id);
|
||||
auto& destination = cache.GetImage(write_id);
|
||||
EXIT_IF(read_id == write_id || source.backing.format != destination.backing.format);
|
||||
|
||||
auto& scheduler = m_context.GetCommandScheduler();
|
||||
scheduler.EndRendering();
|
||||
const auto command = scheduler.Current().Handle();
|
||||
const ImageSubresourceRange range {read_desc.view_info.base_level, 1,
|
||||
read_desc.view_info.base_layer,
|
||||
read_desc.view_info.layer_count};
|
||||
source.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead,
|
||||
range, command);
|
||||
destination.Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite,
|
||||
range, command);
|
||||
std::array<vk::ImageCopy, 2> regions {};
|
||||
uint32_t count = 0;
|
||||
for (const auto aspect: {vk::ImageAspectFlagBits::eDepth, vk::ImageAspectFlagBits::eStencil}) {
|
||||
if ((aspect == vk::ImageAspectFlagBits::eDepth && !depth_copy) ||
|
||||
(aspect == vk::ImageAspectFlagBits::eStencil && !stencil_copy)) {
|
||||
continue;
|
||||
}
|
||||
auto& region = regions[count++];
|
||||
region.srcSubresource = {aspect, range.base_level, range.base_layer,
|
||||
range.layer_count};
|
||||
region.dstSubresource = region.srcSubresource;
|
||||
region.extent = write_desc.info.extent;
|
||||
}
|
||||
command.copyImage(source.backing.image, vk::ImageLayout::eTransferSrcOptimal,
|
||||
destination.backing.image, vk::ImageLayout::eTransferDstOptimal,
|
||||
count, regions.data());
|
||||
return true;
|
||||
}
|
||||
|
||||
vk::ImageAspectFlags RenderDepthInfo::AttachmentWriteAspects() const {
|
||||
const auto format = desc.view_info.format;
|
||||
if (format == vk::Format::eUndefined) {
|
||||
return {};
|
||||
}
|
||||
@@ -373,21 +422,20 @@ vk::ImageAspectFlags RenderDepthInfo::AttachmentWriteAspects() const {
|
||||
const auto available = ImageViewOps::DepthAspectMask(format);
|
||||
vk::ImageAspectFlags writes {};
|
||||
if ((available & vk::ImageAspectFlagBits::eDepth) &&
|
||||
(depth_load_clear_enable || (depth_test_enable && depth_write_enable))) {
|
||||
(depth_load_clear_enable || depth_write_enable)) {
|
||||
writes |= vk::ImageAspectFlagBits::eDepth;
|
||||
}
|
||||
if (!(available & vk::ImageAspectFlagBits::eStencil)) {
|
||||
return writes;
|
||||
}
|
||||
|
||||
const auto face_writes = [&](const PipelineStencilStaticState& state,
|
||||
const PipelineStencilDynamicState& dynamic) {
|
||||
if (dynamic.writeMask == 0) {
|
||||
const auto face_writes = [&](const vk::StencilOpState& state) {
|
||||
if (state.writeMask == 0) {
|
||||
return false;
|
||||
}
|
||||
bool can_pass = state.compareOp != vk::CompareOp::eNever;
|
||||
bool can_fail = state.compareOp != vk::CompareOp::eAlways;
|
||||
if (dynamic.compareMask == 0) {
|
||||
if (state.compareMask == 0) {
|
||||
switch (state.compareOp) {
|
||||
case vk::CompareOp::eEqual:
|
||||
case vk::CompareOp::eLessOrEqual:
|
||||
@@ -413,8 +461,7 @@ vk::ImageAspectFlags RenderDepthInfo::AttachmentWriteAspects() const {
|
||||
(can_pass && depth_fail && state.depthFailOp != vk::StencilOp::eKeep);
|
||||
};
|
||||
if (stencil_clear_enable ||
|
||||
(stencil_test_enable && (face_writes(stencil_static_front, stencil_dynamic_front) ||
|
||||
face_writes(stencil_static_back, stencil_dynamic_back)))) {
|
||||
(stencil_test_enable && (face_writes(stencil_front) || face_writes(stencil_back)))) {
|
||||
writes |= vk::ImageAspectFlagBits::eStencil;
|
||||
}
|
||||
return writes;
|
||||
|
||||
@@ -11,34 +11,21 @@
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
class RenderCommandBuffer;
|
||||
|
||||
// Without a stencil plane, Hi-Stencil fields are inactive. An active plane is compatible when
|
||||
// Hi-Stencil is disabled or HTile backing is present.
|
||||
inline constexpr bool depth_htile_stencil_acceleration_compatible(bool has_stencil, bool has_htile,
|
||||
bool htile_stencil_disabled) {
|
||||
return htile_stencil_disabled || (has_stencil && has_htile);
|
||||
return !has_stencil || htile_stencil_disabled || has_htile;
|
||||
}
|
||||
|
||||
struct RenderDepthInfo {
|
||||
// Discovery keeps guest image information but can remap the view into a larger cache image.
|
||||
TextureCache::ImageDesc desc;
|
||||
vk::Format format = vk::Format::eUndefined;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
uint32_t samples = 1;
|
||||
bool htile = false;
|
||||
uint64_t depth_buffer_size = 0;
|
||||
uint64_t depth_buffer_vaddr = 0;
|
||||
uint64_t depth_tile_swizzle = 0;
|
||||
uint64_t stencil_buffer_size = 0;
|
||||
uint64_t stencil_buffer_vaddr = 0;
|
||||
uint64_t stencil_tile_swizzle = 0;
|
||||
uint64_t htile_buffer_size = 0;
|
||||
uint64_t htile_buffer_vaddr = 0;
|
||||
uint64_t htile_tile_swizzle = 0;
|
||||
bool depth_clear_enable = false;
|
||||
bool depth_load_clear_enable = false;
|
||||
bool depth_meta_clear_enable = false;
|
||||
float depth_clear_value = 0.0f;
|
||||
bool depth_test_enable = false;
|
||||
// Effective draw writes; discovery applies test, target-write and clear controls.
|
||||
bool depth_write_enable = false;
|
||||
vk::CompareOp depth_compare_op = vk::CompareOp::eNever;
|
||||
bool depth_bounds_test_enable = false;
|
||||
@@ -47,29 +34,20 @@ struct RenderDepthInfo {
|
||||
bool stencil_clear_enable = false;
|
||||
uint8_t stencil_clear_value = 0;
|
||||
bool stencil_test_enable = false;
|
||||
PipelineStencilStaticState stencil_static_front;
|
||||
PipelineStencilStaticState stencil_static_back;
|
||||
PipelineStencilDynamicState stencil_dynamic_front;
|
||||
PipelineStencilDynamicState stencil_dynamic_back;
|
||||
vk::StencilOpState stencil_front;
|
||||
vk::StencilOpState stencil_back;
|
||||
ImageId image_id;
|
||||
vk::ImageView image_view = nullptr;
|
||||
uint64_t vaddr[3] = {};
|
||||
uint64_t size[3] = {};
|
||||
int vaddr_num = 0;
|
||||
|
||||
[[nodiscard]] vk::ImageAspectFlags AttachmentWriteAspects() const;
|
||||
};
|
||||
|
||||
inline bool depth_attachment_read_only(const RenderDepthInfo& depth) {
|
||||
return !depth.AttachmentWriteAspects();
|
||||
}
|
||||
|
||||
inline vk::ImageLayout depth_attachment_layout(const RenderDepthInfo& depth) {
|
||||
const auto available = ImageViewOps::DepthAspectMask(depth.format);
|
||||
const auto available = ImageViewOps::DepthAspectMask(depth.desc.view_info.format);
|
||||
const auto writes = depth.AttachmentWriteAspects();
|
||||
const bool has_depth = static_cast<bool>(available & vk::ImageAspectFlagBits::eDepth);
|
||||
const bool has_stencil = static_cast<bool>(available & vk::ImageAspectFlagBits::eStencil);
|
||||
const bool depth_write = static_cast<bool>(writes & vk::ImageAspectFlagBits::eDepth);
|
||||
// LoadOp clears are separate from guest depth writes.
|
||||
const bool depth_write = depth.depth_write_enable;
|
||||
const bool stencil_write = static_cast<bool>(writes & vk::ImageAspectFlagBits::eStencil);
|
||||
if (!has_stencil) {
|
||||
return depth_write ? vk::ImageLayout::eDepthAttachmentOptimal
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <iterator>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
@@ -23,7 +22,6 @@ BlitHelper::BlitHelper(GraphicContext& graphics, CommandScheduler& scheduler)
|
||||
texture_binding.stageFlags = vk::ShaderStageFlagBits::eFragment;
|
||||
|
||||
vk::DescriptorSetLayoutCreateInfo descriptor_info {};
|
||||
descriptor_info.sType = vk::StructureType::eDescriptorSetLayoutCreateInfo;
|
||||
descriptor_info.flags = vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR;
|
||||
descriptor_info.bindingCount = 1;
|
||||
descriptor_info.pBindings = &texture_binding;
|
||||
@@ -32,16 +30,14 @@ BlitHelper::BlitHelper(GraphicContext& graphics, CommandScheduler& scheduler)
|
||||
"create BlitHelper descriptor layout");
|
||||
|
||||
vk::PipelineLayoutCreateInfo layout_info {};
|
||||
layout_info.sType = vk::StructureType::ePipelineLayoutCreateInfo;
|
||||
layout_info.setLayoutCount = 1;
|
||||
layout_info.pSetLayouts = &m_descriptor_layout;
|
||||
RequireVulkanSuccess(
|
||||
m_graphics.device.createPipelineLayout(&layout_info, nullptr, &m_pipeline_layout),
|
||||
"create BlitHelper pipeline layout");
|
||||
|
||||
m_vertex_shader = CreateShader(GPU_BLIT_FS_TRIANGLE_SPV, std::size(GPU_BLIT_FS_TRIANGLE_SPV));
|
||||
m_fragment_shader =
|
||||
CreateShader(GPU_BLIT_COLOR_TO_MS_DEPTH_SPV, std::size(GPU_BLIT_COLOR_TO_MS_DEPTH_SPV));
|
||||
m_vertex_shader = CompileSPV(GPU_BLIT_FS_TRIANGLE_SPV, m_graphics.device);
|
||||
m_fragment_shader = CompileSPV(GPU_BLIT_COLOR_TO_MS_DEPTH_SPV, m_graphics.device);
|
||||
}
|
||||
|
||||
BlitHelper::~BlitHelper() {
|
||||
@@ -62,18 +58,6 @@ BlitHelper::~BlitHelper() {
|
||||
}
|
||||
}
|
||||
|
||||
vk::ShaderModule BlitHelper::CreateShader(const uint32_t* code, size_t words) const {
|
||||
EXIT_IF(code == nullptr || words == 0);
|
||||
vk::ShaderModuleCreateInfo create {};
|
||||
create.sType = vk::StructureType::eShaderModuleCreateInfo;
|
||||
create.codeSize = words * sizeof(uint32_t);
|
||||
create.pCode = code;
|
||||
vk::ShaderModule module = nullptr;
|
||||
RequireVulkanSuccess(m_graphics.device.createShaderModule(&create, nullptr, &module),
|
||||
"create BlitHelper shader module");
|
||||
return module;
|
||||
}
|
||||
|
||||
vk::Pipeline BlitHelper::GetPipeline(PipelineKey key) {
|
||||
const auto cached = std::ranges::find(m_pipelines, key, &Pipeline::key);
|
||||
if (cached != m_pipelines.end()) {
|
||||
@@ -84,51 +68,37 @@ vk::Pipeline BlitHelper::GetPipeline(PipelineKey key) {
|
||||
EXIT_IF(samples == vk::SampleCountFlagBits {} || key.format == vk::Format::eUndefined);
|
||||
|
||||
std::array<vk::PipelineShaderStageCreateInfo, 2> stages {};
|
||||
stages[0].sType = vk::StructureType::ePipelineShaderStageCreateInfo;
|
||||
stages[0].stage = vk::ShaderStageFlagBits::eVertex;
|
||||
stages[0].module = m_vertex_shader;
|
||||
stages[0].pName = "main";
|
||||
stages[1].sType = vk::StructureType::ePipelineShaderStageCreateInfo;
|
||||
stages[1].stage = vk::ShaderStageFlagBits::eFragment;
|
||||
stages[1].module = m_fragment_shader;
|
||||
stages[1].pName = "main";
|
||||
|
||||
vk::PipelineVertexInputStateCreateInfo vertex_input {};
|
||||
vertex_input.sType = vk::StructureType::ePipelineVertexInputStateCreateInfo;
|
||||
vk::PipelineInputAssemblyStateCreateInfo input_assembly {};
|
||||
input_assembly.sType = vk::StructureType::ePipelineInputAssemblyStateCreateInfo;
|
||||
input_assembly.topology = vk::PrimitiveTopology::eTriangleList;
|
||||
vk::PipelineViewportStateCreateInfo viewport {};
|
||||
viewport.sType = vk::StructureType::ePipelineViewportStateCreateInfo;
|
||||
viewport.viewportCount = 1;
|
||||
viewport.scissorCount = 1;
|
||||
vk::PipelineRasterizationStateCreateInfo rasterization {};
|
||||
rasterization.sType = vk::StructureType::ePipelineRasterizationStateCreateInfo;
|
||||
rasterization.polygonMode = vk::PolygonMode::eFill;
|
||||
rasterization.cullMode = vk::CullModeFlagBits::eNone;
|
||||
rasterization.lineWidth = 1.0f;
|
||||
rasterization.lineWidth = 1.0f;
|
||||
vk::PipelineMultisampleStateCreateInfo multisample {};
|
||||
multisample.sType = vk::StructureType::ePipelineMultisampleStateCreateInfo;
|
||||
multisample.rasterizationSamples = samples;
|
||||
vk::PipelineDepthStencilStateCreateInfo depth {};
|
||||
depth.sType = vk::StructureType::ePipelineDepthStencilStateCreateInfo;
|
||||
depth.depthTestEnable = VK_TRUE;
|
||||
depth.depthWriteEnable = VK_TRUE;
|
||||
depth.depthCompareOp = vk::CompareOp::eAlways;
|
||||
vk::PipelineColorBlendStateCreateInfo color_blend {};
|
||||
color_blend.sType = vk::StructureType::ePipelineColorBlendStateCreateInfo;
|
||||
const std::array dynamic_states {vk::DynamicState::eViewport, vk::DynamicState::eScissor};
|
||||
vk::PipelineDynamicStateCreateInfo dynamic {};
|
||||
dynamic.sType = vk::StructureType::ePipelineDynamicStateCreateInfo;
|
||||
dynamic.dynamicStateCount = static_cast<uint32_t>(dynamic_states.size());
|
||||
dynamic.pDynamicStates = dynamic_states.data();
|
||||
|
||||
vk::PipelineRenderingCreateInfo rendering {};
|
||||
rendering.sType = vk::StructureType::ePipelineRenderingCreateInfo;
|
||||
rendering.depthAttachmentFormat = key.format;
|
||||
|
||||
vk::GraphicsPipelineCreateInfo create {};
|
||||
create.sType = vk::StructureType::eGraphicsPipelineCreateInfo;
|
||||
create.pNext = &rendering;
|
||||
create.stageCount = static_cast<uint32_t>(stages.size());
|
||||
create.pStages = stages.data();
|
||||
@@ -186,7 +156,6 @@ void BlitHelper::ReinterpretColorAsMsDepth(Image& source, Image& destination) {
|
||||
command);
|
||||
|
||||
vk::RenderingAttachmentInfo depth_attachment {};
|
||||
depth_attachment.sType = vk::StructureType::eRenderingAttachmentInfo;
|
||||
depth_attachment.imageView = destination_view;
|
||||
depth_attachment.imageLayout = ColorToMsDepthLayout;
|
||||
depth_attachment.loadOp = vk::AttachmentLoadOp::eClear;
|
||||
@@ -194,7 +163,6 @@ void BlitHelper::ReinterpretColorAsMsDepth(Image& source, Image& destination) {
|
||||
depth_attachment.clearValue.depthStencil = {0.0f, 0};
|
||||
|
||||
vk::RenderingInfo rendering {};
|
||||
rendering.sType = vk::StructureType::eRenderingInfo;
|
||||
rendering.renderArea.extent = {destination_info.extent.width, destination_info.extent.height};
|
||||
rendering.layerCount = 1;
|
||||
rendering.pDepthAttachment = &depth_attachment;
|
||||
@@ -204,7 +172,6 @@ void BlitHelper::ReinterpretColorAsMsDepth(Image& source, Image& destination) {
|
||||
descriptor_image.imageView = source_view;
|
||||
descriptor_image.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
|
||||
vk::WriteDescriptorSet descriptor_write {};
|
||||
descriptor_write.sType = vk::StructureType::eWriteDescriptorSet;
|
||||
descriptor_write.dstBinding = 0;
|
||||
descriptor_write.descriptorCount = 1;
|
||||
descriptor_write.descriptorType = vk::DescriptorType::eSampledImage;
|
||||
|
||||
@@ -37,8 +37,7 @@ private:
|
||||
vk::Pipeline handle = nullptr;
|
||||
};
|
||||
|
||||
[[nodiscard]] vk::ShaderModule CreateShader(const uint32_t* code, size_t words) const;
|
||||
[[nodiscard]] vk::Pipeline GetPipeline(PipelineKey key);
|
||||
[[nodiscard]] vk::Pipeline GetPipeline(PipelineKey key);
|
||||
|
||||
GraphicContext& m_graphics;
|
||||
CommandScheduler& m_scheduler;
|
||||
|
||||
@@ -26,12 +26,13 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] vk::ImageCreateFlags ImageCreateFlags(const ImageInfo& info) {
|
||||
[[nodiscard]] vk::ImageCreateFlags ImageCreateFlags(const GraphicContext& graphics,
|
||||
const ImageInfo& info) {
|
||||
vk::ImageCreateFlags flags {};
|
||||
if (DepthAspectTransferFormat(info.pixel_format) == vk::Format::eUndefined) {
|
||||
flags |= vk::ImageCreateFlagBits::eMutableFormat;
|
||||
flags |= vk::ImageCreateFlagBits::eExtendedUsage;
|
||||
if (Prospero::BlockCompressedBytesPerBlock(info.guest_format) != 0) {
|
||||
if (info.IsBlock() && graphics.supports_block_texel_view) {
|
||||
flags |= vk::ImageCreateFlagBits::eBlockTexelViewCompatible;
|
||||
}
|
||||
}
|
||||
@@ -47,6 +48,10 @@ namespace {
|
||||
}
|
||||
|
||||
[[nodiscard]] vk::ImageUsageFlags ImageUsageFlags(GraphicContext& graphics, const ImageInfo& info) {
|
||||
if (info.IsBlock()) {
|
||||
return vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst |
|
||||
vk::ImageUsageFlagBits::eSampled;
|
||||
}
|
||||
const auto properties = graphics.GetFormatProperties(info.pixel_format);
|
||||
auto usage = vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst;
|
||||
if (HasFormatFeature(properties, vk::FormatFeatureFlagBits::eSampledImage)) {
|
||||
@@ -54,29 +59,22 @@ namespace {
|
||||
}
|
||||
if (DepthAspectTransferFormat(info.pixel_format) != vk::Format::eUndefined) {
|
||||
usage |= vk::ImageUsageFlagBits::eDepthStencilAttachment;
|
||||
if (graphics.attachment_feedback_loop_enabled && (usage & vk::ImageUsageFlagBits::eSampled)) {
|
||||
usage |= vk::ImageUsageFlagBits::eAttachmentFeedbackLoopEXT;
|
||||
}
|
||||
return usage;
|
||||
}
|
||||
if (HasFormatFeature(properties, vk::FormatFeatureFlagBits::eColorAttachment)) {
|
||||
usage |= vk::ImageUsageFlagBits::eColorAttachment;
|
||||
}
|
||||
if (info.samples == 1 &&
|
||||
HasFormatFeature(properties, vk::FormatFeatureFlagBits::eStorageImage)) {
|
||||
if (info.samples == 1) {
|
||||
usage |= vk::ImageUsageFlagBits::eStorage;
|
||||
} else if (info.samples == 1) {
|
||||
const auto compatible = SrgbStorageViewFormat(info.pixel_format);
|
||||
if (compatible != vk::Format::eUndefined &&
|
||||
HasFormatFeature(graphics.GetFormatProperties(compatible),
|
||||
vk::FormatFeatureFlagBits::eStorageImage)) {
|
||||
usage |= vk::ImageUsageFlagBits::eStorage;
|
||||
}
|
||||
}
|
||||
return usage;
|
||||
}
|
||||
|
||||
void ValidateRange(GuestRange range, const char* name) {
|
||||
if ((range.address == 0) != (range.size == 0) ||
|
||||
(range.address != 0 && (range.address >= TRACKER_ADDRESS_SIZE ||
|
||||
range.size > TRACKER_ADDRESS_SIZE - range.address))) {
|
||||
void ValidateOptionalRange(GuestRange range, const char* name) {
|
||||
if (!range.ValidOrEmpty()) {
|
||||
EXIT("invalid %s image range: address=0x%016llx size=0x%016llx\n", name,
|
||||
static_cast<unsigned long long>(range.address),
|
||||
static_cast<unsigned long long>(range.size));
|
||||
@@ -212,9 +210,7 @@ void Image::Transit(vk::ImageLayout destination_layout, vk::AccessFlags2 destina
|
||||
if (barriers.empty()) {
|
||||
return;
|
||||
}
|
||||
if (m_scheduler != nullptr) {
|
||||
m_scheduler->EndRendering();
|
||||
}
|
||||
m_scheduler.EndRendering();
|
||||
vk::DependencyInfo dependency {};
|
||||
dependency.imageMemoryBarrierCount = static_cast<uint32_t>(barriers.size());
|
||||
dependency.pImageMemoryBarriers = barriers.data();
|
||||
@@ -223,8 +219,8 @@ void Image::Transit(vk::ImageLayout destination_layout, vk::AccessFlags2 destina
|
||||
|
||||
void Image::Upload(std::span<const vk::BufferImageCopy> copies, vk::Buffer buffer, uint64_t offset,
|
||||
uint64_t size) {
|
||||
EXIT_IF(m_scheduler == nullptr || copies.empty() || buffer == nullptr || size == 0);
|
||||
m_scheduler->EndRendering();
|
||||
EXIT_IF(copies.empty() || buffer == nullptr || size == 0);
|
||||
m_scheduler.EndRendering();
|
||||
vk::BufferMemoryBarrier2 buffer_barrier {};
|
||||
buffer_barrier.srcStageMask = vk::PipelineStageFlagBits2::eAllCommands;
|
||||
buffer_barrier.srcAccessMask = vk::AccessFlagBits2::eMemoryWrite;
|
||||
@@ -244,7 +240,7 @@ void Image::Upload(std::span<const vk::BufferImageCopy> copies, vk::Buffer buffe
|
||||
dependency.pBufferMemoryBarriers = &buffer_barrier;
|
||||
dependency.imageMemoryBarrierCount = static_cast<uint32_t>(image_barriers.size());
|
||||
dependency.pImageMemoryBarriers = image_barriers.data();
|
||||
auto command = m_scheduler->Current().Handle();
|
||||
auto command = m_scheduler.Current().Handle();
|
||||
command.pipelineBarrier2(dependency);
|
||||
command.copyBufferToImage(buffer, backing.image, vk::ImageLayout::eTransferDstOptimal,
|
||||
static_cast<uint32_t>(copies.size()), copies.data());
|
||||
@@ -262,8 +258,8 @@ void Image::Upload(std::span<const vk::BufferImageCopy> copies, vk::Buffer buffe
|
||||
|
||||
void Image::Download(std::span<const vk::BufferImageCopy> copies, vk::Buffer buffer,
|
||||
uint64_t offset, uint64_t size) {
|
||||
EXIT_IF(m_scheduler == nullptr || copies.empty() || buffer == nullptr || size == 0);
|
||||
m_scheduler->EndRendering();
|
||||
EXIT_IF(copies.empty() || buffer == nullptr || size == 0);
|
||||
m_scheduler.EndRendering();
|
||||
vk::BufferMemoryBarrier2 buffer_barrier {};
|
||||
buffer_barrier.srcStageMask = vk::PipelineStageFlagBits2::eAllCommands;
|
||||
buffer_barrier.srcAccessMask =
|
||||
@@ -284,7 +280,7 @@ void Image::Download(std::span<const vk::BufferImageCopy> copies, vk::Buffer buf
|
||||
dependency.pBufferMemoryBarriers = &buffer_barrier;
|
||||
dependency.imageMemoryBarrierCount = static_cast<uint32_t>(image_barriers.size());
|
||||
dependency.pImageMemoryBarriers = image_barriers.data();
|
||||
auto command = m_scheduler->Current().Handle();
|
||||
auto command = m_scheduler.Current().Handle();
|
||||
command.pipelineBarrier2(dependency);
|
||||
command.copyImageToBuffer(backing.image, vk::ImageLayout::eTransferSrcOptimal, buffer,
|
||||
static_cast<uint32_t>(copies.size()), copies.data());
|
||||
@@ -321,8 +317,8 @@ std::pair<uint32_t, uint32_t> Image::SanitizeCopyLayers(const Image& source,
|
||||
}
|
||||
|
||||
void Image::CopyImage(Image& source) {
|
||||
EXIT_IF(m_scheduler == nullptr || source.backing.samples != backing.samples);
|
||||
m_scheduler->EndRendering();
|
||||
EXIT_IF(source.backing.samples != backing.samples);
|
||||
m_scheduler.EndRendering();
|
||||
const uint32_t levels = std::min(source.backing.mip_levels, backing.mip_levels);
|
||||
const uint32_t base_depth = backing.image_type == vk::ImageType::e3D
|
||||
? backing.extent.depth
|
||||
@@ -361,7 +357,7 @@ void Image::CopyImage(Image& source) {
|
||||
if (copies.empty()) {
|
||||
return;
|
||||
}
|
||||
auto command = m_scheduler->Current().Handle();
|
||||
auto command = m_scheduler.Current().Handle();
|
||||
source.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {},
|
||||
command);
|
||||
Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {}, command);
|
||||
@@ -374,8 +370,7 @@ void Image::CopyImage(Image& source) {
|
||||
|
||||
void Image::Resolve(Image& source, const ImageSubresourceRange& source_range,
|
||||
const ImageSubresourceRange& destination_range) {
|
||||
EXIT_IF(m_scheduler == nullptr || backing.samples != 1 ||
|
||||
source.backing.image_type != vk::ImageType::e2D ||
|
||||
EXIT_IF(backing.samples != 1 || source.backing.image_type != vk::ImageType::e2D ||
|
||||
backing.image_type != vk::ImageType::e2D || source_range.level_count != 1 ||
|
||||
destination_range.level_count != 1 ||
|
||||
source_range.base_level >= source.backing.mip_levels ||
|
||||
@@ -403,8 +398,8 @@ void Image::Resolve(Image& source, const ImageSubresourceRange& source_range,
|
||||
resolved_destination_range.layer_count = layers;
|
||||
const vk::Extent3D resolve_extent {info.extent.width, info.extent.height, 1};
|
||||
|
||||
m_scheduler->EndRendering();
|
||||
auto command = m_scheduler->Current().Handle();
|
||||
m_scheduler.EndRendering();
|
||||
auto command = m_scheduler.Current().Handle();
|
||||
source.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead,
|
||||
resolved_source_range, command);
|
||||
Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite,
|
||||
@@ -440,9 +435,8 @@ uint32_t Image::CopyRows(uint64_t row_size, uint32_t rows, uint64_t capacity) no
|
||||
}
|
||||
|
||||
void Image::CopyImageWithBuffer(Image& source, Buffer& buffer) {
|
||||
EXIT_IF(m_scheduler == nullptr || buffer.Handle() == nullptr || source.backing.samples != 1 ||
|
||||
backing.samples != 1);
|
||||
m_scheduler->EndRendering();
|
||||
EXIT_IF(buffer.Handle() == nullptr || source.backing.samples != 1 || backing.samples != 1);
|
||||
m_scheduler.EndRendering();
|
||||
const uint32_t levels = std::min(source.backing.mip_levels, backing.mip_levels);
|
||||
const auto source_aspect =
|
||||
FullAspectMask(source.backing.format) & ~vk::ImageAspectFlagBits::eStencil;
|
||||
@@ -472,7 +466,7 @@ void Image::CopyImageWithBuffer(Image& source, Buffer& buffer) {
|
||||
dependency.dependencyFlags = vk::DependencyFlagBits::eByRegion;
|
||||
dependency.bufferMemoryBarrierCount = 1;
|
||||
dependency.pBufferMemoryBarriers = &barrier;
|
||||
auto command = m_scheduler->Current().Handle();
|
||||
auto command = m_scheduler.Current().Handle();
|
||||
source.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {},
|
||||
command);
|
||||
Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {}, command);
|
||||
@@ -532,9 +526,9 @@ void Image::CopyImageWithBuffer(Image& source, Buffer& buffer) {
|
||||
}
|
||||
|
||||
void Image::CopyMip(Image& source, uint32_t mip, uint32_t layer) {
|
||||
EXIT_IF(m_scheduler == nullptr || source.backing.samples != backing.samples ||
|
||||
mip >= backing.mip_levels || layer >= backing.layers);
|
||||
m_scheduler->EndRendering();
|
||||
EXIT_IF(source.backing.samples != backing.samples || mip >= backing.mip_levels ||
|
||||
layer >= backing.layers);
|
||||
m_scheduler.EndRendering();
|
||||
const auto width = std::max(backing.extent.width >> mip, 1u);
|
||||
const auto height = std::max(backing.extent.height >> mip, 1u);
|
||||
const auto depth = std::max(backing.extent.depth >> mip, 1u);
|
||||
@@ -554,7 +548,7 @@ void Image::CopyMip(Image& source, uint32_t mip, uint32_t layer) {
|
||||
copy.dstSubresource = {aspect, mip, layer, destination_layers};
|
||||
copy.extent = {width, height, depth};
|
||||
}
|
||||
auto command = m_scheduler->Current().Handle();
|
||||
auto command = m_scheduler.Current().Handle();
|
||||
Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {}, command);
|
||||
source.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {},
|
||||
command);
|
||||
@@ -567,13 +561,13 @@ void Image::CopyMip(Image& source, uint32_t mip, uint32_t layer) {
|
||||
namespace ImageOps {
|
||||
|
||||
void Validate(const ImageInfo& info) {
|
||||
ValidateRange(info.data, "data");
|
||||
ValidateRange(info.stencil, "stencil");
|
||||
ValidateOptionalRange(info.data, "data");
|
||||
ValidateOptionalRange(info.stencil, "stencil");
|
||||
|
||||
if (info.pixel_format == vk::Format::eUndefined) {
|
||||
const bool metadata_empty =
|
||||
info.metadata.range.address == 0 && info.metadata.range.size == 0 &&
|
||||
info.metadata.kind == ImageMetadataKind::None && info.metadata.control == 0 &&
|
||||
info.metadata.range.Empty() && info.metadata.kind == ImageMetadataKind::None &&
|
||||
info.metadata.control == 0 &&
|
||||
info.metadata.compression == VideoOutCompression::Uncompressed &&
|
||||
!info.metadata.stencil_compressed;
|
||||
if (info.data.Empty() || info.HasStencil() || !metadata_empty || info.extent.width == 0 ||
|
||||
@@ -620,16 +614,14 @@ void Validate(const ImageInfo& info) {
|
||||
}
|
||||
switch (info.metadata.kind) {
|
||||
case ImageMetadataKind::None:
|
||||
if (info.metadata.range.address != 0 || info.metadata.range.size != 0 ||
|
||||
info.metadata.control != 0 ||
|
||||
if (!info.metadata.range.Empty() || info.metadata.control != 0 ||
|
||||
info.metadata.compression != VideoOutCompression::Uncompressed ||
|
||||
info.metadata.stencil_compressed) {
|
||||
EXIT("metadata-free image has metadata state\n");
|
||||
}
|
||||
break;
|
||||
case ImageMetadataKind::Htile:
|
||||
ValidateRange(info.metadata.range, "HTILE");
|
||||
if (info.metadata.range.Empty() ||
|
||||
if (!info.metadata.range.Valid() ||
|
||||
info.metadata.compression != VideoOutCompression::Uncompressed) {
|
||||
EXIT("invalid HTILE metadata\n");
|
||||
}
|
||||
@@ -646,13 +638,13 @@ void Validate(const ImageInfo& info) {
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t RenderTargetTransferFormat(uint32_t bytes_per_element) {
|
||||
Prospero::BufferFormat RenderTargetTransferFormat(uint32_t bytes_per_element) {
|
||||
switch (bytes_per_element) {
|
||||
case 1: return Prospero::GpuEnumValue(Prospero::BufferFormat::k8UNorm);
|
||||
case 2: return Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm);
|
||||
case 4: return Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float);
|
||||
case 8: return Prospero::GpuEnumValue(Prospero::BufferFormat::k16_16_16_16Float);
|
||||
case 16: return Prospero::GpuEnumValue(Prospero::BufferFormat::k32_32_32_32Float);
|
||||
case 1: return Prospero::BufferFormat::k8UNorm;
|
||||
case 2: return Prospero::BufferFormat::k16UNorm;
|
||||
case 4: return Prospero::BufferFormat::k32Float;
|
||||
case 8: return Prospero::BufferFormat::k16_16_16_16Float;
|
||||
case 16: return Prospero::BufferFormat::k32_32_32_32Float;
|
||||
default: EXIT("unsupported render-target element size: %u\n", bytes_per_element);
|
||||
}
|
||||
}
|
||||
@@ -660,7 +652,7 @@ uint32_t RenderTargetTransferFormat(uint32_t bytes_per_element) {
|
||||
} // namespace ImageOps
|
||||
|
||||
Image::Image(GraphicContext& graphics, CommandScheduler& scheduler, const ImageInfo& image_info)
|
||||
: info(image_info), m_graphics(&graphics), m_scheduler(&scheduler) {
|
||||
: info(image_info), m_graphics(graphics), m_scheduler(scheduler) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
ImageOps::Validate(info);
|
||||
m_cpu_dirty =
|
||||
@@ -669,29 +661,17 @@ Image::Image(GraphicContext& graphics, CommandScheduler& scheduler, const ImageI
|
||||
return;
|
||||
}
|
||||
|
||||
backing.format = info.pixel_format;
|
||||
backing.image_type = HostImageType(info.type);
|
||||
backing.extent = info.extent;
|
||||
backing.guest_pitch = info.pitch;
|
||||
backing.layers = info.IsVolume() ? 1u : info.resources.layers;
|
||||
backing.mip_levels = info.resources.levels;
|
||||
backing.samples = info.samples;
|
||||
backing.flags = ImageCreateFlags(info);
|
||||
backing.usage = ImageUsageFlags(graphics, info);
|
||||
|
||||
vk::ImageCreateInfo create {};
|
||||
create.sType = vk::StructureType::eImageCreateInfo;
|
||||
create.flags = backing.flags;
|
||||
create.imageType = backing.image_type;
|
||||
create.extent = backing.extent;
|
||||
create.mipLevels = backing.mip_levels;
|
||||
create.arrayLayers = backing.layers;
|
||||
create.format = backing.format;
|
||||
create.flags = ImageCreateFlags(graphics, info);
|
||||
create.imageType = HostImageType(info.type);
|
||||
create.extent = info.extent;
|
||||
create.mipLevels = info.resources.levels;
|
||||
create.arrayLayers = info.IsVolume() ? 1u : info.resources.layers;
|
||||
create.format = info.pixel_format;
|
||||
create.tiling = vk::ImageTiling::eOptimal;
|
||||
create.initialLayout = backing.state.layout;
|
||||
create.usage = backing.usage;
|
||||
create.sharingMode = vk::SharingMode::eExclusive;
|
||||
create.samples = vulkan_sample_count(backing.samples);
|
||||
create.initialLayout = vk::ImageLayout::eUndefined;
|
||||
create.usage = ImageUsageFlags(graphics, info);
|
||||
create.samples = vulkan_sample_count(info.samples);
|
||||
|
||||
vk::ImageFormatProperties properties {};
|
||||
if (graphics.GetImageFormatProperties(create.format, create.imageType, create.tiling,
|
||||
@@ -702,10 +682,9 @@ Image::Image(GraphicContext& graphics, CommandScheduler& scheduler, const ImageI
|
||||
"flags=0x%x samples=%u\n",
|
||||
static_cast<int>(create.format), static_cast<int>(create.imageType),
|
||||
static_cast<vk::ImageUsageFlags::MaskType>(create.usage),
|
||||
static_cast<vk::ImageCreateFlags::MaskType>(create.flags), backing.samples);
|
||||
static_cast<vk::ImageCreateFlags::MaskType>(create.flags), info.samples);
|
||||
}
|
||||
|
||||
backing.memory.property = vk::MemoryPropertyFlagBits::eDeviceLocal;
|
||||
if (!graphics.CreateImage(create, backing)) {
|
||||
EXIT("failed to create image: extent=%ux%ux%u format=%d layers=%u levels=%u\n",
|
||||
create.extent.width, create.extent.height, create.extent.depth,
|
||||
@@ -714,11 +693,12 @@ Image::Image(GraphicContext& graphics, CommandScheduler& scheduler, const ImageI
|
||||
}
|
||||
|
||||
uint64_t Image::HashGuestEdges() const {
|
||||
constexpr uint64_t page_mask = TRACKER_PAGE_SIZE - 1;
|
||||
std::array<uint8_t, TRACKER_PAGE_SIZE * 2> bytes {};
|
||||
const auto range = info.data;
|
||||
const uint64_t head_end = std::min(range.End(), (range.address + page_mask) & ~page_mask);
|
||||
const uint64_t tail_begin = std::max(range.address, range.End() & ~page_mask);
|
||||
const uint64_t head_end =
|
||||
std::min(range.End(), Common::AlignUp(range.address, TRACKER_PAGE_SIZE));
|
||||
const uint64_t tail_begin =
|
||||
std::max(range.address, Common::AlignDown(range.End(), TRACKER_PAGE_SIZE));
|
||||
const uint64_t head_size = head_end - range.address;
|
||||
const uint64_t tail_address = tail_begin < head_end ? head_end : tail_begin;
|
||||
const uint64_t tail_size = range.End() - tail_address;
|
||||
@@ -733,21 +713,13 @@ uint64_t Image::HashGuestEdges() const {
|
||||
|
||||
Image::~Image() {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
if (m_graphics == nullptr) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::lock_guard lock(views.mutex);
|
||||
for (auto& cached: views.views) {
|
||||
if (cached.view != nullptr) {
|
||||
m_graphics->device.destroyImageView(cached.view, nullptr);
|
||||
cached.view = nullptr;
|
||||
}
|
||||
for (const auto& cached: views) {
|
||||
if (cached.view != nullptr) {
|
||||
m_graphics.device.destroyImageView(cached.view, nullptr);
|
||||
}
|
||||
views.views.clear();
|
||||
}
|
||||
if (backing.image != nullptr) {
|
||||
m_graphics->DeleteImage(backing);
|
||||
m_graphics.DeleteImage(backing);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
#ifndef EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_IMAGE_H_
|
||||
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_IMAGE_H_
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/slotVector.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "graphics/host_gpu/renderer/image/imageInfo.h"
|
||||
|
||||
#include <compare>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
@@ -19,29 +20,13 @@ class Buffer;
|
||||
class CommandScheduler;
|
||||
struct ImageTestAccess;
|
||||
|
||||
struct ImageId {
|
||||
uint32_t index = std::numeric_limits<uint32_t>::max();
|
||||
uint32_t generation = 0;
|
||||
|
||||
[[nodiscard]] explicit operator bool() const noexcept {
|
||||
return index != std::numeric_limits<uint32_t>::max();
|
||||
}
|
||||
auto operator<=>(const ImageId&) const = default;
|
||||
};
|
||||
using ImageId = Common::SlotId;
|
||||
|
||||
struct CachedImageView {
|
||||
ImageViewInfo info;
|
||||
vk::ImageView view = nullptr;
|
||||
};
|
||||
|
||||
struct ImageViewCache {
|
||||
ImageViewCache() = default;
|
||||
KYTY_CLASS_NO_COPY(ImageViewCache);
|
||||
|
||||
std::mutex mutex;
|
||||
std::vector<CachedImageView> views;
|
||||
};
|
||||
|
||||
struct ImageUsage {
|
||||
bool texture = false;
|
||||
bool storage = false;
|
||||
@@ -51,10 +36,13 @@ struct ImageUsage {
|
||||
};
|
||||
|
||||
struct ImageBinding {
|
||||
bool is_bound = false;
|
||||
bool is_target = false;
|
||||
bool needs_rebind = false;
|
||||
bool force_general = false;
|
||||
vk::ImageLayout attachment_layout = vk::ImageLayout::eUndefined;
|
||||
vk::AccessFlags2 attachment_access;
|
||||
bool is_bound = false;
|
||||
bool is_target = false;
|
||||
bool needs_rebind = false;
|
||||
bool force_general = false;
|
||||
bool shader_write = false;
|
||||
};
|
||||
|
||||
class Image final {
|
||||
@@ -64,7 +52,6 @@ public:
|
||||
KYTY_CLASS_NO_COPY(Image);
|
||||
|
||||
[[nodiscard]] vk::ImageView FindView(const ImageViewInfo& view_info);
|
||||
void AssociateDepth(ImageId image_id) { depth_id = image_id; }
|
||||
using Barriers = std::vector<vk::ImageMemoryBarrier2>;
|
||||
[[nodiscard]] Barriers GetBarriers(vk::ImageLayout destination_layout,
|
||||
vk::AccessFlags2 destination_access,
|
||||
@@ -142,21 +129,18 @@ public:
|
||||
return pages ? ImagePageRangesOverlap(info.data.address, info.data.size, address, size)
|
||||
: ImageRangeOverlaps(info.data.address, info.data.size, address, size);
|
||||
}
|
||||
[[nodiscard]] bool GpuOverlaps(uint64_t address, uint64_t size) const noexcept {
|
||||
return IsGpuModified() && Overlaps(address, size);
|
||||
}
|
||||
[[nodiscard]] bool SafeToDownload() const noexcept {
|
||||
return IsGpuModified() && !IsBufferModified() && !IsCpuDirty();
|
||||
}
|
||||
[[nodiscard]] bool IsTracked() const noexcept { return track_addr != 0 && track_addr_end != 0; }
|
||||
[[nodiscard]] uint64_t AccountedSize() const noexcept {
|
||||
return backing.image == nullptr ? 0 : (info.data.size + 1023) & ~uint64_t {1023};
|
||||
return backing.image == nullptr ? 0 : Common::AlignUp(info.data.size, 1024);
|
||||
}
|
||||
[[nodiscard]] uint64_t HashGuestEdges() const;
|
||||
|
||||
ImageInfo info;
|
||||
VulkanImage backing;
|
||||
ImageViewCache views;
|
||||
std::vector<CachedImageView> views;
|
||||
ImageUsage usage;
|
||||
ImageBinding binding;
|
||||
bool registered = false;
|
||||
@@ -176,8 +160,8 @@ private:
|
||||
[[nodiscard]] static std::pair<uint32_t, uint32_t>
|
||||
SanitizeCopyLayers(const Image& source, const Image& destination, uint32_t depth);
|
||||
|
||||
GraphicContext* m_graphics = nullptr;
|
||||
CommandScheduler* m_scheduler = nullptr;
|
||||
GraphicContext& m_graphics;
|
||||
CommandScheduler& m_scheduler;
|
||||
uint64_t m_maybe_cpu_hash = 0;
|
||||
bool m_cpu_dirty = false;
|
||||
bool m_maybe_cpu_dirty = false;
|
||||
@@ -188,8 +172,8 @@ private:
|
||||
|
||||
namespace ImageOps {
|
||||
|
||||
void Validate(const ImageInfo& info);
|
||||
[[nodiscard]] uint32_t RenderTargetTransferFormat(uint32_t bytes_per_element);
|
||||
void Validate(const ImageInfo& info);
|
||||
[[nodiscard]] Prospero::BufferFormat RenderTargetTransferFormat(uint32_t bytes_per_element);
|
||||
|
||||
} // namespace ImageOps
|
||||
|
||||
|
||||
@@ -15,18 +15,6 @@
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
struct GuestRange {
|
||||
uint64_t address = 0;
|
||||
uint64_t size = 0;
|
||||
|
||||
[[nodiscard]] constexpr bool Empty() const noexcept { return address == 0 || size == 0; }
|
||||
[[nodiscard]] constexpr bool Valid() const noexcept {
|
||||
return !Empty() && address < TRACKER_ADDRESS_SIZE && size <= TRACKER_ADDRESS_SIZE - address;
|
||||
}
|
||||
[[nodiscard]] constexpr uint64_t End() const noexcept { return address + size; }
|
||||
auto operator<=>(const GuestRange&) const = default;
|
||||
};
|
||||
|
||||
enum class VideoOutCompression : uint8_t { Uncompressed, Dcc256_256_0, Dcc256_64_64, Unsupported };
|
||||
|
||||
enum class ImageMetadataKind : uint8_t { None, Htile, Dcc };
|
||||
@@ -35,8 +23,11 @@ struct ImageMetadataInfo {
|
||||
GuestRange range;
|
||||
ImageMetadataKind kind = ImageMetadataKind::None;
|
||||
uint32_t control = 0;
|
||||
uint32_t dcc_clear_word = 0;
|
||||
VideoOutCompression compression = VideoOutCompression::Uncompressed;
|
||||
bool stencil_compressed = false;
|
||||
bool dcc_clear_register_valid = false;
|
||||
bool dcc_alpha_msb = true;
|
||||
};
|
||||
|
||||
struct ImageSubresources {
|
||||
@@ -56,6 +47,7 @@ struct ImageSubresourceRange {
|
||||
struct ImageMipInfo {
|
||||
uint64_t offset = 0;
|
||||
uint64_t size = 0;
|
||||
// Padded dimensions in storage elements (compressed blocks for BC formats).
|
||||
uint32_t pitch = 0;
|
||||
uint32_t height = 0;
|
||||
auto operator<=>(const ImageMipInfo&) const = default;
|
||||
@@ -67,14 +59,14 @@ struct ImageInfo {
|
||||
ImageMetadataInfo metadata;
|
||||
uint32_t htile_clear_mask = UINT32_MAX;
|
||||
vk::Format pixel_format = vk::Format::eUndefined;
|
||||
uint32_t guest_format = 0;
|
||||
Prospero::BufferFormat guest_format = Prospero::BufferFormat::kInvalid;
|
||||
Prospero::ImageType type = Prospero::ImageType::kColor2D;
|
||||
vk::Extent3D extent = {1, 1, 1};
|
||||
ImageSubresources resources;
|
||||
uint32_t pitch = 0;
|
||||
uint32_t bytes_per_block = 0;
|
||||
uint32_t samples = 1;
|
||||
uint32_t tile_mode = 0;
|
||||
Prospero::TileMode tile_mode = Prospero::TileMode::kLinear;
|
||||
bool bgra16 = false;
|
||||
std::array<ImageMipInfo, 16> mip_layout {};
|
||||
|
||||
@@ -86,15 +78,10 @@ struct ImageInfo {
|
||||
[[nodiscard]] bool IsBlock() const noexcept {
|
||||
return Prospero::BlockCompressedBytesPerBlock(guest_format) != 0;
|
||||
}
|
||||
[[nodiscard]] bool IsTiled() const noexcept {
|
||||
return tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kLinear);
|
||||
}
|
||||
[[nodiscard]] bool IsTiled() const noexcept { return tile_mode != Prospero::TileMode::kLinear; }
|
||||
[[nodiscard]] constexpr bool IsVolume() const noexcept {
|
||||
return type == Prospero::ImageType::kColor3D;
|
||||
}
|
||||
[[nodiscard]] constexpr bool IsLayered() const noexcept {
|
||||
return !IsVolume() && resources.layers > 1;
|
||||
}
|
||||
[[nodiscard]] constexpr uint32_t TransferLayers() const noexcept {
|
||||
return IsVolume() ? extent.depth : resources.layers;
|
||||
}
|
||||
@@ -107,9 +94,24 @@ struct ImageInfo {
|
||||
bytes_per_block == other.bytes_per_block;
|
||||
}
|
||||
[[nodiscard]] int32_t MipOf(const ImageInfo& container) const noexcept {
|
||||
if (!IsCompatible(container) || tile_mode != container.tile_mode || resources.levels != 1 ||
|
||||
container.resources.layers == 0 ||
|
||||
container.resources.levels > container.mip_layout.size()) {
|
||||
if (container.resources.levels > container.mip_layout.size()) {
|
||||
return -1;
|
||||
}
|
||||
for (uint32_t level = 0; level < container.resources.levels; level++) {
|
||||
if (SliceOf(container, static_cast<int32_t>(level)) >= 0) {
|
||||
return static_cast<int32_t>(level);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
[[nodiscard]] int32_t SliceOf(const ImageInfo& container, int32_t mip) const noexcept {
|
||||
if (!IsCompatible(container) || tile_mode != container.tile_mode || type != container.type ||
|
||||
IsVolume() || resources.levels != 1 || resources.layers == 0 ||
|
||||
container.resources.layers == 0 || mip < 0 ||
|
||||
static_cast<uint32_t>(mip) >= container.resources.levels ||
|
||||
container.resources.levels > container.mip_layout.size() ||
|
||||
!data.Valid() || !container.data.Valid() || data.address < container.data.address ||
|
||||
data.End() > container.data.End()) {
|
||||
return -1;
|
||||
}
|
||||
if (HasStencil() != container.HasStencil() ||
|
||||
@@ -118,75 +120,36 @@ struct ImageInfo {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t mip = -1;
|
||||
for (uint32_t level = 0; level < container.resources.levels; level++) {
|
||||
const auto& layout = container.mip_layout[level];
|
||||
if (layout.size == 0 || layout.size % container.resources.layers != 0 ||
|
||||
container.data.address > UINT64_MAX - layout.offset) {
|
||||
continue;
|
||||
}
|
||||
const auto mip_base = container.data.address + layout.offset;
|
||||
const auto slice_size = layout.size / container.resources.layers;
|
||||
if (slice_size == 0 || mip_base > UINT64_MAX - layout.size) {
|
||||
continue;
|
||||
}
|
||||
const auto mip_end = mip_base + layout.size;
|
||||
if (data.address >= mip_base && data.address < mip_end &&
|
||||
(data.address - mip_base) % slice_size == 0) {
|
||||
mip = static_cast<int32_t>(level);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mip < 0) {
|
||||
const auto level = static_cast<uint32_t>(mip);
|
||||
const auto& layout = container.mip_layout[level];
|
||||
if (extent.width != std::max(container.extent.width >> level, 1u) ||
|
||||
extent.height != std::max(container.extent.height >> level, 1u) ||
|
||||
extent.depth != container.extent.depth || mip_layout[0].pitch != layout.pitch ||
|
||||
mip_layout[0].height != layout.height || layout.size == 0 ||
|
||||
layout.size % container.resources.layers != 0 ||
|
||||
container.data.size % container.resources.layers != 0 ||
|
||||
data.size % resources.layers != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto level = static_cast<uint32_t>(mip);
|
||||
if (extent.width != std::max(container.extent.width >> level, 1u) ||
|
||||
extent.height != std::max(container.extent.height >> level, 1u)) {
|
||||
// PS5 block slices contain the entire reversed mip chain.
|
||||
const auto slice_stride = container.data.size / container.resources.layers;
|
||||
const auto child_stride = data.size / resources.layers;
|
||||
if (child_stride != layout.size / container.resources.layers ||
|
||||
layout.offset >= slice_stride || child_stride > slice_stride - layout.offset ||
|
||||
(resources.layers > 1 && child_stride != slice_stride)) {
|
||||
return -1;
|
||||
}
|
||||
const auto mip_depth = std::max(container.extent.depth >> level, 1u);
|
||||
if (container.type == Prospero::ImageType::kColor3D &&
|
||||
type == Prospero::ImageType::kColor2D) {
|
||||
if (resources.layers != mip_depth) {
|
||||
return -1;
|
||||
}
|
||||
} else if (type != container.type) {
|
||||
const auto address_delta = data.address - container.data.address;
|
||||
if (address_delta < layout.offset || (address_delta - layout.offset) % slice_stride != 0) {
|
||||
return -1;
|
||||
}
|
||||
return mip;
|
||||
}
|
||||
[[nodiscard]] int32_t SliceOf(const ImageInfo& container, int32_t mip) const noexcept {
|
||||
if (!IsCompatible(container) || type != container.type || mip < 0 ||
|
||||
static_cast<uint32_t>(mip) >= container.resources.levels ||
|
||||
container.resources.levels > container.mip_layout.size() ||
|
||||
container.resources.layers == 0 || data.size == 0) {
|
||||
const auto layer = (address_delta - layout.offset) / slice_stride;
|
||||
if (layer > INT32_MAX || layer >= container.resources.layers ||
|
||||
resources.layers > container.resources.layers - layer) {
|
||||
return -1;
|
||||
}
|
||||
const auto level = static_cast<uint32_t>(mip);
|
||||
if (extent.width != std::max(container.extent.width >> level, 1u) ||
|
||||
extent.height != std::max(container.extent.height >> level, 1u)) {
|
||||
return -1;
|
||||
}
|
||||
const auto& layout = container.mip_layout[level];
|
||||
if (layout.size == 0 || layout.size % container.resources.layers != 0 ||
|
||||
container.data.address > UINT64_MAX - layout.offset) {
|
||||
return -1;
|
||||
}
|
||||
const auto slice_size = layout.size / container.resources.layers;
|
||||
if (slice_size == 0 || data.size % slice_size != 0) {
|
||||
return -1;
|
||||
}
|
||||
const auto mip_base = container.data.address + layout.offset;
|
||||
if (data.address < mip_base) {
|
||||
return -1;
|
||||
}
|
||||
const auto address_delta = data.address - mip_base;
|
||||
if (address_delta % data.size != 0 || address_delta / data.size > INT32_MAX) {
|
||||
return -1;
|
||||
}
|
||||
return static_cast<int32_t>(address_delta / data.size);
|
||||
return static_cast<int32_t>(layer);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -214,7 +177,6 @@ struct DepthFormatPolicy {
|
||||
Prospero::DepthFormat depth_format;
|
||||
Prospero::BufferFormat guest_format;
|
||||
uint32_t bytes_per_element;
|
||||
vk::Format sampled_view_format;
|
||||
vk::Format depth_attachment_format;
|
||||
std::array<vk::Format, 3> stencil_attachment_formats;
|
||||
};
|
||||
@@ -223,21 +185,19 @@ inline constexpr std::array<DepthFormatPolicy, 2> DEPTH_FORMAT_POLICIES {{
|
||||
{Prospero::DepthFormat::kZ16,
|
||||
Prospero::BufferFormat::k16UNorm,
|
||||
2,
|
||||
vk::Format::eR16Unorm,
|
||||
vk::Format::eD16Unorm,
|
||||
{vk::Format::eD16UnormS8Uint, vk::Format::eD24UnormS8Uint, vk::Format::eD32SfloatS8Uint}},
|
||||
{Prospero::DepthFormat::kZ32F,
|
||||
Prospero::BufferFormat::k32Float,
|
||||
4,
|
||||
vk::Format::eR32Sfloat,
|
||||
vk::Format::eD32Sfloat,
|
||||
{vk::Format::eD32SfloatS8Uint, vk::Format::eUndefined, vk::Format::eUndefined}},
|
||||
}};
|
||||
|
||||
[[nodiscard]] inline constexpr const DepthFormatPolicy*
|
||||
FindDepthFormatPolicy(uint32_t depth_format) noexcept {
|
||||
FindDepthFormatPolicy(Prospero::DepthFormat depth_format) noexcept {
|
||||
for (const auto& policy: DEPTH_FORMAT_POLICIES) {
|
||||
if (Prospero::GpuEnumValue(policy.depth_format) == depth_format) {
|
||||
if (policy.depth_format == depth_format) {
|
||||
return &policy;
|
||||
}
|
||||
}
|
||||
@@ -245,9 +205,9 @@ FindDepthFormatPolicy(uint32_t depth_format) noexcept {
|
||||
}
|
||||
|
||||
[[nodiscard]] inline constexpr const DepthFormatPolicy*
|
||||
FindGuestDepthFormatPolicy(uint32_t guest_format) noexcept {
|
||||
FindGuestDepthFormatPolicy(Prospero::BufferFormat guest_format) noexcept {
|
||||
for (const auto& policy: DEPTH_FORMAT_POLICIES) {
|
||||
if (Prospero::GpuEnumValue(policy.guest_format) == guest_format) {
|
||||
if (policy.guest_format == guest_format) {
|
||||
return &policy;
|
||||
}
|
||||
}
|
||||
@@ -269,10 +229,11 @@ FindGuestDepthFormatPolicy(uint32_t guest_format) noexcept {
|
||||
return has_stencil ? policy.stencil_attachment_formats.front() : policy.depth_attachment_format;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline constexpr vk::Format DepthAttachmentFormat(uint32_t depth_format,
|
||||
uint32_t stencil_format) noexcept {
|
||||
[[nodiscard]] inline constexpr vk::Format
|
||||
DepthAttachmentFormat(Prospero::DepthFormat depth_format,
|
||||
Prospero::StencilFormat stencil_format) noexcept {
|
||||
bool has_stencil = false;
|
||||
switch (static_cast<Prospero::StencilFormat>(stencil_format)) {
|
||||
switch (stencil_format) {
|
||||
case Prospero::StencilFormat::kInvalid: break;
|
||||
case Prospero::StencilFormat::k8UInt: has_stencil = true; break;
|
||||
default: return vk::Format::eUndefined;
|
||||
@@ -318,26 +279,6 @@ inline bool ImageInfo::IsDepth() const noexcept {
|
||||
return std::bit_cast<uint32_t>(static_cast<float>(value) / 65535.0f);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline constexpr bool IsSupportedSampledDepthFormat(vk::Format image_format,
|
||||
uint32_t guest_format,
|
||||
vk::Format view_format) noexcept {
|
||||
const auto* policy = FindGuestDepthFormatPolicy(guest_format);
|
||||
return policy != nullptr && view_format == policy->sampled_view_format &&
|
||||
(image_format == policy->depth_attachment_format ||
|
||||
IsStencilAttachmentFormat(*policy, image_format));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline constexpr bool IsSupportedSampledDepthFormat(vk::Format image_format,
|
||||
vk::Format view_format) noexcept {
|
||||
for (const auto& policy: DEPTH_FORMAT_POLICIES) {
|
||||
if (IsSupportedSampledDepthFormat(image_format, Prospero::GpuEnumValue(policy.guest_format),
|
||||
view_format)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline constexpr bool IsSupportedDepthTargetFormat(const ImageInfo& info) {
|
||||
const auto* policy = FindGuestDepthFormatPolicy(info.guest_format);
|
||||
return policy != nullptr && info.bytes_per_block == policy->bytes_per_element &&
|
||||
@@ -374,19 +315,11 @@ ClassifyVideoOutCompression(bool compressed, uint64_t metadata_address, uint32_t
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] inline constexpr bool
|
||||
CanUseVideoOutNativeWithoutUpload(VideoOutCompression compression, bool render_target,
|
||||
bool gpu_modified, bool guest_modified) noexcept {
|
||||
return compression != VideoOutCompression::Uncompressed &&
|
||||
compression != VideoOutCompression::Unsupported && !guest_modified &&
|
||||
(render_target || gpu_modified);
|
||||
}
|
||||
|
||||
struct VideoOutPixelFormatInfo {
|
||||
vk::Format format = vk::Format::eUndefined;
|
||||
uint32_t guest_format = 0;
|
||||
uint32_t bytes_per_element = 0;
|
||||
bool bgra16 = false;
|
||||
vk::Format format = vk::Format::eUndefined;
|
||||
Prospero::BufferFormat guest_format = Prospero::BufferFormat::kInvalid;
|
||||
uint32_t bytes_per_element = 0;
|
||||
bool bgra16 = false;
|
||||
};
|
||||
|
||||
struct VideoOutFormatPolicy {
|
||||
@@ -396,23 +329,17 @@ struct VideoOutFormatPolicy {
|
||||
|
||||
inline constexpr std::array<VideoOutFormatPolicy, 6> VIDEO_OUT_FORMAT_POLICIES {{
|
||||
{0x8000000022000000ull,
|
||||
{vk::Format::eR8G8B8A8Srgb, Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8Srgb), 4,
|
||||
false}},
|
||||
{vk::Format::eR8G8B8A8Srgb, Prospero::BufferFormat::k8_8_8_8Srgb, 4, false}},
|
||||
{0x8000000000000000ull,
|
||||
{vk::Format::eB8G8R8A8Srgb, Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8Srgb), 4,
|
||||
false}},
|
||||
{vk::Format::eB8G8R8A8Srgb, Prospero::BufferFormat::k8_8_8_8Srgb, 4, false}},
|
||||
{0x8100000022000000ull,
|
||||
{vk::Format::eA2B10G10R10UnormPack32,
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k10_10_10_2UNorm), 4, false}},
|
||||
{vk::Format::eA2B10G10R10UnormPack32, Prospero::BufferFormat::k10_10_10_2UNorm, 4, false}},
|
||||
{0x8100000000000000ull,
|
||||
{vk::Format::eA2R10G10B10UnormPack32,
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k10_10_10_2UNorm), 4, false}},
|
||||
{vk::Format::eA2R10G10B10UnormPack32, Prospero::BufferFormat::k10_10_10_2UNorm, 4, false}},
|
||||
{0xc001000622000000ull,
|
||||
{vk::Format::eR16G16B16A16Sfloat,
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k16_16_16_16Float), 8, false}},
|
||||
{vk::Format::eR16G16B16A16Sfloat, Prospero::BufferFormat::k16_16_16_16Float, 8, false}},
|
||||
{0xc001000600000000ull,
|
||||
{vk::Format::eR16G16B16A16Sfloat,
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k16_16_16_16Float), 8, true}},
|
||||
{vk::Format::eR16G16B16A16Sfloat, Prospero::BufferFormat::k16_16_16_16Float, 8, true}},
|
||||
}};
|
||||
|
||||
[[nodiscard]] inline bool DecodeVideoOutPixelFormat(uint64_t pixel_format,
|
||||
@@ -438,32 +365,6 @@ inline constexpr std::array<VideoOutFormatPolicy, 6> VIDEO_OUT_FORMAT_POLICIES {
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline constexpr bool
|
||||
IsSupportedDisplayRenderTargetTileMode(uint32_t tile_mode) noexcept {
|
||||
return tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline constexpr bool IsSupportedStandard64RenderTarget(const ImageInfo& info) {
|
||||
if (info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kStandard64KB) ||
|
||||
info.data.address == 0 || (info.data.address & 0xffffu) != 0 || info.extent.width == 0 ||
|
||||
info.extent.height == 0 || info.bytes_per_block != 4 || info.resources.levels != 1 ||
|
||||
info.resources.layers != 1 || info.samples != 1) {
|
||||
return false;
|
||||
}
|
||||
const auto expected_pitch =
|
||||
(static_cast<uint64_t>(info.extent.width) + 127u) & ~uint64_t {127u};
|
||||
const auto padded_height =
|
||||
(static_cast<uint64_t>(info.extent.height) + 127u) & ~uint64_t {127u};
|
||||
return expected_pitch <= UINT32_MAX && info.pitch == expected_pitch &&
|
||||
expected_pitch <= UINT64_MAX / padded_height / info.bytes_per_block &&
|
||||
info.data.size == expected_pitch * padded_height * info.bytes_per_block;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline constexpr bool IsTiledRenderTarget(const ImageInfo& info) noexcept {
|
||||
return info.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) ||
|
||||
IsSupportedStandard64RenderTarget(info);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool DecodePackedColorClear(vk::Format format, uint32_t packed,
|
||||
vk::ClearColorValue& clear) {
|
||||
vk::ClearColorValue next {};
|
||||
@@ -473,6 +374,10 @@ IsSupportedDisplayRenderTargetTileMode(uint32_t tile_mode) noexcept {
|
||||
return encoded <= 0.04045f ? encoded / 12.92f : std::pow((encoded + 0.055f) / 1.055f, 2.4f);
|
||||
};
|
||||
switch (format) {
|
||||
// A single-plane float target carries its clear as raw float bits, the same encoding the
|
||||
// depth decoder below uses. Without this the clear is discarded and the target keeps stale
|
||||
// contents.
|
||||
case vk::Format::eR32Sfloat: next.float32[0] = std::bit_cast<float>(packed); break;
|
||||
case vk::Format::eR32Uint: next.uint32[0] = packed; break;
|
||||
case vk::Format::eR32Sint: next.int32[0] = static_cast<int32_t>(packed); break;
|
||||
case vk::Format::eR8G8B8A8Srgb:
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "graphics/host_gpu/renderer/image/image.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
namespace {
|
||||
@@ -23,10 +21,6 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsCompatibleViewFormat(vk::Format image_format, vk::Format view_format) {
|
||||
return ImageViewOps::FormatsCompatible(image_format, view_format);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsStencilViewFormat(vk::Format format) {
|
||||
switch (format) {
|
||||
case vk::Format::eS8Uint:
|
||||
@@ -36,17 +30,6 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsDepthViewFormat(vk::Format format) {
|
||||
switch (format) {
|
||||
case vk::Format::eD16Unorm:
|
||||
case vk::Format::eR16Unorm:
|
||||
case vk::Format::eD32Sfloat:
|
||||
case vk::Format::eR32Sfloat:
|
||||
case vk::Format::eR32Uint: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsValidViewType(const VulkanImage& image, const ImageViewInfo& info) {
|
||||
switch (image.image_type) {
|
||||
case vk::ImageType::e1D:
|
||||
@@ -327,20 +310,26 @@ vk::ImageView Image::FindView(const ImageViewInfo& view_info) {
|
||||
const auto& image = backing;
|
||||
auto normalized = view_info;
|
||||
const bool is_storage = static_cast<bool>(normalized.usage & vk::ImageUsageFlagBits::eStorage);
|
||||
normalized.aspect = FullAspectMask(image.format);
|
||||
if (normalized.aspect & vk::ImageAspectFlagBits::eDepth &&
|
||||
IsDepthViewFormat(normalized.format)) {
|
||||
const auto image_aspect = FullAspectMask(image.format);
|
||||
if (image_aspect & vk::ImageAspectFlagBits::eDepth &&
|
||||
ImageViewOps::IsFormatDepthCompatible(normalized.format)) {
|
||||
normalized.format = image.format;
|
||||
normalized.aspect = vk::ImageAspectFlagBits::eDepth;
|
||||
}
|
||||
if (normalized.aspect & vk::ImageAspectFlagBits::eStencil &&
|
||||
if (image_aspect & vk::ImageAspectFlagBits::eStencil &&
|
||||
IsStencilViewFormat(normalized.format)) {
|
||||
normalized.format = image.format;
|
||||
normalized.aspect = vk::ImageAspectFlagBits::eStencil;
|
||||
}
|
||||
normalized.usage = is_storage ? vk::ImageUsageFlagBits::eStorage : vk::ImageUsageFlags {};
|
||||
for (const auto& cached: views) {
|
||||
if (cached.info == normalized) {
|
||||
return cached.view;
|
||||
}
|
||||
}
|
||||
|
||||
const bool format_compatible = normalized.format != vk::Format::eUndefined &&
|
||||
IsCompatibleViewFormat(image.format, normalized.format);
|
||||
ImageViewOps::FormatsCompatible(image.format, normalized.format);
|
||||
const bool slice_view =
|
||||
image.image_type == vk::ImageType::e3D && (normalized.type == vk::ImageViewType::e2D ||
|
||||
normalized.type == vk::ImageViewType::e2DArray);
|
||||
@@ -368,21 +357,12 @@ vk::ImageView Image::FindView(const ImageViewInfo& view_info) {
|
||||
image.layers);
|
||||
}
|
||||
|
||||
std::lock_guard lock(views.mutex);
|
||||
for (const auto& cached: views.views) {
|
||||
if (cached.info == normalized) {
|
||||
return cached.view;
|
||||
}
|
||||
}
|
||||
|
||||
vk::ImageViewUsageCreateInfo usage {};
|
||||
usage.sType = vk::StructureType::eImageViewUsageCreateInfo;
|
||||
usage.usage = image.usage;
|
||||
if (!is_storage) {
|
||||
usage.usage &= ~vk::ImageUsageFlagBits::eStorage;
|
||||
}
|
||||
vk::ImageViewCreateInfo create {};
|
||||
create.sType = vk::StructureType::eImageViewCreateInfo;
|
||||
create.pNext = &usage;
|
||||
create.image = image.image;
|
||||
create.viewType = normalized.type;
|
||||
@@ -395,7 +375,7 @@ vk::ImageView Image::FindView(const ImageViewInfo& view_info) {
|
||||
create.subresourceRange.layerCount = normalized.layer_count;
|
||||
|
||||
vk::ImageView view = nullptr;
|
||||
const auto result = m_graphics->device.createImageView(&create, nullptr, &view);
|
||||
const auto result = m_graphics.device.createImageView(&create, nullptr, &view);
|
||||
if (result != vk::Result::eSuccess || view == nullptr) {
|
||||
EXIT("failed to create image view: result=%d image_format=%d view_format=%d type=%d "
|
||||
"aspect=0x%x mip=%u+%u layer=%u+%u usage=0x%x\n",
|
||||
@@ -405,7 +385,7 @@ vk::ImageView Image::FindView(const ImageViewInfo& view_info) {
|
||||
view_info.level_count, view_info.base_layer, view_info.layer_count,
|
||||
static_cast<vk::ImageUsageFlags::MaskType>(view_info.usage));
|
||||
}
|
||||
views.views.push_back({normalized, view});
|
||||
views.push_back({normalized, view});
|
||||
return view;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,25 @@ namespace ImageViewOps {
|
||||
|
||||
[[nodiscard]] vk::ImageAspectFlags DepthAspectMask(vk::Format format);
|
||||
[[nodiscard]] bool FormatsCompatible(vk::Format base, vk::Format view) noexcept;
|
||||
|
||||
[[nodiscard]] inline bool IsFormatDepthCompatible(vk::Format format) noexcept {
|
||||
switch (format) {
|
||||
case vk::Format::eD32Sfloat:
|
||||
case vk::Format::eR32Sfloat:
|
||||
case vk::Format::eR32Uint:
|
||||
case vk::Format::eD16Unorm:
|
||||
case vk::Format::eR16Unorm: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
} // namespace ImageViewOps
|
||||
|
||||
[[nodiscard]] inline bool IsSupportedSampledDepthFormat(vk::Format image_format,
|
||||
vk::Format view_format) noexcept {
|
||||
return DepthAspectTransferFormat(image_format) != vk::Format::eUndefined &&
|
||||
ImageViewOps::IsFormatDepthCompatible(view_format);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool IsValidImageSwizzle(uint32_t swizzle) noexcept {
|
||||
if ((swizzle & ~0xfffu) != 0) {
|
||||
return false;
|
||||
@@ -75,34 +92,21 @@ SelectSampledColorView(vk::Format image_format, vk::Format view_format, uint32_t
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] inline uint32_t
|
||||
SelectSampledDepthView(vk::Format image_format, vk::Format view_format, uint32_t swizzle) noexcept {
|
||||
if (IsSupportedSampledDepthView(image_format, view_format, swizzle)) {
|
||||
return swizzle;
|
||||
}
|
||||
EXIT("unsupported sampled depth image view: image_format=%d view_format=%d swizzle=0x%03x\n",
|
||||
static_cast<int>(image_format), static_cast<int>(view_format), swizzle);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool
|
||||
IsSupportedSampledDepthResource(const ShaderRecompiler::IR::ImageResource& resource) noexcept {
|
||||
return resource.kind == ShaderRecompiler::IR::ResourceKind::Image &&
|
||||
(resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D ||
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray ||
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa ||
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaaArray) &&
|
||||
resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::None && resource.read &&
|
||||
if (resource.resource_class != ShaderRecompiler::IR::ImageResourceClass::Sampled) {
|
||||
return false;
|
||||
}
|
||||
if (resource.numeric_class == Prospero::TextureNumericClass::Unsupported) {
|
||||
return false;
|
||||
}
|
||||
if (resource.numeric_class != Prospero::TextureNumericClass::Float && resource.depth_compare) {
|
||||
return false;
|
||||
}
|
||||
return resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::None && resource.read &&
|
||||
!resource.written && !resource.atomic;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool
|
||||
IsSupportedSampledDepthUintResource(const ShaderRecompiler::IR::ImageResource& resource) noexcept {
|
||||
return resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint &&
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D &&
|
||||
resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::None && resource.read &&
|
||||
!resource.written && !resource.atomic && !resource.depth_compare;
|
||||
}
|
||||
|
||||
inline void ValidateStorageColorView(vk::Format image_format, vk::Format view_format,
|
||||
uint32_t swizzle) noexcept {
|
||||
if (!ImageViewOps::FormatsCompatible(image_format, view_format) ||
|
||||
@@ -113,28 +117,33 @@ inline void ValidateStorageColorView(vk::Format image_format, vk::Format view_fo
|
||||
|
||||
[[nodiscard]] inline bool
|
||||
IsSupportedStorageImageResource(const ShaderRecompiler::IR::ImageResource& resource) noexcept {
|
||||
return (resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImage ||
|
||||
resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint) &&
|
||||
return resource.resource_class == ShaderRecompiler::IR::ImageResourceClass::Storage &&
|
||||
(resource.numeric_class == Prospero::TextureNumericClass::Float ||
|
||||
resource.numeric_class == Prospero::TextureNumericClass::Uint) &&
|
||||
(resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim1D ||
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim1DArray ||
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D ||
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim3D ||
|
||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray) &&
|
||||
resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::None && resource.written &&
|
||||
((resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::None &&
|
||||
resource.mip_count == 1u) ||
|
||||
(resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::DynamicStorage &&
|
||||
resource.mip_count != 0u)) &&
|
||||
resource.written &&
|
||||
(!resource.atomic ||
|
||||
(resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint &&
|
||||
resource.read)) &&
|
||||
(resource.numeric_class == Prospero::TextureNumericClass::Uint && resource.read)) &&
|
||||
!resource.depth_compare;
|
||||
}
|
||||
|
||||
inline void
|
||||
ValidateStorageImageResource(const ShaderRecompiler::IR::ImageResource& resource) noexcept {
|
||||
if (!IsSupportedStorageImageResource(resource)) {
|
||||
EXIT("unsupported storage color image resource: kind=%u dimension=%u mip=%u "
|
||||
EXIT("unsupported storage color image resource: class=%u numeric=%u dimension=%u mip=%u "
|
||||
"read=%d written=%d atomic=%d depth_compare=%d\n",
|
||||
static_cast<uint32_t>(resource.kind), static_cast<uint32_t>(resource.dimension),
|
||||
static_cast<uint32_t>(resource.mip_mode), resource.read, resource.written,
|
||||
resource.atomic, resource.depth_compare);
|
||||
static_cast<uint32_t>(resource.resource_class),
|
||||
static_cast<uint32_t>(resource.numeric_class),
|
||||
static_cast<uint32_t>(resource.dimension), static_cast<uint32_t>(resource.mip_mode),
|
||||
resource.read, resource.written, resource.atomic, resource.depth_compare);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,178 +1,67 @@
|
||||
#include "graphics/host_gpu/renderer/image/textureCommon.h"
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "graphics/guest_gpu/gpu_defs.h"
|
||||
#include "graphics/guest_gpu/gpu_format.h"
|
||||
#include "graphics/host_gpu/renderer/image/tiler.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
#include "graphics/shader/shader.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
namespace {
|
||||
|
||||
struct RenderTargetFormatMapping {
|
||||
Prospero::ChannelLayout layout;
|
||||
Prospero::ChannelType type;
|
||||
Prospero::ChannelOrder order;
|
||||
RenderTargetFormatInfo info;
|
||||
// Rows are channel order; columns are the number of physical components minus one. Unused
|
||||
// selectors complete each entry to a permutation so logical write masks can be inverted.
|
||||
constexpr Prospero::ColorComponentMapping kRenderTargetColorMappings[4][4] = {
|
||||
{Prospero::ColorMappingRgba, Prospero::ColorMappingRgba, Prospero::ColorMappingRgba,
|
||||
Prospero::ColorMappingRgba},
|
||||
{Prospero::ColorMappingGr, Prospero::ColorMappingRabg, Prospero::ColorMappingRgab,
|
||||
Prospero::ColorMappingBgra},
|
||||
{Prospero::ColorMappingBgra, Prospero::ColorMappingGr, Prospero::ColorMappingBgra,
|
||||
Prospero::ColorMappingAbgr},
|
||||
{Prospero::ColorMappingAgba, Prospero::ColorMappingArbg, Prospero::ColorMappingAgbr,
|
||||
Prospero::ColorMappingArgb},
|
||||
};
|
||||
|
||||
constexpr RenderTargetFormatMapping kRenderTargetFormats[] = {
|
||||
{Prospero::ChannelLayout::k8,
|
||||
Prospero::ChannelType::kUInt,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR8Uint, 1}},
|
||||
{Prospero::ChannelLayout::k8_8,
|
||||
Prospero::ChannelType::kUNorm,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR8G8Unorm, 2}},
|
||||
{Prospero::ChannelLayout::k8_8_8_8,
|
||||
Prospero::ChannelType::kUNorm,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR8G8B8A8Unorm, 4}},
|
||||
{Prospero::ChannelLayout::k8_8_8_8,
|
||||
Prospero::ChannelType::kSNorm,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR8G8B8A8Snorm, 4}},
|
||||
{Prospero::ChannelLayout::k8_8_8_8,
|
||||
Prospero::ChannelType::kSrgb,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR8G8B8A8Srgb, 4}},
|
||||
{Prospero::ChannelLayout::k8_8_8_8,
|
||||
Prospero::ChannelType::kUNorm,
|
||||
Prospero::ChannelOrder::kAlt,
|
||||
{vk::Format::eB8G8R8A8Unorm, 4}},
|
||||
{Prospero::ChannelLayout::k8_8_8_8,
|
||||
Prospero::ChannelType::kSNorm,
|
||||
Prospero::ChannelOrder::kAlt,
|
||||
{vk::Format::eB8G8R8A8Snorm, 4}},
|
||||
{Prospero::ChannelLayout::k8_8_8_8,
|
||||
Prospero::ChannelType::kSrgb,
|
||||
Prospero::ChannelOrder::kAlt,
|
||||
{vk::Format::eB8G8R8A8Srgb, 4}},
|
||||
{Prospero::ChannelLayout::k5_5_5_1,
|
||||
Prospero::ChannelType::kUNorm,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR5G5B5A1UnormPack16, 2}},
|
||||
{Prospero::ChannelLayout::k4_4_4_4,
|
||||
Prospero::ChannelType::kUNorm,
|
||||
Prospero::ChannelOrder::kReversed,
|
||||
{vk::Format::eB4G4R4A4UnormPack16, 2}},
|
||||
{Prospero::ChannelLayout::k10_10_10_2,
|
||||
Prospero::ChannelType::kUNorm,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eA2B10G10R10UnormPack32, 4}},
|
||||
{Prospero::ChannelLayout::k10_10_10_2,
|
||||
Prospero::ChannelType::kUNorm,
|
||||
Prospero::ChannelOrder::kAlt,
|
||||
{vk::Format::eA2R10G10B10UnormPack32, 4}},
|
||||
{Prospero::ChannelLayout::k11_11_10,
|
||||
Prospero::ChannelType::kFloat,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eB10G11R11UfloatPack32, 4}},
|
||||
{Prospero::ChannelLayout::k5_6_5,
|
||||
Prospero::ChannelType::kUNorm,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eB5G6R5UnormPack16, 2}},
|
||||
{Prospero::ChannelLayout::k16,
|
||||
Prospero::ChannelType::kUNorm,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR16Unorm, 2}},
|
||||
{Prospero::ChannelLayout::k16,
|
||||
Prospero::ChannelType::kUInt,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR16Uint, 2}},
|
||||
{Prospero::ChannelLayout::k16,
|
||||
Prospero::ChannelType::kFloat,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR16Sfloat, 2}},
|
||||
{Prospero::ChannelLayout::k16_16,
|
||||
Prospero::ChannelType::kUNorm,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR16G16Unorm, 4}},
|
||||
{Prospero::ChannelLayout::k16_16,
|
||||
Prospero::ChannelType::kSNorm,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR16G16Snorm, 4}},
|
||||
{Prospero::ChannelLayout::k16_16,
|
||||
Prospero::ChannelType::kUInt,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR16G16Uint, 4}},
|
||||
{Prospero::ChannelLayout::k16_16,
|
||||
Prospero::ChannelType::kFloat,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR16G16Sfloat, 4}},
|
||||
{Prospero::ChannelLayout::k16_16_16_16,
|
||||
Prospero::ChannelType::kUNorm,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR16G16B16A16Unorm, 8}},
|
||||
{Prospero::ChannelLayout::k16_16_16_16,
|
||||
Prospero::ChannelType::kUInt,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR16G16B16A16Uint, 8}},
|
||||
{Prospero::ChannelLayout::k16_16_16_16,
|
||||
Prospero::ChannelType::kFloat,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR16G16B16A16Sfloat, 8}},
|
||||
{Prospero::ChannelLayout::k16_16_16_16,
|
||||
Prospero::ChannelType::kFloat,
|
||||
Prospero::ChannelOrder::kAlt,
|
||||
{vk::Format::eR16G16B16A16Sfloat, 8, Prospero::ColorMappingBgra}},
|
||||
{Prospero::ChannelLayout::k16_16_16_16,
|
||||
Prospero::ChannelType::kFloat,
|
||||
Prospero::ChannelOrder::kReversed,
|
||||
{vk::Format::eR16G16B16A16Sfloat, 8, Prospero::ColorMappingAbgr}},
|
||||
{Prospero::ChannelLayout::k32,
|
||||
Prospero::ChannelType::kFloat,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR32Sfloat, 4}},
|
||||
{Prospero::ChannelLayout::k32_32,
|
||||
Prospero::ChannelType::kUInt,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR32G32Uint, 8}},
|
||||
{Prospero::ChannelLayout::k32_32,
|
||||
Prospero::ChannelType::kFloat,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR32G32Sfloat, 8}},
|
||||
{Prospero::ChannelLayout::k32_32_32_32,
|
||||
Prospero::ChannelType::kFloat,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR32G32B32A32Sfloat, 16}},
|
||||
struct HostFormatInfo {
|
||||
vk::Format format = vk::Format::eUndefined;
|
||||
Prospero::ColorComponentMapping host_to_storage;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// TODO: cleanup!
|
||||
RenderTargetFormatInfo TextureGetRenderTargetFormat(uint32_t raw_layout, uint32_t raw_type,
|
||||
uint32_t raw_order) {
|
||||
const auto layout = static_cast<Prospero::ChannelLayout>(raw_layout);
|
||||
const auto type = static_cast<Prospero::ChannelType>(raw_type);
|
||||
const auto order = static_cast<Prospero::ChannelOrder>(raw_order);
|
||||
|
||||
if (layout == Prospero::ChannelLayout::k8 && type == Prospero::ChannelType::kUNorm &&
|
||||
raw_order <= Prospero::GpuEnumValue(Prospero::ChannelOrder::kAltReversed)) {
|
||||
return {vk::Format::eR8Unorm, 1};
|
||||
}
|
||||
for (const auto& mapping: kRenderTargetFormats) {
|
||||
if (mapping.layout == layout && mapping.type == type && mapping.order == order) {
|
||||
return mapping.info;
|
||||
HostFormatInfo ResolveHostFormat(Prospero::BufferFormat guest_format,
|
||||
Prospero::ChannelOrder order) {
|
||||
if (order == Prospero::ChannelOrder::kAlt) {
|
||||
switch (guest_format) {
|
||||
case Prospero::BufferFormat::k8_8_8_8UNorm:
|
||||
return {vk::Format::eB8G8R8A8Unorm, Prospero::ColorMappingBgra};
|
||||
case Prospero::BufferFormat::k8_8_8_8SNorm:
|
||||
return {vk::Format::eB8G8R8A8Snorm, Prospero::ColorMappingBgra};
|
||||
case Prospero::BufferFormat::k8_8_8_8Srgb:
|
||||
return {vk::Format::eB8G8R8A8Srgb, Prospero::ColorMappingBgra};
|
||||
case Prospero::BufferFormat::k10_10_10_2UNorm:
|
||||
return {vk::Format::eA2R10G10B10UnormPack32, Prospero::ColorMappingBgra};
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
EXIT("unsupported render-target format combination: layout=%u type=%u order=%u\n", raw_layout,
|
||||
raw_type, raw_order);
|
||||
const auto format = VulkanFormat(guest_format);
|
||||
switch (guest_format) {
|
||||
case Prospero::BufferFormat::k5_5_5_1UNorm: return {format, Prospero::ColorMappingBgra};
|
||||
case Prospero::BufferFormat::k1_5_5_5UNorm:
|
||||
case Prospero::BufferFormat::k4_4_4_4UNorm: return {format, Prospero::ColorMappingAbgr};
|
||||
default: return {format, {}};
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
static uint32_t GetTextureLevelDepth(uint32_t depth, uint32_t level, bool volume_texture) {
|
||||
uint32_t GetTextureLevelDepth(uint32_t depth, uint32_t level, bool volume_texture) {
|
||||
return volume_texture ? std::max(depth >> level, 1u) : depth;
|
||||
}
|
||||
|
||||
static size_t GetTextureRegionCount(uint32_t depth, uint64_t levels, bool volume_texture) {
|
||||
size_t GetTextureRegionCount(uint32_t depth, uint32_t levels, bool volume_texture) {
|
||||
size_t count = 0;
|
||||
for (uint32_t level = 0; level < levels; level++) {
|
||||
count += GetTextureLevelDepth(depth, level, volume_texture);
|
||||
@@ -180,57 +69,15 @@ static size_t GetTextureRegionCount(uint32_t depth, uint64_t levels, bool volume
|
||||
return count;
|
||||
}
|
||||
|
||||
vk::ComponentSwizzle TextureGetComponentSwizzle(uint8_t s) {
|
||||
switch (static_cast<Prospero::CompSwizzle>(s)) {
|
||||
case Prospero::CompSwizzle::kZero: return vk::ComponentSwizzle::eZero;
|
||||
case Prospero::CompSwizzle::kOne: return vk::ComponentSwizzle::eOne;
|
||||
case Prospero::CompSwizzle::kRed: return vk::ComponentSwizzle::eR;
|
||||
case Prospero::CompSwizzle::kGreen: return vk::ComponentSwizzle::eG;
|
||||
case Prospero::CompSwizzle::kBlue: return vk::ComponentSwizzle::eB;
|
||||
case Prospero::CompSwizzle::kAlpha: return vk::ComponentSwizzle::eA;
|
||||
default: EXIT("unknown swizzle: %d\n", static_cast<int>(s));
|
||||
}
|
||||
return vk::ComponentSwizzle::eIdentity;
|
||||
}
|
||||
|
||||
static uint32_t TextureGetDstSel(uint32_t swizzle, uint32_t channel) {
|
||||
return (swizzle >> (channel * 3u)) & 0x7u;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
vk::ComponentMapping TextureGetComponentMapping(uint32_t swizzle) {
|
||||
vk::ComponentMapping components {};
|
||||
components.r = TextureGetComponentSwizzle(static_cast<uint8_t>(TextureGetDstSel(swizzle, 0)));
|
||||
components.g = TextureGetComponentSwizzle(static_cast<uint8_t>(TextureGetDstSel(swizzle, 1)));
|
||||
components.b = TextureGetComponentSwizzle(static_cast<uint8_t>(TextureGetDstSel(swizzle, 2)));
|
||||
components.a = TextureGetComponentSwizzle(static_cast<uint8_t>(TextureGetDstSel(swizzle, 3)));
|
||||
return components;
|
||||
}
|
||||
|
||||
vk::Format TextureGetFormat(uint32_t fmt) {
|
||||
const auto vk_format = VulkanFormat(fmt);
|
||||
if (vk_format != vk::Format::eUndefined) {
|
||||
return vk_format;
|
||||
}
|
||||
EXIT("unknown format: fmt = %u\n", fmt);
|
||||
return vk::Format::eUndefined;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
uint64_t CalcTextureSliceStride(const TextureUploadMipLayout* mips, uint64_t levels,
|
||||
uint64_t CalcTextureSliceStride(const TextureUploadMipLayout* mips, uint32_t levels,
|
||||
uint64_t total_size, uint32_t depth) {
|
||||
uint64_t stride = 0;
|
||||
for (uint32_t i = 0; i < levels; i++) {
|
||||
stride = std::max(stride, mips[i].offset + mips[i].size);
|
||||
for (uint32_t mip_level = 0; mip_level < levels; ++mip_level) {
|
||||
stride = std::max(stride, mips[mip_level].offset + mips[mip_level].size);
|
||||
}
|
||||
|
||||
if (depth > 1 && total_size != 0 && total_size % depth == 0) {
|
||||
const auto guest_stride = total_size / depth;
|
||||
if (guest_stride >= stride) {
|
||||
stride = guest_stride;
|
||||
}
|
||||
stride = std::max(stride, total_size / depth);
|
||||
}
|
||||
|
||||
return stride;
|
||||
@@ -238,47 +85,102 @@ uint64_t CalcTextureSliceStride(const TextureUploadMipLayout* mips, uint64_t lev
|
||||
|
||||
uint64_t CalcLinearUploadLevelSize(const TileTextureElementLayout& element, uint32_t pitch,
|
||||
uint32_t height) {
|
||||
const uint32_t elements_w =
|
||||
const uint32_t element_columns =
|
||||
std::max((pitch + element.texel_width - 1u) / element.texel_width, 1u);
|
||||
const uint32_t elements_h =
|
||||
const uint32_t element_rows =
|
||||
std::max((height + element.texel_height - 1u) / element.texel_height, 1u);
|
||||
return static_cast<uint64_t>(elements_w) * elements_h * element.bytes;
|
||||
return static_cast<uint64_t>(element_columns) * element_rows * element.bytes;
|
||||
}
|
||||
|
||||
uint64_t SetLinearUploadLevels(TextureUploadMipLayout* mips,
|
||||
const TileTextureElementLayout& element, uint64_t height,
|
||||
uint64_t levels, uint32_t base_pitch) {
|
||||
uint64_t offset = 0;
|
||||
auto pitch = base_pitch;
|
||||
auto h = static_cast<uint32_t>(height);
|
||||
const TileTextureElementLayout& element, uint32_t height,
|
||||
uint32_t levels, uint32_t base_pitch) {
|
||||
uint64_t offset = 0;
|
||||
auto mip_pitch = base_pitch;
|
||||
auto mip_height = height;
|
||||
|
||||
for (uint32_t i = 0; i < levels; i++) {
|
||||
const auto size = CalcLinearUploadLevelSize(element, pitch, h);
|
||||
mips[i].size = size;
|
||||
mips[i].offset = offset;
|
||||
for (uint32_t mip_level = 0; mip_level < levels; ++mip_level) {
|
||||
const auto size = CalcLinearUploadLevelSize(element, mip_pitch, mip_height);
|
||||
mips[mip_level] = {offset, size};
|
||||
|
||||
offset += size;
|
||||
if (pitch > 1) {
|
||||
pitch /= 2;
|
||||
if (mip_pitch > 1) {
|
||||
mip_pitch /= 2;
|
||||
}
|
||||
if (h > 1) {
|
||||
h /= 2;
|
||||
if (mip_height > 1) {
|
||||
mip_height /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
bool FitsBufferRange(uint64_t offset, uint64_t size, uint64_t capacity) {
|
||||
return offset <= capacity && size <= capacity - offset;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TextureUploadLayout TextureCalcUploadLayout(uint32_t fmt, uint32_t width, uint32_t height,
|
||||
uint32_t levels, uint32_t depth, uint32_t tile,
|
||||
uint64_t upload_size, bool allow_depth_tile,
|
||||
bool volume_texture, const char* owner) {
|
||||
RenderTargetFormatInfo TextureGetRenderTargetFormat(Prospero::ChannelLayout layout,
|
||||
Prospero::ChannelType type,
|
||||
Prospero::ChannelOrder order) {
|
||||
const auto encoding = Prospero::ResolveRenderTargetFormat(layout, type);
|
||||
if (encoding.IsValid() && encoding.SupportsOrder(order)) {
|
||||
const auto host_format = ResolveHostFormat(encoding.buffer_format, order);
|
||||
const auto bytes = Prospero::RenderTargetBytesPerElement(encoding.buffer_format);
|
||||
if (host_format.format != vk::Format::eUndefined && bytes != 0) {
|
||||
const auto order_mapping =
|
||||
kRenderTargetColorMappings[static_cast<size_t>(order)][encoding.components - 1u];
|
||||
return {host_format.format, bytes, host_format.host_to_storage.Then(order_mapping)};
|
||||
}
|
||||
}
|
||||
EXIT("unsupported render-target format combination: layout=%u type=%u order=%u\n",
|
||||
static_cast<uint32_t>(layout), static_cast<uint32_t>(type), static_cast<uint32_t>(order));
|
||||
}
|
||||
|
||||
vk::ComponentMapping TextureGetComponentMapping(uint32_t swizzle,
|
||||
Prospero::ColorComponentMapping host_to_storage) {
|
||||
constexpr std::array host_components {vk::ComponentSwizzle::eR, vk::ComponentSwizzle::eG,
|
||||
vk::ComponentSwizzle::eB, vk::ComponentSwizzle::eA};
|
||||
std::array<vk::ComponentSwizzle, 4> storage_components {};
|
||||
for (uint32_t host_component = 0; host_component < host_components.size(); ++host_component) {
|
||||
storage_components[host_to_storage.Map(host_component)] = host_components[host_component];
|
||||
}
|
||||
const auto resolve = [&](uint32_t component) {
|
||||
const auto selector = static_cast<Prospero::CompSwizzle>(GetDstSel(swizzle, component));
|
||||
switch (selector) {
|
||||
case Prospero::CompSwizzle::kZero: return vk::ComponentSwizzle::eZero;
|
||||
case Prospero::CompSwizzle::kOne: return vk::ComponentSwizzle::eOne;
|
||||
case Prospero::CompSwizzle::kRed: return storage_components[0];
|
||||
case Prospero::CompSwizzle::kGreen: return storage_components[1];
|
||||
case Prospero::CompSwizzle::kBlue: return storage_components[2];
|
||||
case Prospero::CompSwizzle::kAlpha: return storage_components[3];
|
||||
default: EXIT("unknown swizzle: %u\n", static_cast<uint32_t>(selector));
|
||||
}
|
||||
};
|
||||
return {resolve(0), resolve(1), resolve(2), resolve(3)};
|
||||
}
|
||||
|
||||
SurfaceFormatInfo TextureGetSurfaceFormatInfo(Prospero::BufferFormat format) {
|
||||
const auto backing_format = Prospero::RemapTextureFormat(format);
|
||||
const auto host_format = ResolveHostFormat(backing_format, Prospero::ChannelOrder::kStandard);
|
||||
const auto conversion_format =
|
||||
backing_format != format ? format : Prospero::BufferFormat::kInvalid;
|
||||
if (host_format.format != vk::Format::eUndefined) {
|
||||
return {host_format.format, conversion_format, host_format.host_to_storage};
|
||||
}
|
||||
EXIT("unknown format: fmt = %u\n", static_cast<uint32_t>(format));
|
||||
}
|
||||
|
||||
TextureUploadLayout TextureCalcUploadLayout(Prospero::BufferFormat format, uint32_t width,
|
||||
uint32_t height, uint32_t levels, uint32_t depth,
|
||||
Prospero::TileMode tile_mode, uint64_t upload_size,
|
||||
bool allow_depth_tile, bool volume_texture,
|
||||
const char* owner) {
|
||||
TextureUploadLayout layout {};
|
||||
layout.surface.description = {
|
||||
fmt,
|
||||
tile,
|
||||
format,
|
||||
tile_mode,
|
||||
volume_texture ? TileSurfaceDimension::Dim3D : TileSurfaceDimension::Dim2D,
|
||||
width,
|
||||
height,
|
||||
@@ -287,49 +189,37 @@ TextureUploadLayout TextureCalcUploadLayout(uint32_t fmt, uint32_t width, uint32
|
||||
volume_texture ? 1u : depth,
|
||||
};
|
||||
const auto& description = layout.surface.description;
|
||||
const auto tile_mode = static_cast<Prospero::TileMode>(description.tile_mode);
|
||||
TileTextureElementLayout element {};
|
||||
|
||||
if (fmt == 0) {
|
||||
if (format == Prospero::BufferFormat::kInvalid) {
|
||||
EXIT("%s: legacy texture upload format unsupported: fmt=0 tile=%u size=%" PRIu64
|
||||
" extent=%ux%u levels=%u\n",
|
||||
owner, description.tile_mode, upload_size, width, height, levels);
|
||||
owner, static_cast<uint32_t>(description.tile_mode), upload_size, width, height,
|
||||
levels);
|
||||
}
|
||||
|
||||
switch (tile_mode) {
|
||||
case Prospero::TileMode::kLinear:
|
||||
if (!TileGetTextureElementLayout(fmt, element)) {
|
||||
EXIT("%s: unsupported linear texture format: fmt=%u\n", owner, fmt);
|
||||
}
|
||||
break;
|
||||
case Prospero::TileMode::kDepth:
|
||||
if (!allow_depth_tile || !TileGetTiledTextureLayout(description, layout.surface)) {
|
||||
EXIT("%s: unsupported typed tiled upload: fmt=%u tile=%u "
|
||||
"size=%" PRIu64 " extent=%ux%u levels=%u\n",
|
||||
owner, static_cast<uint32_t>(fmt), description.tile_mode, upload_size, width,
|
||||
height, levels);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (!TileGetTiledTextureLayout(description, layout.surface)) {
|
||||
EXIT("%s: unsupported typed tiled upload: fmt=%u tile=%u "
|
||||
"size=%" PRIu64 " extent=%ux%u levels=%u\n",
|
||||
owner, static_cast<uint32_t>(fmt), description.tile_mode, upload_size, width,
|
||||
height, levels);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (tile_mode != Prospero::TileMode::kLinear) {
|
||||
if (tile_mode == Prospero::TileMode::kLinear) {
|
||||
if (!TileGetTextureElementLayout(format, element)) {
|
||||
EXIT("%s: unsupported linear texture format: fmt=%u\n", owner,
|
||||
static_cast<uint32_t>(format));
|
||||
}
|
||||
} else {
|
||||
if ((tile_mode == Prospero::TileMode::kDepth && !allow_depth_tile) ||
|
||||
!TileGetTiledTextureLayout(description, layout.surface)) {
|
||||
EXIT("%s: unsupported typed tiled upload: fmt=%u tile=%u "
|
||||
"size=%" PRIu64 " extent=%ux%u levels=%u\n",
|
||||
owner, static_cast<uint32_t>(format), static_cast<uint32_t>(tile_mode),
|
||||
upload_size, width, height, levels);
|
||||
}
|
||||
element = {layout.surface.texture.block.bytes_per_element,
|
||||
layout.surface.texture.texel_width, layout.surface.texture.texel_height};
|
||||
}
|
||||
layout.pitch = TileGetTexturePitch(fmt, width, description.tile_mode);
|
||||
layout.pitch = TileGetTexturePitch(format, width, description.tile_mode);
|
||||
if (tile_mode == Prospero::TileMode::kLinear) {
|
||||
TileSizeOffset level_sizes[16] {};
|
||||
TilePaddedSize padded_sizes[16] {};
|
||||
TileGetTextureSize(fmt, width, height, levels, description.tile_mode, nullptr, level_sizes,
|
||||
padded_sizes);
|
||||
TileGetTextureSize(format, width, height, levels, description.tile_mode, nullptr,
|
||||
level_sizes, padded_sizes);
|
||||
for (uint32_t level = 0; level < levels; ++level) {
|
||||
layout.mips[level] = {level_sizes[level].offset, level_sizes[level].size,
|
||||
padded_sizes[level].width, padded_sizes[level].height};
|
||||
@@ -353,42 +243,33 @@ TextureUploadLayout TextureCalcUploadLayout(uint32_t fmt, uint32_t width, uint32
|
||||
std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayout& layout) {
|
||||
const auto& description = layout.surface.description;
|
||||
const bool volume_texture = description.dimension == TileSurfaceDimension::Dim3D;
|
||||
const bool linear_texture = description.tile_mode == Prospero::TileMode::kLinear;
|
||||
const auto depth = volume_texture ? description.depth : description.layers;
|
||||
uint32_t mip_width = description.width;
|
||||
uint32_t mip_height = description.height;
|
||||
uint32_t mip_pitch = volume_texture && static_cast<Prospero::TileMode>(description.tile_mode) !=
|
||||
Prospero::TileMode::kLinear
|
||||
? description.width
|
||||
: layout.pitch;
|
||||
uint32_t mip_pitch = volume_texture && !linear_texture ? description.width : layout.pitch;
|
||||
|
||||
std::vector<vk::BufferImageCopy> regions;
|
||||
regions.reserve(GetTextureRegionCount(depth, description.levels, volume_texture));
|
||||
for (uint32_t i = 0; i < description.levels; i++) {
|
||||
EXIT_NOT_IMPLEMENTED(layout.mips[i].size == 0);
|
||||
|
||||
const auto mip_depth = GetTextureLevelDepth(depth, i, volume_texture);
|
||||
|
||||
for (uint32_t z = 0; z < mip_depth; z++) {
|
||||
const auto slice_offset = z * layout.slice_stride;
|
||||
vk::BufferImageCopy region {};
|
||||
region.bufferOffset = layout.mips[i].offset + slice_offset;
|
||||
region.imageSubresource = {vk::ImageAspectFlagBits::eColor, i, volume_texture ? 0u : z,
|
||||
1};
|
||||
region.imageOffset.z = volume_texture ? static_cast<int>(z) : 0;
|
||||
region.imageExtent = {mip_width, mip_height, 1};
|
||||
const bool linear = static_cast<Prospero::TileMode>(description.tile_mode) ==
|
||||
Prospero::TileMode::kLinear;
|
||||
if (linear) {
|
||||
region.bufferRowLength = layout.mips[i].row_length;
|
||||
region.bufferImageHeight = layout.mips[i].image_height;
|
||||
} else {
|
||||
const auto texel_width = layout.surface.texture.texel_width;
|
||||
const auto align = [](uint32_t value, uint32_t block) {
|
||||
return ((value + block - 1u) / block) * block;
|
||||
};
|
||||
const auto pitch = align(mip_pitch, texel_width);
|
||||
region.bufferRowLength = pitch > align(mip_width, texel_width) ? pitch : 0;
|
||||
}
|
||||
for (uint32_t mip_level = 0; mip_level < description.levels; ++mip_level) {
|
||||
EXIT_NOT_IMPLEMENTED(layout.mips[mip_level].size == 0);
|
||||
const auto mip_depth = GetTextureLevelDepth(depth, mip_level, volume_texture);
|
||||
vk::BufferImageCopy region {};
|
||||
region.imageSubresource = {vk::ImageAspectFlagBits::eColor, mip_level, 0, 1};
|
||||
region.imageExtent = {mip_width, mip_height, 1};
|
||||
if (linear_texture) {
|
||||
region.bufferRowLength = layout.mips[mip_level].row_length;
|
||||
region.bufferImageHeight = layout.mips[mip_level].image_height;
|
||||
} else {
|
||||
const auto texel_width = layout.surface.texture.texel_width;
|
||||
const auto aligned_pitch = Common::AlignUp(mip_pitch, texel_width);
|
||||
region.bufferRowLength =
|
||||
aligned_pitch > Common::AlignUp(mip_width, texel_width) ? aligned_pitch : 0;
|
||||
}
|
||||
for (uint32_t slice_index = 0; slice_index < mip_depth; ++slice_index) {
|
||||
region.bufferOffset = layout.mips[mip_level].offset + slice_index * layout.slice_stride;
|
||||
region.imageSubresource.baseArrayLayer = volume_texture ? 0u : slice_index;
|
||||
region.imageOffset.z = volume_texture ? static_cast<int>(slice_index) : 0;
|
||||
regions.push_back(region);
|
||||
}
|
||||
|
||||
@@ -406,17 +287,9 @@ std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayo
|
||||
return regions;
|
||||
}
|
||||
|
||||
static bool SetGpuTileSize(uint64_t offset, uint64_t length, uint64_t capacity, uint64_t& size) {
|
||||
if (offset > capacity || length > capacity - offset) {
|
||||
return false;
|
||||
}
|
||||
size = length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextureBuildGpuTileInfos(uint64_t tiled_size, const std::vector<vk::BufferImageCopy>& regions,
|
||||
const TextureUploadLayout& layout, uint32_t levels,
|
||||
std::vector<GpuTileInfo>& out_infos) {
|
||||
std::vector<GpuTileInfo>& out_tile_infos) {
|
||||
const auto& description = layout.surface.description;
|
||||
const bool volume_texture = description.dimension == TileSurfaceDimension::Dim3D;
|
||||
const auto depth = volume_texture ? description.depth : description.layers;
|
||||
@@ -434,111 +307,69 @@ bool TextureBuildGpuTileInfos(uint64_t tiled_size, const std::vector<vk::BufferI
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<GpuTileInfo> infos;
|
||||
infos.reserve(regions.size());
|
||||
if (volume_texture) {
|
||||
size_t region_base = 0;
|
||||
for (uint32_t level = 0; level < levels; ++level) {
|
||||
const uint32_t mip_depth = GetTextureLevelDepth(depth, level, true);
|
||||
const bool tail = level >= surface.first_tail_level;
|
||||
const uint64_t linear_stride = layout.slice_stride;
|
||||
for (uint32_t z = 0; z < mip_depth; z += block.block_depth) {
|
||||
const uint32_t copy_depth = std::min(block.block_depth, mip_depth - z);
|
||||
const auto& region = regions[region_base + z];
|
||||
const auto pitch =
|
||||
region.bufferRowLength != 0 ? region.bufferRowLength : region.imageExtent.width;
|
||||
const auto logical_height = region.bufferImageHeight != 0
|
||||
? region.bufferImageHeight
|
||||
: region.imageExtent.height;
|
||||
GpuTileInfo info {};
|
||||
info.family = block.family;
|
||||
info.bytes_per_element = block.bytes_per_element;
|
||||
info.linear_offset = region.bufferOffset;
|
||||
info.tiled_offset =
|
||||
static_cast<uint64_t>(z / block.block_depth) * surface.block_slice_size +
|
||||
surface.mips[level].offset;
|
||||
const uint64_t linear_span =
|
||||
static_cast<uint64_t>(copy_depth - 1u) * linear_stride +
|
||||
layout.mips[level].size;
|
||||
if (!SetGpuTileSize(info.linear_offset, linear_span, UINT64_MAX,
|
||||
info.linear_size) ||
|
||||
!SetGpuTileSize(info.tiled_offset, surface.mips[level].size, tiled_size,
|
||||
info.tiled_size)) {
|
||||
return false;
|
||||
}
|
||||
info.linear_slice_stride = linear_stride;
|
||||
info.width = std::max((region.imageExtent.width + texture.texel_width - 1u) /
|
||||
texture.texel_width,
|
||||
1u);
|
||||
info.height = std::max(
|
||||
(logical_height + texture.texel_height - 1u) / texture.texel_height, 1u);
|
||||
info.depth = copy_depth;
|
||||
info.surface_z =
|
||||
std::vector<GpuTileInfo> tile_infos;
|
||||
tile_infos.reserve(regions.size());
|
||||
const auto slices_per_copy = volume_texture ? block.block_depth : 1u;
|
||||
size_t region_base = 0;
|
||||
for (uint32_t mip_level = 0; mip_level < levels; ++mip_level) {
|
||||
const auto& mip = surface.mips[mip_level];
|
||||
const auto mip_depth = GetTextureLevelDepth(depth, mip_level, volume_texture);
|
||||
for (uint32_t slice_index = 0; slice_index < mip_depth; slice_index += slices_per_copy) {
|
||||
const auto& region = regions[region_base + slice_index];
|
||||
GpuTileInfo tile_info {};
|
||||
tile_info.family = block.family;
|
||||
tile_info.bytes_per_element = block.bytes_per_element;
|
||||
tile_info.linear_offset = region.bufferOffset;
|
||||
tile_info.linear_size = layout.mips[mip_level].size;
|
||||
tile_info.tiled_size = mip.size;
|
||||
if (volume_texture) {
|
||||
tile_info.depth = std::min(slices_per_copy, mip_depth - slice_index);
|
||||
tile_info.linear_slice_stride = layout.slice_stride;
|
||||
tile_info.linear_size += (tile_info.depth - 1u) * layout.slice_stride;
|
||||
tile_info.tiled_offset =
|
||||
mip.offset +
|
||||
static_cast<uint64_t>(slice_index / slices_per_copy) * surface.block_slice_size;
|
||||
tile_info.surface_z =
|
||||
block.block_depth == 1 ? static_cast<uint32_t>(region.imageOffset.z) : 0;
|
||||
info.pitch = std::max((pitch + texture.texel_width - 1u) / texture.texel_width, 1u);
|
||||
info.tail_x = tail ? surface.mips[level].tail_x : 0;
|
||||
info.tail_y = tail ? surface.mips[level].tail_y : 0;
|
||||
info.tail = tail;
|
||||
info.tiled_width = surface.mips[level].padded_width;
|
||||
info.tiled_height = surface.mips[level].padded_height;
|
||||
infos.push_back(info);
|
||||
}
|
||||
region_base += mip_depth;
|
||||
}
|
||||
} else {
|
||||
size_t region_index = 0;
|
||||
for (uint32_t level = 0; level < levels; level++) {
|
||||
const auto& level_size = layout.mips[level];
|
||||
const auto& mip = surface.mips[level];
|
||||
const bool tail = level >= surface.first_tail_level;
|
||||
const auto level_depth = GetTextureLevelDepth(depth, level, false);
|
||||
for (uint32_t z = 0; z < level_depth; z++) {
|
||||
const auto& region = regions[region_index++];
|
||||
const auto pitch =
|
||||
region.bufferRowLength != 0 ? region.bufferRowLength : region.imageExtent.width;
|
||||
const auto logical_height = region.bufferImageHeight != 0
|
||||
? region.bufferImageHeight
|
||||
: region.imageExtent.height;
|
||||
GpuTileInfo info {};
|
||||
info.family = block.family;
|
||||
info.bytes_per_element = block.bytes_per_element;
|
||||
info.linear_offset = region.bufferOffset;
|
||||
} else {
|
||||
const auto source_stride = layout.source_slice_stride != 0
|
||||
? layout.source_slice_stride
|
||||
: surface.block_slice_size;
|
||||
if (z > (UINT64_MAX - mip.offset) / source_stride) {
|
||||
if (slice_index > (UINT64_MAX - mip.offset) / source_stride) {
|
||||
return false;
|
||||
}
|
||||
info.tiled_offset = mip.offset + static_cast<uint64_t>(z) * source_stride;
|
||||
if (!SetGpuTileSize(info.linear_offset, level_size.size, UINT64_MAX,
|
||||
info.linear_size) ||
|
||||
!SetGpuTileSize(info.tiled_offset, mip.size, tiled_size, info.tiled_size)) {
|
||||
return false;
|
||||
}
|
||||
info.width = std::max((region.imageExtent.width + texture.texel_width - 1u) /
|
||||
texture.texel_width,
|
||||
1u);
|
||||
info.height = std::max(
|
||||
(logical_height + texture.texel_height - 1u) / texture.texel_height, 1u);
|
||||
info.surface_z = block.family == TileBlockFamily::RenderTarget64KB ||
|
||||
block.family == TileBlockFamily::Depth64KB
|
||||
? region.imageSubresource.baseArrayLayer
|
||||
: 0;
|
||||
info.pitch = std::max((pitch + texture.texel_width - 1u) / texture.texel_width, 1u);
|
||||
info.tail = tail;
|
||||
info.tail_x = tail ? mip.tail_x : 0;
|
||||
info.tail_y = tail ? mip.tail_y : 0;
|
||||
info.tiled_width = mip.padded_width;
|
||||
info.tiled_height = mip.padded_height;
|
||||
infos.push_back(info);
|
||||
tile_info.tiled_offset =
|
||||
mip.offset + static_cast<uint64_t>(slice_index) * source_stride;
|
||||
tile_info.surface_z = block.family == TileBlockFamily::RenderTarget64KB ||
|
||||
block.family == TileBlockFamily::Depth64KB
|
||||
? region.imageSubresource.baseArrayLayer
|
||||
: 0;
|
||||
}
|
||||
if (!FitsBufferRange(tile_info.linear_offset, tile_info.linear_size, UINT64_MAX) ||
|
||||
!FitsBufferRange(tile_info.tiled_offset, tile_info.tiled_size, tiled_size)) {
|
||||
return false;
|
||||
}
|
||||
const auto row_length =
|
||||
region.bufferRowLength != 0 ? region.bufferRowLength : region.imageExtent.width;
|
||||
const auto image_height = region.bufferImageHeight != 0 ? region.bufferImageHeight
|
||||
: region.imageExtent.height;
|
||||
tile_info.width = std::max(
|
||||
(region.imageExtent.width + texture.texel_width - 1u) / texture.texel_width, 1u);
|
||||
tile_info.height =
|
||||
std::max((image_height + texture.texel_height - 1u) / texture.texel_height, 1u);
|
||||
tile_info.pitch =
|
||||
std::max((row_length + texture.texel_width - 1u) / texture.texel_width, 1u);
|
||||
tile_info.tail = mip_level >= surface.first_tail_level;
|
||||
tile_info.tail_x = tile_info.tail ? mip.tail_x : 0;
|
||||
tile_info.tail_y = tile_info.tail ? mip.tail_y : 0;
|
||||
tile_info.tiled_width = mip.padded_width;
|
||||
tile_info.tiled_height = mip.padded_height;
|
||||
tile_infos.push_back(tile_info);
|
||||
}
|
||||
region_base += mip_depth;
|
||||
}
|
||||
|
||||
if (infos.empty()) {
|
||||
return false;
|
||||
}
|
||||
out_infos = std::move(infos);
|
||||
out_tile_infos = std::move(tile_infos);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_HOST_GPU_RENDERER_IMAGE_TEXTURECOMMON_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_HOST_GPU_RENDERER_IMAGE_TEXTURECOMMON_H_
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "common/common.h"
|
||||
#include "graphics/guest_gpu/gpu_defs.h"
|
||||
#include "graphics/guest_gpu/tile.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
@@ -19,6 +17,12 @@ struct RenderTargetFormatInfo {
|
||||
Prospero::ColorComponentMapping export_mapping;
|
||||
};
|
||||
|
||||
struct SurfaceFormatInfo {
|
||||
vk::Format vk_format;
|
||||
Prospero::BufferFormat conversion_format;
|
||||
Prospero::ColorComponentMapping host_to_storage;
|
||||
};
|
||||
|
||||
struct TextureUploadMipLayout {
|
||||
uint64_t offset = 0;
|
||||
uint64_t size = 0;
|
||||
@@ -34,17 +38,21 @@ struct TextureUploadLayout {
|
||||
TextureUploadMipLayout mips[16] = {};
|
||||
};
|
||||
|
||||
vk::ComponentMapping TextureGetComponentMapping(uint32_t swizzle);
|
||||
vk::Format TextureGetFormat(uint32_t fmt);
|
||||
RenderTargetFormatInfo TextureGetRenderTargetFormat(uint32_t layout, uint32_t type, uint32_t order);
|
||||
TextureUploadLayout TextureCalcUploadLayout(uint32_t fmt, uint32_t width, uint32_t height,
|
||||
uint32_t levels, uint32_t depth, uint32_t tile,
|
||||
uint64_t upload_size, bool allow_depth_tile,
|
||||
bool volume_texture, const char* owner);
|
||||
vk::ComponentMapping TextureGetComponentMapping(uint32_t swizzle,
|
||||
Prospero::ColorComponentMapping host_to_storage);
|
||||
SurfaceFormatInfo TextureGetSurfaceFormatInfo(Prospero::BufferFormat format);
|
||||
RenderTargetFormatInfo TextureGetRenderTargetFormat(Prospero::ChannelLayout layout,
|
||||
Prospero::ChannelType type,
|
||||
Prospero::ChannelOrder order);
|
||||
TextureUploadLayout TextureCalcUploadLayout(Prospero::BufferFormat format, uint32_t width,
|
||||
uint32_t height, uint32_t levels, uint32_t depth,
|
||||
Prospero::TileMode tile_mode, uint64_t upload_size,
|
||||
bool allow_depth_tile, bool volume_texture,
|
||||
const char* owner);
|
||||
std::vector<vk::BufferImageCopy> TextureBuildImageCopies(const TextureUploadLayout& layout);
|
||||
bool TextureBuildGpuTileInfos(uint64_t tiled_size, const std::vector<vk::BufferImageCopy>& regions,
|
||||
const TextureUploadLayout& layout, uint32_t levels,
|
||||
std::vector<GpuTileInfo>& infos);
|
||||
std::vector<GpuTileInfo>& out_tile_infos);
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "graphics/host_gpu/renderer/image/tiler.h"
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "gpu_tiler_shaders/gpu_tiler_demote_d16_spv.h"
|
||||
#include "gpu_tiler_shaders/gpu_tiler_depth_spv.h"
|
||||
@@ -40,7 +41,6 @@ TileManager::TileManager(GraphicContext& graphics, CommandScheduler& scheduler,
|
||||
nullptr};
|
||||
|
||||
vk::DescriptorSetLayoutCreateInfo descriptor_info {};
|
||||
descriptor_info.sType = vk::StructureType::eDescriptorSetLayoutCreateInfo;
|
||||
descriptor_info.flags = vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR;
|
||||
descriptor_info.bindingCount = static_cast<uint32_t>(bindings.size());
|
||||
descriptor_info.pBindings = bindings.data();
|
||||
@@ -50,7 +50,6 @@ TileManager::TileManager(GraphicContext& graphics, CommandScheduler& scheduler,
|
||||
|
||||
const vk::PushConstantRange push_range {vk::ShaderStageFlagBits::eCompute, 0, sizeof(Push)};
|
||||
vk::PipelineLayoutCreateInfo layout_info {};
|
||||
layout_info.sType = vk::StructureType::ePipelineLayoutCreateInfo;
|
||||
layout_info.setLayoutCount = 1;
|
||||
layout_info.pSetLayouts = &m_descriptor_layout;
|
||||
layout_info.pushConstantRangeCount = 1;
|
||||
@@ -92,11 +91,9 @@ TileManager::~TileManager() {
|
||||
TileManager::Scratch TileManager::AllocateScratch(uint64_t size) {
|
||||
EXIT_IF(size == 0);
|
||||
vk::BufferCreateInfo create {};
|
||||
create.sType = vk::StructureType::eBufferCreateInfo;
|
||||
create.size = size;
|
||||
create.usage = vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eTransferSrc |
|
||||
vk::BufferUsageFlagBits::eTransferDst;
|
||||
create.sharingMode = vk::SharingMode::eExclusive;
|
||||
|
||||
VmaAllocationCreateInfo allocate {};
|
||||
allocate.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
|
||||
@@ -225,7 +222,7 @@ void TileManager::Prepare(bool tile, uint64_t tiled_capacity, uint64_t linear_ca
|
||||
|
||||
const uint64_t uniform_alignment =
|
||||
std::max<uint64_t>(limits.minUniformBufferOffsetAlignment, 1);
|
||||
const uint64_t stride = (sizeof(Push) + uniform_alignment - 1) & ~(uniform_alignment - 1);
|
||||
const uint64_t stride = Common::AlignUp<uint64_t>(sizeof(Push), uniform_alignment);
|
||||
EXIT_NOT_IMPLEMENTED(dispatches.size() > UINT64_MAX / stride);
|
||||
const uint64_t bytes = dispatches.size() * stride;
|
||||
auto [mapped, offset] = m_stream_buffer.Map(bytes, uniform_alignment);
|
||||
@@ -263,21 +260,14 @@ vk::Pipeline TileManager::GetPipeline(uint32_t slot) {
|
||||
const uint32_t values[] {1u << element_index, direction_index};
|
||||
const vk::SpecializationMapEntry entries[] {{0, 0, 4}, {1, 4, 4}};
|
||||
const vk::SpecializationInfo specialization {2, entries, sizeof(values), values};
|
||||
vk::ShaderModuleCreateInfo module_info {};
|
||||
module_info.sType = vk::StructureType::eShaderModuleCreateInfo;
|
||||
module_info.codeSize = shaders[family_index].words * sizeof(uint32_t);
|
||||
module_info.pCode = shaders[family_index].code;
|
||||
vk::ShaderModule module = nullptr;
|
||||
RequireVulkanSuccess(m_graphics.device.createShaderModule(&module_info, nullptr, &module),
|
||||
"create TileManager shader module");
|
||||
const auto module =
|
||||
CompileSPV({shaders[family_index].code, shaders[family_index].words}, m_graphics.device);
|
||||
vk::PipelineShaderStageCreateInfo stage {};
|
||||
stage.sType = vk::StructureType::ePipelineShaderStageCreateInfo;
|
||||
stage.stage = vk::ShaderStageFlagBits::eCompute;
|
||||
stage.module = module;
|
||||
stage.pName = "main";
|
||||
stage.pSpecializationInfo = &specialization;
|
||||
vk::ComputePipelineCreateInfo create {};
|
||||
create.sType = vk::StructureType::eComputePipelineCreateInfo;
|
||||
create.stage = stage;
|
||||
create.layout = m_pipeline_layout;
|
||||
const auto result =
|
||||
@@ -287,19 +277,19 @@ vk::Pipeline TileManager::GetPipeline(uint32_t slot) {
|
||||
return m_pipelines[slot];
|
||||
}
|
||||
|
||||
void TileManager::Record(bool tile, vk::Buffer source, uint64_t source_offset,
|
||||
void TileManager::Record(vk::Buffer source, uint64_t source_offset,
|
||||
uint64_t source_capacity, vk::Buffer target, uint64_t target_offset,
|
||||
uint64_t target_capacity, std::span<Dispatch> dispatches,
|
||||
bool clear_target) {
|
||||
const auto& limits = m_graphics.GetPhysicalDeviceProperties().limits;
|
||||
const uint64_t descriptor_alignment =
|
||||
std::max<uint64_t>(limits.minStorageBufferOffsetAlignment, 4);
|
||||
const uint64_t source_descriptor_offset = source_offset & ~(descriptor_alignment - 1);
|
||||
const uint64_t target_descriptor_offset = target_offset & ~(descriptor_alignment - 1);
|
||||
const uint64_t source_descriptor_offset = Common::AlignDown(source_offset, descriptor_alignment);
|
||||
const uint64_t target_descriptor_offset = Common::AlignDown(target_offset, descriptor_alignment);
|
||||
const uint64_t source_base = source_offset - source_descriptor_offset;
|
||||
const uint64_t target_base = target_offset - target_descriptor_offset;
|
||||
const uint64_t source_range = (source_base + source_capacity + 3u) & ~uint64_t {3};
|
||||
const uint64_t target_range = (target_base + target_capacity + 3u) & ~uint64_t {3};
|
||||
const uint64_t source_range = Common::AlignUp(source_base + source_capacity, 4);
|
||||
const uint64_t target_range = Common::AlignUp(target_base + target_capacity, 4);
|
||||
EXIT_NOT_IMPLEMENTED(source_range > limits.maxStorageBufferRange ||
|
||||
target_range > limits.maxStorageBufferRange || target_offset % 4 != 0 ||
|
||||
target_capacity % 4 != 0);
|
||||
@@ -307,7 +297,6 @@ void TileManager::Record(bool tile, vk::Buffer source, uint64_t source_offset,
|
||||
m_scheduler.EndRendering();
|
||||
auto command = m_scheduler.Current().Handle();
|
||||
vk::BufferMemoryBarrier barriers[3] {};
|
||||
barriers[0].sType = vk::StructureType::eBufferMemoryBarrier;
|
||||
barriers[0].srcAccessMask = vk::AccessFlagBits::eMemoryWrite | vk::AccessFlagBits::eHostWrite;
|
||||
barriers[0].dstAccessMask = vk::AccessFlagBits::eShaderRead;
|
||||
barriers[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
@@ -316,7 +305,6 @@ void TileManager::Record(bool tile, vk::Buffer source, uint64_t source_offset,
|
||||
barriers[0].offset = source_offset;
|
||||
barriers[0].size = source_capacity;
|
||||
barriers[1] = barriers[0];
|
||||
barriers[1].srcAccessMask = vk::AccessFlagBits::eMemoryWrite | vk::AccessFlagBits::eHostWrite;
|
||||
barriers[1].dstAccessMask =
|
||||
clear_target ? vk::AccessFlagBits::eTransferWrite
|
||||
: vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite;
|
||||
@@ -352,7 +340,6 @@ void TileManager::Record(bool tile, vk::Buffer source, uint64_t source_offset,
|
||||
const vk::DescriptorBufferInfo infos[] {source_info, target_info, params_info};
|
||||
std::array<vk::WriteDescriptorSet, 3> writes {};
|
||||
for (uint32_t index = 0; index < writes.size(); index++) {
|
||||
writes[index].sType = vk::StructureType::eWriteDescriptorSet;
|
||||
writes[index].dstBinding = index;
|
||||
writes[index].descriptorCount = 1;
|
||||
writes[index].descriptorType = index == 2 ? vk::DescriptorType::eUniformBuffer
|
||||
@@ -382,9 +369,9 @@ TileManager::Result TileManager::Detile(vk::Buffer tiled, uint64_t tiled_offset,
|
||||
const uint64_t source_base = tiled_offset & (descriptor_alignment - 1);
|
||||
std::vector<Dispatch> dispatches;
|
||||
Prepare(false, tiled_capacity, linear_capacity, infos, source_base, 0, dispatches);
|
||||
auto scratch = AllocateScratch((linear_capacity + 3u) & ~uint64_t {3});
|
||||
auto scratch = AllocateScratch(Common::AlignUp(linear_capacity, 4));
|
||||
DeferDestroy(scratch);
|
||||
Record(false, tiled, tiled_offset, tiled_capacity, scratch.buffer, 0, scratch.size, dispatches,
|
||||
Record(tiled, tiled_offset, tiled_capacity, scratch.buffer, 0, scratch.size, dispatches,
|
||||
true);
|
||||
return {scratch.buffer, 0, linear_capacity};
|
||||
}
|
||||
@@ -399,7 +386,7 @@ void TileManager::Tile(vk::Buffer linear, uint64_t linear_offset, uint64_t linea
|
||||
const uint64_t target_base = tiled_offset & (descriptor_alignment - 1);
|
||||
std::vector<Dispatch> dispatches;
|
||||
Prepare(true, tiled_capacity, linear_capacity, infos, source_base, target_base, dispatches);
|
||||
Record(true, linear, linear_offset, linear_capacity, tiled, tiled_offset, tiled_capacity,
|
||||
Record(linear, linear_offset, linear_capacity, tiled, tiled_offset, tiled_capacity,
|
||||
dispatches, false);
|
||||
}
|
||||
|
||||
@@ -416,19 +403,19 @@ void TileManager::TileImage(Image& image, std::span<const vk::BufferImageCopy> r
|
||||
// Reserve all stream parameters before creating a scheduler-lived scratch dependency:
|
||||
// StreamBuffer::Map is allowed to submit the current tick when it wraps.
|
||||
Prepare(true, tiled_capacity, linear_capacity, infos, 0, target_base, dispatches);
|
||||
auto linear = AllocateScratch((linear_capacity + 3u) & ~uint64_t {3});
|
||||
auto linear = AllocateScratch(Common::AlignUp(linear_capacity, 4));
|
||||
DeferDestroy(linear);
|
||||
image.Download(regions, linear.buffer, 0, linear.size);
|
||||
Result source {linear.buffer, 0, linear.size};
|
||||
if (transform == ColorTransform::SwapBgra16) {
|
||||
source = SwapBgra16(source);
|
||||
}
|
||||
Record(true, source.buffer, source.offset, linear_capacity, tiled, tiled_offset, tiled_capacity,
|
||||
Record(source.buffer, source.offset, linear_capacity, tiled, tiled_offset, tiled_capacity,
|
||||
dispatches, false);
|
||||
}
|
||||
|
||||
TileManager::Result TileManager::GetScratchBuffer(uint64_t size) {
|
||||
auto scratch = AllocateScratch((size + 3u) & ~uint64_t {3});
|
||||
auto scratch = AllocateScratch(Common::AlignUp(size, 4));
|
||||
DeferDestroy(scratch);
|
||||
return {scratch.buffer, 0, scratch.size};
|
||||
}
|
||||
@@ -436,11 +423,11 @@ TileManager::Result TileManager::GetScratchBuffer(uint64_t size) {
|
||||
TileManager::StorageBinding TileManager::BindStorage(Result buffer, uint64_t size) const {
|
||||
const auto& limits = m_graphics.GetPhysicalDeviceProperties().limits;
|
||||
const auto alignment = std::max<uint64_t>(limits.minStorageBufferOffsetAlignment, 4);
|
||||
const auto descriptor_offset = buffer.offset - buffer.offset % alignment;
|
||||
const auto descriptor_offset = Common::AlignDown(buffer.offset, alignment);
|
||||
const auto base = buffer.offset - descriptor_offset;
|
||||
EXIT_IF(buffer.buffer == nullptr || size == 0 || buffer.size < size || base > UINT32_MAX ||
|
||||
size > UINT64_MAX - base || base + size > UINT64_MAX - 3);
|
||||
const auto range = (base + size + 3) & ~uint64_t {3};
|
||||
const auto range = Common::AlignUp(base + size, 4);
|
||||
EXIT_IF(range > limits.maxStorageBufferRange || range > UINT32_MAX);
|
||||
return {{buffer.buffer, descriptor_offset, range}, static_cast<uint32_t>(base)};
|
||||
}
|
||||
@@ -481,21 +468,13 @@ void TileManager::ConvertD16(Result source, Result target, D16Direction directio
|
||||
code = GPU_TILER_DEMOTE_D16_SPV;
|
||||
words = std::size(GPU_TILER_DEMOTE_D16_SPV);
|
||||
}
|
||||
vk::ShaderModuleCreateInfo module_info {};
|
||||
module_info.sType = vk::StructureType::eShaderModuleCreateInfo;
|
||||
module_info.codeSize = words * sizeof(uint32_t);
|
||||
module_info.pCode = code;
|
||||
vk::ShaderModule module = nullptr;
|
||||
RequireVulkanSuccess(m_graphics.device.createShaderModule(&module_info, nullptr, &module),
|
||||
"create D16 conversion shader module");
|
||||
const auto module = CompileSPV({code, words}, m_graphics.device);
|
||||
vk::PipelineShaderStageCreateInfo stage {};
|
||||
stage.sType = vk::StructureType::ePipelineShaderStageCreateInfo;
|
||||
stage.stage = vk::ShaderStageFlagBits::eCompute;
|
||||
stage.module = module;
|
||||
stage.pName = "main";
|
||||
stage.pSpecializationInfo = &specialization;
|
||||
vk::ComputePipelineCreateInfo create {};
|
||||
create.sType = vk::StructureType::eComputePipelineCreateInfo;
|
||||
create.stage = stage;
|
||||
create.layout = m_pipeline_layout;
|
||||
const auto result =
|
||||
@@ -525,14 +504,13 @@ void TileManager::ConvertD16(Result source, Result target, D16Direction directio
|
||||
const auto target_required = required(layout.height, layout.layers, layout.target_row_stride,
|
||||
layout.target_slice_stride, target_active);
|
||||
EXIT_IF(source_required > UINT64_MAX - 3 || target_required > UINT64_MAX - 3);
|
||||
const auto source_barrier_size = (source_required + 3) & ~uint64_t {3};
|
||||
const auto target_barrier_size = (target_required + 3) & ~uint64_t {3};
|
||||
const auto source_barrier_size = Common::AlignUp(source_required, 4);
|
||||
const auto target_barrier_size = Common::AlignUp(target_required, 4);
|
||||
EXIT_IF(source.size < source_barrier_size || target.size < target_barrier_size);
|
||||
|
||||
m_scheduler.EndRendering();
|
||||
auto command = m_scheduler.Current().Handle();
|
||||
vk::BufferMemoryBarrier barriers[2] {};
|
||||
barriers[0].sType = vk::StructureType::eBufferMemoryBarrier;
|
||||
barriers[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barriers[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barriers[0].buffer = source.buffer;
|
||||
@@ -542,7 +520,6 @@ void TileManager::ConvertD16(Result source, Result target, D16Direction directio
|
||||
vk::AccessFlagBits::eTransferWrite |
|
||||
vk::AccessFlagBits::eShaderWrite;
|
||||
barriers[0].dstAccessMask = vk::AccessFlagBits::eShaderRead;
|
||||
barriers[1].sType = vk::StructureType::eBufferMemoryBarrier;
|
||||
barriers[1].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barriers[1].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barriers[1].buffer = target.buffer;
|
||||
@@ -596,7 +573,6 @@ void TileManager::ConvertD16(Result source, Result target, D16Direction directio
|
||||
};
|
||||
std::array<vk::WriteDescriptorSet, 2> writes {};
|
||||
for (uint32_t index = 0; index < writes.size(); index++) {
|
||||
writes[index].sType = vk::StructureType::eWriteDescriptorSet;
|
||||
writes[index].dstBinding = index;
|
||||
writes[index].descriptorCount = 1;
|
||||
writes[index].descriptorType = vk::DescriptorType::eStorageBuffer;
|
||||
@@ -626,20 +602,12 @@ void TileManager::ConvertD16(Result source, Result target, D16Direction directio
|
||||
|
||||
void TileManager::SwapBgra16(Result input, Result output, uint32_t pixels) {
|
||||
if (m_swap_bgra16 == nullptr) {
|
||||
vk::ShaderModuleCreateInfo module_info {};
|
||||
module_info.sType = vk::StructureType::eShaderModuleCreateInfo;
|
||||
module_info.codeSize = std::size(GPU_TILER_SWAP_BGRA16_SPV) * sizeof(uint32_t);
|
||||
module_info.pCode = GPU_TILER_SWAP_BGRA16_SPV;
|
||||
vk::ShaderModule module = nullptr;
|
||||
RequireVulkanSuccess(m_graphics.device.createShaderModule(&module_info, nullptr, &module),
|
||||
"create BGRA16 swap shader module");
|
||||
const auto module = CompileSPV(GPU_TILER_SWAP_BGRA16_SPV, m_graphics.device);
|
||||
vk::PipelineShaderStageCreateInfo stage {};
|
||||
stage.sType = vk::StructureType::ePipelineShaderStageCreateInfo;
|
||||
stage.stage = vk::ShaderStageFlagBits::eCompute;
|
||||
stage.module = module;
|
||||
stage.pName = "main";
|
||||
vk::ComputePipelineCreateInfo create {};
|
||||
create.sType = vk::StructureType::eComputePipelineCreateInfo;
|
||||
create.stage = stage;
|
||||
create.layout = m_pipeline_layout;
|
||||
const auto result =
|
||||
@@ -658,7 +626,6 @@ void TileManager::SwapBgra16(Result input, Result output, uint32_t pixels) {
|
||||
};
|
||||
std::array<vk::WriteDescriptorSet, 2> writes {};
|
||||
for (uint32_t index = 0; index < writes.size(); index++) {
|
||||
writes[index].sType = vk::StructureType::eWriteDescriptorSet;
|
||||
writes[index].dstBinding = index;
|
||||
writes[index].descriptorCount = 1;
|
||||
writes[index].descriptorType = vk::DescriptorType::eStorageBuffer;
|
||||
@@ -666,7 +633,6 @@ void TileManager::SwapBgra16(Result input, Result output, uint32_t pixels) {
|
||||
}
|
||||
vk::BufferMemoryBarrier barriers[2] {};
|
||||
for (uint32_t index = 0; index < 2; index++) {
|
||||
barriers[index].sType = vk::StructureType::eBufferMemoryBarrier;
|
||||
barriers[index].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barriers[index].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barriers[index].buffer = infos[index].buffer;
|
||||
|
||||
@@ -125,7 +125,7 @@ private:
|
||||
void Prepare(bool tile, uint64_t tiled_capacity, uint64_t linear_capacity,
|
||||
std::span<const GpuTileInfo> infos, uint64_t source_base, uint64_t target_base,
|
||||
std::vector<Dispatch>& dispatches);
|
||||
void Record(bool tile, vk::Buffer source, uint64_t source_offset, uint64_t source_capacity,
|
||||
void Record(vk::Buffer source, uint64_t source_offset, uint64_t source_capacity,
|
||||
vk::Buffer target, uint64_t target_offset, uint64_t target_capacity,
|
||||
std::span<Dispatch> dispatches, bool clear_target);
|
||||
[[nodiscard]] vk::Pipeline GetPipeline(uint32_t slot);
|
||||
|
||||
@@ -7,12 +7,10 @@ namespace Libs::Graphics {
|
||||
|
||||
MasterSemaphore::MasterSemaphore(GraphicContext& graphics): m_graphics(graphics) {
|
||||
vk::SemaphoreTypeCreateInfo type_info {};
|
||||
type_info.sType = vk::StructureType::eSemaphoreTypeCreateInfo;
|
||||
type_info.semaphoreType = vk::SemaphoreType::eTimeline;
|
||||
type_info.initialValue = 0;
|
||||
|
||||
vk::SemaphoreCreateInfo create_info {};
|
||||
create_info.sType = vk::StructureType::eSemaphoreCreateInfo;
|
||||
create_info.pNext = &type_info;
|
||||
|
||||
const auto result = m_graphics.device.createSemaphore(&create_info, nullptr, &m_semaphore);
|
||||
@@ -47,7 +45,6 @@ void MasterSemaphore::Wait(uint64_t tick) {
|
||||
}
|
||||
|
||||
vk::SemaphoreWaitInfo wait_info {};
|
||||
wait_info.sType = vk::StructureType::eSemaphoreWaitInfo;
|
||||
wait_info.semaphoreCount = 1;
|
||||
wait_info.pSemaphores = &m_semaphore;
|
||||
wait_info.pValues = &tick;
|
||||
|
||||
@@ -1,300 +0,0 @@
|
||||
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/profiler.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
namespace {
|
||||
|
||||
using BindingKind = ShaderRecompiler::IR::DescriptorBindingKind;
|
||||
|
||||
vk::ShaderStageFlags StageFlags(DescriptorCache::Stage stage) {
|
||||
switch (stage) {
|
||||
case DescriptorCache::Stage::Vertex: return vk::ShaderStageFlagBits::eVertex;
|
||||
case DescriptorCache::Stage::Pixel: return vk::ShaderStageFlagBits::eFragment;
|
||||
case DescriptorCache::Stage::Compute: return vk::ShaderStageFlagBits::eCompute;
|
||||
default: EXIT("unknown descriptor stage\n");
|
||||
}
|
||||
}
|
||||
|
||||
bool IsSampledImage(BindingKind kind) {
|
||||
switch (kind) {
|
||||
case BindingKind::Sampled1D:
|
||||
case BindingKind::Sampled1DArray:
|
||||
case BindingKind::Sampled2D:
|
||||
case BindingKind::Sampled2DArray:
|
||||
case BindingKind::Sampled2DMsaa:
|
||||
case BindingKind::Sampled2DMsaaArray:
|
||||
case BindingKind::Sampled3D:
|
||||
case BindingKind::SampledUint1D:
|
||||
case BindingKind::SampledUint1DArray:
|
||||
case BindingKind::SampledUint2D:
|
||||
case BindingKind::SampledUint2DArray:
|
||||
case BindingKind::SampledUint2DMsaa:
|
||||
case BindingKind::SampledUint2DMsaaArray:
|
||||
case BindingKind::SampledUint3D: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsStorageImage(BindingKind kind) {
|
||||
switch (kind) {
|
||||
case BindingKind::Storage1D:
|
||||
case BindingKind::Storage1DArray:
|
||||
case BindingKind::Storage2D:
|
||||
case BindingKind::Storage2DArray:
|
||||
case BindingKind::Storage3D:
|
||||
case BindingKind::StorageUint1D:
|
||||
case BindingKind::StorageUint1DArray:
|
||||
case BindingKind::StorageUint2D:
|
||||
case BindingKind::StorageUint2DArray:
|
||||
case BindingKind::StorageUint3D: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
vk::DescriptorType DescriptorType(BindingKind kind) {
|
||||
if (kind == BindingKind::Samplers) {
|
||||
return vk::DescriptorType::eSampler;
|
||||
}
|
||||
if (IsSampledImage(kind)) {
|
||||
return vk::DescriptorType::eSampledImage;
|
||||
}
|
||||
if (IsStorageImage(kind)) {
|
||||
return vk::DescriptorType::eStorageImage;
|
||||
}
|
||||
return vk::DescriptorType::eStorageBuffer;
|
||||
}
|
||||
|
||||
uint32_t DescriptorCount(const ShaderRecompiler::IR::DescriptorBinding& binding) {
|
||||
return binding.resources.empty() ? 1u : static_cast<uint32_t>(binding.resources.size());
|
||||
}
|
||||
|
||||
std::vector<uint32_t> LayoutKey(DescriptorCache::Stage stage,
|
||||
const ShaderRecompiler::IR::Program& program) {
|
||||
std::vector<uint32_t> key;
|
||||
key.reserve(1u + program.bindings.descriptors.size() * 4u);
|
||||
key.push_back(static_cast<uint32_t>(stage));
|
||||
for (const auto& binding: program.bindings.descriptors) {
|
||||
key.push_back(static_cast<uint32_t>(binding.kind));
|
||||
key.push_back(binding.binding);
|
||||
key.push_back(DescriptorCount(binding));
|
||||
key.push_back(static_cast<uint32_t>(DescriptorType(binding.kind)));
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
vk::DescriptorBufferInfo BufferInfo(const BufferView& view) {
|
||||
EXIT_IF(view.buffer == nullptr);
|
||||
return {view.buffer, view.offset, view.range};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
vk::DescriptorImageInfo DescriptorCache::MakeImageInfo(const TextureBinding& texture) {
|
||||
EXIT_IF(!texture.image_id || texture.image_view == nullptr ||
|
||||
texture.layout == vk::ImageLayout::eUndefined);
|
||||
return {nullptr, texture.image_view, texture.layout};
|
||||
}
|
||||
|
||||
DescriptorCache::~DescriptorCache() {
|
||||
for (auto& [layout, sets]: m_free_sets_by_layout) {
|
||||
(void)layout;
|
||||
for (auto* set: sets) {
|
||||
delete set;
|
||||
}
|
||||
}
|
||||
for (const auto& [key, layout]: m_descriptor_set_layouts) {
|
||||
(void)key;
|
||||
m_graphics.device.destroyDescriptorSetLayout(layout, nullptr);
|
||||
}
|
||||
for (const auto& pool: m_pools) {
|
||||
m_graphics.device.destroyDescriptorPool(pool.pool, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
vk::DescriptorSetLayout
|
||||
DescriptorCache::GetDescriptorSetLayoutInternal(Stage stage,
|
||||
const ShaderRecompiler::IR::Program& program) {
|
||||
const auto key = LayoutKey(stage, program);
|
||||
if (const auto found = m_descriptor_set_layouts.find(key);
|
||||
found != m_descriptor_set_layouts.end()) {
|
||||
return found->second;
|
||||
}
|
||||
|
||||
std::vector<vk::DescriptorSetLayoutBinding> bindings;
|
||||
bindings.reserve(program.bindings.descriptors.size());
|
||||
for (const auto& descriptor: program.bindings.descriptors) {
|
||||
bindings.push_back({descriptor.binding, DescriptorType(descriptor.kind),
|
||||
DescriptorCount(descriptor), StageFlags(stage), nullptr});
|
||||
}
|
||||
if (bindings.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
vk::DescriptorSetLayoutCreateInfo info {};
|
||||
info.sType = vk::StructureType::eDescriptorSetLayoutCreateInfo;
|
||||
info.bindingCount = static_cast<uint32_t>(bindings.size());
|
||||
info.pBindings = bindings.data();
|
||||
vk::DescriptorSetLayout layout = nullptr;
|
||||
const auto result = m_graphics.device.createDescriptorSetLayout(&info, nullptr, &layout);
|
||||
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess || layout == nullptr);
|
||||
m_descriptor_set_layouts.emplace(key, layout);
|
||||
return layout;
|
||||
}
|
||||
|
||||
void DescriptorCache::CreatePool() {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
constexpr uint32_t MaxSets = 512;
|
||||
const vk::DescriptorPoolSize sizes[] = {
|
||||
{vk::DescriptorType::eStorageBuffer,
|
||||
MaxSets * (ShaderRecompiler::IR::ShaderInfo::MaxBuffers +
|
||||
ShaderRecompiler::IR::ShaderInfo::MaxAddresses + 3u)},
|
||||
{vk::DescriptorType::eSampledImage, MaxSets * ShaderRecompiler::IR::ShaderInfo::MaxImages},
|
||||
{vk::DescriptorType::eStorageImage, MaxSets * ShaderRecompiler::IR::ShaderInfo::MaxImages},
|
||||
{vk::DescriptorType::eSampler, MaxSets * ShaderRecompiler::IR::ShaderInfo::MaxSamplers},
|
||||
};
|
||||
vk::DescriptorPoolCreateInfo info {};
|
||||
info.sType = vk::StructureType::eDescriptorPoolCreateInfo;
|
||||
info.poolSizeCount = static_cast<uint32_t>(std::size(sizes));
|
||||
info.pPoolSizes = sizes;
|
||||
info.maxSets = MaxSets;
|
||||
const auto pool_id = static_cast<int>(m_pools.size());
|
||||
auto& pool = m_pools.emplace_back();
|
||||
const auto result = m_graphics.device.createDescriptorPool(&info, nullptr, &pool.pool);
|
||||
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess || pool.pool == nullptr);
|
||||
pool.next_free_pool = m_first_free_pool;
|
||||
m_first_free_pool = pool_id;
|
||||
}
|
||||
|
||||
VulkanDescriptorSet* DescriptorCache::Allocate(Stage stage,
|
||||
const ShaderRecompiler::IR::Program& program) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
Common::LockGuard lock(m_mutex);
|
||||
const auto layout = GetDescriptorSetLayoutInternal(stage, program);
|
||||
EXIT_IF(layout == nullptr);
|
||||
auto& free_sets = m_free_sets_by_layout[layout];
|
||||
if (!free_sets.empty()) {
|
||||
auto* result = free_sets.back();
|
||||
free_sets.pop_back();
|
||||
return result;
|
||||
}
|
||||
|
||||
constexpr uint32_t Batch = 32;
|
||||
for (int attempt = 0; attempt < 2; attempt++) {
|
||||
for (int pool_id = m_first_free_pool; pool_id != -1;
|
||||
pool_id = m_pools[pool_id].next_free_pool) {
|
||||
auto& pool = m_pools[pool_id];
|
||||
std::array<vk::DescriptorSetLayout, Batch> layouts;
|
||||
layouts.fill(layout);
|
||||
std::array<vk::DescriptorSet, Batch> sets {};
|
||||
vk::DescriptorSetAllocateInfo info {};
|
||||
info.sType = vk::StructureType::eDescriptorSetAllocateInfo;
|
||||
info.descriptorPool = pool.pool;
|
||||
info.descriptorSetCount = Batch;
|
||||
info.pSetLayouts = layouts.data();
|
||||
if (m_graphics.device.allocateDescriptorSets(&info, sets.data()) ==
|
||||
vk::Result::eSuccess) {
|
||||
free_sets.reserve(free_sets.size() + Batch - 1u);
|
||||
for (uint32_t i = 0; i + 1u < Batch; i++) {
|
||||
free_sets.push_back(new VulkanDescriptorSet {sets[i], layout, pool_id});
|
||||
}
|
||||
return new VulkanDescriptorSet {sets.back(), layout, pool_id};
|
||||
}
|
||||
m_first_free_pool = pool.next_free_pool;
|
||||
break;
|
||||
}
|
||||
CreatePool();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DescriptorCache::Recycle(VulkanDescriptorSet& set) {
|
||||
EXIT_IF(set.set == nullptr || set.layout == nullptr);
|
||||
Common::LockGuard lock(m_mutex);
|
||||
m_free_sets_by_layout[set.layout].push_back(&set);
|
||||
}
|
||||
|
||||
VulkanDescriptorSet& DescriptorCache::GetDescriptor(Stage stage,
|
||||
const ShaderRecompiler::IR::Program& program,
|
||||
const NativeDescriptors& data) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
EXIT_IF(data.buffers.size() != program.info.buffers.size() ||
|
||||
data.images.size() != program.info.images.size() ||
|
||||
data.samplers.size() != program.info.samplers.size() ||
|
||||
data.addresses.size() != program.info.addresses.size());
|
||||
auto* set = Allocate(stage, program);
|
||||
EXIT_NOT_IMPLEMENTED(set == nullptr);
|
||||
|
||||
const auto descriptor_count = program.info.buffers.size() + program.info.images.size() +
|
||||
program.info.samplers.size() + program.info.addresses.size() + 3u;
|
||||
std::vector<vk::DescriptorBufferInfo> buffer_infos;
|
||||
std::vector<vk::DescriptorImageInfo> image_infos;
|
||||
std::vector<vk::WriteDescriptorSet> writes;
|
||||
buffer_infos.reserve(descriptor_count);
|
||||
image_infos.reserve(descriptor_count);
|
||||
writes.reserve(program.bindings.descriptors.size());
|
||||
|
||||
for (const auto& binding: program.bindings.descriptors) {
|
||||
vk::WriteDescriptorSet write {};
|
||||
write.sType = vk::StructureType::eWriteDescriptorSet;
|
||||
write.dstSet = set->set;
|
||||
write.dstBinding = binding.binding;
|
||||
write.descriptorType = DescriptorType(binding.kind);
|
||||
write.descriptorCount = DescriptorCount(binding);
|
||||
const auto buffer_start = buffer_infos.size();
|
||||
const auto image_start = image_infos.size();
|
||||
switch (binding.kind) {
|
||||
case BindingKind::Buffers:
|
||||
for (const auto resource: binding.resources) {
|
||||
buffer_infos.push_back(BufferInfo(data.buffers.at(resource)));
|
||||
}
|
||||
break;
|
||||
case BindingKind::AddressMemory:
|
||||
for (const auto resource: binding.resources) {
|
||||
buffer_infos.push_back(BufferInfo(data.addresses.at(resource)));
|
||||
}
|
||||
break;
|
||||
case BindingKind::FlattenedSrt:
|
||||
buffer_infos.push_back(BufferInfo(data.flattened_srt));
|
||||
break;
|
||||
case BindingKind::UserData: buffer_infos.push_back(BufferInfo(data.user_data)); break;
|
||||
case BindingKind::Gds: buffer_infos.push_back(BufferInfo(data.gds)); break;
|
||||
case BindingKind::Samplers:
|
||||
for (const auto resource: binding.resources) {
|
||||
const auto sampler = data.samplers.at(resource);
|
||||
EXIT_IF(sampler == nullptr);
|
||||
image_infos.push_back({sampler, nullptr, vk::ImageLayout::eUndefined});
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
for (const auto resource: binding.resources) {
|
||||
const auto& texture = data.images.at(resource);
|
||||
image_infos.push_back(MakeImageInfo(texture));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (buffer_infos.size() != buffer_start) {
|
||||
write.pBufferInfo = buffer_infos.data() + buffer_start;
|
||||
}
|
||||
if (image_infos.size() != image_start) {
|
||||
write.pImageInfo = image_infos.data() + image_start;
|
||||
}
|
||||
writes.push_back(write);
|
||||
}
|
||||
m_graphics.device.updateDescriptorSets(static_cast<uint32_t>(writes.size()), writes.data(), 0,
|
||||
nullptr);
|
||||
return *set;
|
||||
}
|
||||
|
||||
vk::DescriptorSetLayout
|
||||
DescriptorCache::GetDescriptorSetLayout(Stage stage, const ShaderRecompiler::IR::Program& program) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
return GetDescriptorSetLayoutInternal(stage, program);
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
Loaded 100 of 398 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user