Compare commits
740
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65f2df7776 | ||
|
|
b89a0173ef | ||
|
|
1fec588fb1 | ||
|
|
4bed88289f | ||
|
|
de1edbdf25 | ||
|
|
d468303bc9 | ||
|
|
7a6138597e | ||
|
|
28da328815 | ||
|
|
de3c4b6d56 | ||
|
|
5d27240e0c | ||
|
|
94fc37b8c2 | ||
|
|
532cb28ce9 | ||
|
|
b30d12aca5 | ||
|
|
d891a6927f | ||
|
|
22b5715837 | ||
|
|
7eb08f11ea | ||
|
|
588a05f77c | ||
|
|
81658541a1 | ||
|
|
d0eb56ff20 | ||
|
|
3ec2b47df7 | ||
|
|
1c55ad49ee | ||
|
|
348e61fd76 | ||
|
|
8ce6d54930 | ||
|
|
8db5e700d7 | ||
|
|
0ec3596440 | ||
|
|
ee4c03d0f1 | ||
|
|
85589a7d16 | ||
|
|
d476c7fa1b | ||
|
|
f12e7f9fbf | ||
|
|
3ed008c0b6 | ||
|
|
66ec81187d | ||
|
|
0efd94a769 | ||
|
|
1801def1ee | ||
|
|
61155644d5 | ||
|
|
b3ec8b188e | ||
|
|
dbf0cde330 | ||
|
|
072562c571 | ||
|
|
780799f30b | ||
|
|
cf0c1f74dc | ||
|
|
d3ca32efbe | ||
|
|
de6df6dc0e | ||
|
|
6dd85c6530 | ||
|
|
5ca7f41513 | ||
|
|
4ad07e25d4 | ||
|
|
89a191c2e9 | ||
|
|
f63296fc18 | ||
|
|
ca27bb58bf | ||
|
|
86d2e11801 | ||
|
|
cd528e57ab | ||
|
|
4a465450f1 | ||
|
|
4e648b224f | ||
|
|
04c7f84c6f | ||
|
|
8c31f46683 | ||
|
|
563c32b95b | ||
|
|
ab0063bc6b | ||
|
|
af48bc3c74 | ||
|
|
65dfac9637 | ||
|
|
9e119937af | ||
|
|
3e644b25f0 | ||
|
|
fadd5eb953 | ||
|
|
b48b6136ba | ||
|
|
ca0f98c513 | ||
|
|
70b87510a2 | ||
|
|
31b836282d | ||
|
|
cdd9c4aebe | ||
|
|
f3a61f100c | ||
|
|
0774ce6b5e | ||
|
|
60db518b75 | ||
|
|
3c5316f1e9 | ||
|
|
1f33a6efd2 | ||
|
|
4127f619e1 | ||
|
|
b3cc4d11b8 | ||
|
|
8523f4e970 | ||
|
|
6b7450dc0b | ||
|
|
7b384fb5e8 | ||
|
|
20dc6d479b | ||
|
|
0736f3c3b3 | ||
|
|
6a7767f4e4 | ||
|
|
628d387cbd | ||
|
|
75a1c6d7a8 | ||
|
|
8c76370e2d | ||
|
|
bdc15a5c2d | ||
|
|
acfe9298d9 | ||
|
|
57f6e5371b | ||
|
|
9fa3cbfcde | ||
|
|
c81ece5c3c | ||
|
|
aae48c1121 | ||
|
|
918ce935b8 | ||
|
|
6f37e07dba | ||
|
|
171b3279e0 | ||
|
|
b3a1cfe7c9 | ||
|
|
d273838e07 | ||
|
|
a1de8c924d | ||
|
|
72765a5b0a | ||
|
|
e1beabaa98 | ||
|
|
ebaee14b8b | ||
|
|
d4f08dafbb | ||
|
|
753fb102c4 | ||
|
|
b219113a55 | ||
|
|
9140d44b29 | ||
|
|
8b0d65768a | ||
|
|
349bf7be38 | ||
|
|
a01d282f14 | ||
|
|
3c3296a114 | ||
|
|
61501ba122 | ||
|
|
99ba40e965 | ||
|
|
4116af7971 | ||
|
|
2fab93faff | ||
|
|
b92c5cab07 | ||
|
|
8ed9678cbe | ||
|
|
731e52a3c1 | ||
|
|
687b6da800 | ||
|
|
64f4c097ac | ||
|
|
0191e1fe46 | ||
|
|
21093067db | ||
|
|
1b10e7d4f3 | ||
|
|
5b2d3ac225 | ||
|
|
422dfcc1e6 | ||
|
|
0d304393f4 | ||
|
|
57aa99e964 | ||
|
|
cfc133fcf1 | ||
|
|
8eade6be1f | ||
|
|
2250664e58 | ||
|
|
8a27cc8b7f | ||
|
|
d786d59ea1 | ||
|
|
79116905aa | ||
|
|
763950ec18 | ||
|
|
13571868de | ||
|
|
0f2c89b140 | ||
|
|
f8b70e8ec5 | ||
|
|
7abdea5a2e | ||
|
|
cb3e808e8d | ||
|
|
b2e0b97bad | ||
|
|
9c0cbed214 | ||
|
|
c531a9c077 | ||
|
|
2a1f762e23 | ||
|
|
4dd98e63e4 | ||
|
|
1b7187f427 | ||
|
|
0dddd94eab | ||
|
|
2ef0244266 | ||
|
|
0774bf9501 | ||
|
|
8f5b2fd230 | ||
|
|
274191036f | ||
|
|
13afbc2876 | ||
|
|
a7bb0a60fb | ||
|
|
0650b13358 | ||
|
|
6cd851e77e | ||
|
|
87a8cb3872 | ||
|
|
a4869eaf19 | ||
|
|
1740220f6a | ||
|
|
5deccffdce | ||
|
|
a76eacba67 | ||
|
|
ee31ca6fc0 | ||
|
|
7dd6686087 | ||
|
|
fdffd93949 | ||
|
|
f0a0a9b2e1 | ||
|
|
e743e9243f | ||
|
|
8e13d53b1e | ||
|
|
ff274c8387 | ||
|
|
e08e1c209b | ||
|
|
de310ed9ad | ||
|
|
d01b29fa01 | ||
|
|
8cba3d4366 | ||
|
|
be1e51ad91 | ||
|
|
29ce1cad84 | ||
|
|
6a967ad007 | ||
|
|
fe2bb3d9a4 | ||
|
|
cd7973bdcf | ||
|
|
e6384e0e92 | ||
|
|
e63dc17f54 | ||
|
|
af338cf2ec | ||
|
|
f1d8a04337 | ||
|
|
b13497e2a0 | ||
|
|
175c9f9966 | ||
|
|
1a50420b46 | ||
|
|
bbad75a012 | ||
|
|
25c08f19e3 | ||
|
|
59063fb7b4 | ||
|
|
71f60de0ad | ||
|
|
743661109d | ||
|
|
5014cbb023 | ||
|
|
4142e7a1cf | ||
|
|
a721e648b7 | ||
|
|
358e89ef08 | ||
|
|
e023e90e7a | ||
|
|
a5b5b0c3a7 | ||
|
|
b7b4d3c295 | ||
|
|
a445c4e156 | ||
|
|
1432c59499 | ||
|
|
4c7c10fad0 | ||
|
|
7c8a0d1967 | ||
|
|
11fbacea20 | ||
|
|
8cd70454c3 | ||
|
|
1d988182fc | ||
|
|
dba49f00df | ||
|
|
89c0b44da9 | ||
|
|
4f450fd5c4 | ||
|
|
f0943cfca2 | ||
|
|
9aa52b79d4 | ||
|
|
a82125dbbd | ||
|
|
24fcb299b5 | ||
|
|
753aa9ca61 | ||
|
|
a33eada7ec | ||
|
|
676a2be874 | ||
|
|
c95d466cdd | ||
|
|
3d8ac384ad | ||
|
|
57424cc7e9 | ||
|
|
78fe4ba547 | ||
|
|
a3d25cd22b | ||
|
|
56bf841bac | ||
|
|
dfdf0db794 | ||
|
|
bf6bd4eca8 | ||
|
|
36c69dda9c | ||
|
|
f14fdc11f5 | ||
|
|
0157487b1f | ||
|
|
1716c856c3 | ||
|
|
31521c70c2 | ||
|
|
8cac90d81e | ||
|
|
388b4f5efb | ||
|
|
f9b5a7f301 | ||
|
|
00c78cc225 | ||
|
|
2bdb8b560c | ||
|
|
a974fddd4c | ||
|
|
83a3e243da | ||
|
|
f286e308bf | ||
|
|
29af3be2e3 | ||
|
|
6623dcbebf | ||
|
|
89dc43f361 | ||
|
|
b77aac43ea | ||
|
|
0d63fa02ee | ||
|
|
e187bc442d | ||
|
|
d62268fbfb | ||
|
|
3b73e3de60 | ||
|
|
7685736d6c | ||
|
|
b8a3a7f221 | ||
|
|
732628acf5 | ||
|
|
7bd00d2b30 | ||
|
|
0759797e61 | ||
|
|
fddc5160c7 | ||
|
|
2a579c460f | ||
|
|
efdc801cff | ||
|
|
dd654c575a | ||
|
|
0c2599b6ca | ||
|
|
979d6742e0 | ||
|
|
4a7a033d04 | ||
|
|
5c2bbcb4a1 | ||
|
|
dec903a0ee | ||
|
|
e9475b322b | ||
|
|
d9aead8aac | ||
|
|
ce148ebeb1 | ||
|
|
e1315fbaa7 | ||
|
|
1690549392 | ||
|
|
c73710e343 | ||
|
|
092a0489da | ||
|
|
1c6279366f | ||
|
|
2ac85049a9 | ||
|
|
ea428a27d1 | ||
|
|
3bef718238 | ||
|
|
e9daaa0503 | ||
|
|
6d3ea21993 | ||
|
|
f39127605d | ||
|
|
db476f00f7 | ||
|
|
e54d8ec0a4 | ||
|
|
b95ac854b4 | ||
|
|
96443f6dac | ||
|
|
e5e3dff9fa | ||
|
|
31d9da38fd | ||
|
|
c5a59bb6b1 | ||
|
|
e6854148d7 | ||
|
|
001daf0350 | ||
|
|
d26c48110d | ||
|
|
15a070f0c9 | ||
|
|
93230b06cf | ||
|
|
4c55fc3c48 | ||
|
|
a4797a631e | ||
|
|
5868e857aa | ||
|
|
32ba063bd7 | ||
|
|
02f0839f9b | ||
|
|
e50f3c4c91 | ||
|
|
7ca15bfc16 | ||
|
|
b7b7afb62a | ||
|
|
b8ff6a9ad9 | ||
|
|
a1dcb4f259 | ||
|
|
c3501393fd | ||
|
|
a03de75a4e | ||
|
|
0c3d59aba3 | ||
|
|
ce1987f291 | ||
|
|
7eae926ed7 | ||
|
|
606af52eda | ||
|
|
8ea30aea80 | ||
|
|
72dea695ba | ||
|
|
1c0cf56ed0 | ||
|
|
bcb4eb35cd | ||
|
|
bce2676335 | ||
|
|
510097d722 | ||
|
|
5784118a12 | ||
|
|
b5de0f2d23 | ||
|
|
04df3f4dad | ||
|
|
093ca4d623 | ||
|
|
a3ed1558d3 | ||
|
|
fe379070ae | ||
|
|
ead0a2f597 | ||
|
|
5dbc593f90 | ||
|
|
d47dc12bf0 | ||
|
|
4392f63e57 | ||
|
|
f64b7aca79 | ||
|
|
3762ec5750 | ||
|
|
4591fdd9c0 | ||
|
|
d3068d2a0e | ||
|
|
ca0a4bc23a | ||
|
|
433c163c61 | ||
|
|
c867fda883 | ||
|
|
566540ae6d | ||
|
|
759f490dae | ||
|
|
867d4d44f5 | ||
|
|
65747d5877 | ||
|
|
bd357f6076 | ||
|
|
9e960b7d45 | ||
|
|
0c54d4a6a1 | ||
|
|
526e846a81 | ||
|
|
8bcfa373d4 | ||
|
|
db9a0f2c1f | ||
|
|
646b1e2193 | ||
|
|
41bd30f81d | ||
|
|
bd626aea27 | ||
|
|
8a03f75485 | ||
|
|
dc9594fbdf | ||
|
|
fad0b533c0 | ||
|
|
4d773e9453 | ||
|
|
e0c610b013 | ||
|
|
0da4f36c33 | ||
|
|
11d17e1db3 | ||
|
|
b8c318d923 | ||
|
|
e563cc6f5e | ||
|
|
cb8914ab96 | ||
|
|
2fecda7bdc | ||
|
|
543d8efb25 | ||
|
|
396b1079a7 | ||
|
|
eedcd9f8cb | ||
|
|
ab252b210c | ||
|
|
1071b8ea90 | ||
|
|
a9c5bbf185 | ||
|
|
8dd4bff7a7 | ||
|
|
77c54446af | ||
|
|
4a0bc0af57 | ||
|
|
a171e75e70 | ||
|
|
f5ffa092fe | ||
|
|
8de5fdd551 | ||
|
|
ecca3abe98 | ||
|
|
8570e35224 | ||
|
|
84df9f9a03 | ||
|
|
5f93fb9613 | ||
|
|
75f77fdbdd | ||
|
|
7825d582c2 | ||
|
|
5deb0ba933 | ||
|
|
18e917aaf2 | ||
|
|
a165f3890a | ||
|
|
f88b6d9c97 | ||
|
|
db8ecd9952 | ||
|
|
b5ec06c2d2 | ||
|
|
dfbac8385e | ||
|
|
eaab737b2c | ||
|
|
45991cda0a | ||
|
|
09a016bb6f | ||
|
|
31607d355d | ||
|
|
a67d50b9e2 | ||
|
|
8d42f53c80 | ||
|
|
5e21a55bce | ||
|
|
85930ea2e8 | ||
|
|
4b3cde8b39 | ||
|
|
eedfa85355 | ||
|
|
bb54c6c807 | ||
|
|
c23f905858 | ||
|
|
8c7be0a1d0 | ||
|
|
878497b3d5 | ||
|
|
a155561769 | ||
|
|
8b86a0ed2e | ||
|
|
26f9a9b122 | ||
|
|
6e5f6978fb | ||
|
|
0d31d7b2de | ||
|
|
dd862b7e0a | ||
|
|
52065f2cbd | ||
|
|
77c02d5831 | ||
|
|
f7e4c8d16d | ||
|
|
b7410474ff | ||
|
|
384cc4ebf6 | ||
|
|
7c0643f122 | ||
|
|
58abae3067 | ||
|
|
4742693869 | ||
|
|
6c39e474ea | ||
|
|
5c8977c906 | ||
|
|
6b3cc2e2f3 | ||
|
|
76b2edd6ea | ||
|
|
8ebb37d011 | ||
|
|
3366db83ef | ||
|
|
5415254aac | ||
|
|
caacc61602 | ||
|
|
e902df19b2 | ||
|
|
40b372dda7 | ||
|
|
fd330a98aa | ||
|
|
90c23a7d5d | ||
|
|
5710304114 | ||
|
|
0f684820bc | ||
|
|
e1d2d6ade6 | ||
|
|
e0b6f2ca71 | ||
|
|
2a35ee279b | ||
|
|
5abf780360 | ||
|
|
181f158ea9 | ||
|
|
f4087fc81d | ||
|
|
82793f9b82 | ||
|
|
537576e84a | ||
|
|
375d1f55e3 | ||
|
|
597218963f | ||
|
|
0d29b3ddc8 | ||
|
|
e0e3b42623 | ||
|
|
0995ca6614 | ||
|
|
3d741fc8d4 | ||
|
|
c20772b458 | ||
|
|
4a6b659098 | ||
|
|
2b93f08ca5 | ||
|
|
fca62b8fff | ||
|
|
dee6d9de2c | ||
|
|
574e3e7521 | ||
|
|
29fdf82ac5 | ||
|
|
c8cf2a54e8 | ||
|
|
bfd32e67d4 | ||
|
|
95915367fa | ||
|
|
b769cfda62 | ||
|
|
40ac88dfed | ||
|
|
6d96082f07 | ||
|
|
21f913db1c | ||
|
|
699a578966 | ||
|
|
babbfa1394 | ||
|
|
73f0d58e10 | ||
|
|
9cb4b74bbd | ||
|
|
f2c8456971 | ||
|
|
e571600c30 | ||
|
|
e84c94d3eb | ||
|
|
e791d4818f | ||
|
|
cdadb43e50 | ||
|
|
edaff60fbf | ||
|
|
3f64e46557 | ||
|
|
32dab23bc6 | ||
|
|
362de7dd31 | ||
|
|
6b01fbb903 | ||
|
|
4934d9f7bc | ||
|
|
ed73955b00 | ||
|
|
3f1f2726c4 | ||
|
|
1a7bd7ea8d | ||
|
|
946ea43436 | ||
|
|
7f42385f5f | ||
|
|
8845ac2b75 | ||
|
|
da85001d4d | ||
|
|
8d15079937 | ||
|
|
adc542b5cd | ||
|
|
72feb044a3 | ||
|
|
c0ba260ddf | ||
|
|
2da215e8e7 | ||
|
|
4d0368b5f1 | ||
|
|
d11c78857d | ||
|
|
e829d90c4a | ||
|
|
a8006acd33 | ||
|
|
86ec502040 | ||
|
|
b19a73cc50 | ||
|
|
a99c695932 | ||
|
|
8cd720b53a | ||
|
|
04df79e58f | ||
|
|
a4b3717762 | ||
|
|
1035e442ee | ||
|
|
60d3638f15 | ||
|
|
3e7d3c9035 | ||
|
|
eb360be2ad | ||
|
|
485ac8704d | ||
|
|
dc74540423 | ||
|
|
96219c22db | ||
|
|
f3b7048775 | ||
|
|
aaed6fb376 | ||
|
|
35a5d3310f | ||
|
|
9da79a1d2d | ||
|
|
595b355d1b | ||
|
|
3a67d769f4 | ||
|
|
6fb536a937 | ||
|
|
bb460d78d8 | ||
|
|
838835a715 | ||
|
|
63acd53e23 | ||
|
|
fcd2d0457b | ||
|
|
58fc26461b | ||
|
|
fd82f6b04e | ||
|
|
cef3200533 | ||
|
|
634500c398 | ||
|
|
b3e294ac54 | ||
|
|
2bf7535ad0 | ||
|
|
3315f75de9 | ||
|
|
bbe7dbfa92 | ||
|
|
5697922a65 | ||
|
|
4da83df2b9 | ||
|
|
8f156d6588 | ||
|
|
cec0871c11 | ||
|
|
41a809368b | ||
|
|
ea53eb9290 | ||
|
|
05875057c9 | ||
|
|
44125926d1 | ||
|
|
60734349de | ||
|
|
fbfd5ec0ec | ||
|
|
a74a95cc65 | ||
|
|
bc0ea16ff0 | ||
|
|
b2dc8aa84c | ||
|
|
9987e8b6ab | ||
|
|
0d9bdcc008 | ||
|
|
b5c634045e | ||
|
|
1f05531d53 | ||
|
|
88f382f0e3 | ||
|
|
3e59925900 | ||
|
|
ea404f8d4f | ||
|
|
602a550d93 | ||
|
|
12abbe2d23 | ||
|
|
fb0ee97588 | ||
|
|
56ef48c9c3 | ||
|
|
47c3dd3dd1 | ||
|
|
c181648bfa | ||
|
|
0cf78b3abe | ||
|
|
22889a7ad9 | ||
|
|
a14451d0ac | ||
|
|
5dfdab10b7 | ||
|
|
d6f2e3b8e9 | ||
|
|
df30517743 | ||
|
|
8f80643c1a | ||
|
|
5aea9be2fb | ||
|
|
ad02e483d0 | ||
|
|
f463d53036 | ||
|
|
b8d4408524 | ||
|
|
fef70be848 | ||
|
|
bdb12d9b24 | ||
|
|
43ad283a83 | ||
|
|
2e4936afc4 | ||
|
|
891ffabd11 | ||
|
|
e2c19498b4 | ||
|
|
c32630b265 | ||
|
|
bf21e78d02 | ||
|
|
525d4e08a3 | ||
|
|
2e9bf6d694 | ||
|
|
e845f01d85 | ||
|
|
bef48be7b4 | ||
|
|
b54fdc9e6e | ||
|
|
cedbf2e30b | ||
|
|
592f8d1b0d | ||
|
|
29612a684e | ||
|
|
e6d5807399 | ||
|
|
fa2553251f | ||
|
|
7387718852 | ||
|
|
a503a6209a | ||
|
|
ce3e5f74e8 | ||
|
|
f37112358e | ||
|
|
f368b14621 | ||
|
|
6960b1148a | ||
|
|
3cc23aac25 | ||
|
|
11dc8740f2 | ||
|
|
4b797cff88 | ||
|
|
fe1c1736cd | ||
|
|
4058ef5f09 | ||
|
|
d25ca01624 | ||
|
|
d96f87dbbf | ||
|
|
8a2e31023f | ||
|
|
1ea8e171d9 | ||
|
|
e2be3dd0ac | ||
|
|
1c51164e7b | ||
|
|
f190f03530 | ||
|
|
055fc7cbc1 | ||
|
|
9073fc3dc3 | ||
|
|
2409dc9bc4 | ||
|
|
6586c8d70b | ||
|
|
ac0c94da69 | ||
|
|
d19d6a72b0 | ||
|
|
c81b5d98ef | ||
|
|
756866675f | ||
|
|
ebcd4925f7 | ||
|
|
13bed4fdf9 | ||
|
|
1dae5c6b6c | ||
|
|
8e6663e4b0 | ||
|
|
ba804444b8 | ||
|
|
58cfb85004 | ||
|
|
ab46481b45 | ||
|
|
4296782399 | ||
|
|
ef375a7103 | ||
|
|
fd258b7765 | ||
|
|
147de8d372 | ||
|
|
eb2a1928b6 | ||
|
|
35b033102f | ||
|
|
7bc65421a9 | ||
|
|
736514cf37 | ||
|
|
db0158dae8 | ||
|
|
e706695ed1 | ||
|
|
e8877ffe2d | ||
|
|
1fd5d9dae6 | ||
|
|
cd48089318 | ||
|
|
6379ab22c9 | ||
|
|
0bf02b75d5 | ||
|
|
c4259b5598 | ||
|
|
6b3c1179bc | ||
|
|
c1a0e61e8e | ||
|
|
fc06298ed4 | ||
|
|
4ca87b188c | ||
|
|
f1f60c3e0d | ||
|
|
76d21b56af | ||
|
|
0ab0e50a4f | ||
|
|
34f4c06a5c | ||
|
|
6de36f1e53 | ||
|
|
2a9a3b1ac2 | ||
|
|
84b73a3770 | ||
|
|
662a10e273 | ||
|
|
ecce02502e | ||
|
|
d17875aea1 | ||
|
|
b8897a68f9 | ||
|
|
84efd4871e | ||
|
|
85ea5a6b70 | ||
|
|
77891acb92 | ||
|
|
74fe102035 | ||
|
|
25cc554925 | ||
|
|
d787a521b5 | ||
|
|
46e52c71e1 | ||
|
|
1dad27a172 | ||
|
|
60d230b380 | ||
|
|
d7cb38e706 | ||
|
|
ce00cca6ee | ||
|
|
d627d57bad | ||
|
|
8ffc776f1c | ||
|
|
be032b52c1 | ||
|
|
4388e81e5f | ||
|
|
71a185d139 | ||
|
|
d2cf5985ac | ||
|
|
debcbb8e5c | ||
|
|
b9dd62c7d3 | ||
|
|
dc2b430f34 | ||
|
|
e5ef4e8868 | ||
|
|
c0d6cd3ac3 | ||
|
|
b63ab2dc19 | ||
|
|
5d8dad561c | ||
|
|
8933be1ae2 | ||
|
|
6b66b48b1d | ||
|
|
9c23eb6e33 | ||
|
|
baea54a3fc | ||
|
|
d9f800454c | ||
|
|
f9ee0de07a | ||
|
|
2786d0a9f7 | ||
|
|
491e8032e6 | ||
|
|
ef3f13f5d3 | ||
|
|
bcb5b2d790 | ||
|
|
02de3f2e2a | ||
|
|
0e7bb53c07 | ||
|
|
759109d478 | ||
|
|
54d5af6edf | ||
|
|
38fbe47ced | ||
|
|
f066c925ba | ||
|
|
994fdf4e1d | ||
|
|
50b50d65e3 | ||
|
|
0aaa985649 | ||
|
|
29564f8eae | ||
|
|
c15db68a5b | ||
|
|
3452fb3e56 | ||
|
|
35eb8e7be1 | ||
|
|
834b774128 | ||
|
|
5aa0eb9f9d | ||
|
|
d9a6e2e649 | ||
|
|
cb823b16a1 | ||
|
|
0bd41e877e | ||
|
|
ecc3e73967 | ||
|
|
464b4c24f9 | ||
|
|
32367516e8 | ||
|
|
1709a55606 | ||
|
|
58b4455979 | ||
|
|
ea1dede19c | ||
|
|
20ea3381fa | ||
|
|
7aa6fccd7c | ||
|
|
adbd54f4f8 | ||
|
|
9d54261f18 | ||
|
|
a97757c9ae | ||
|
|
7c79d9f89d | ||
|
|
bf0914f813 | ||
|
|
b96bc30fbd | ||
|
|
62b50eb8ba | ||
|
|
b4932e384a | ||
|
|
5e68dc5f8d | ||
|
|
f222f1b925 | ||
|
|
22e4a54782 | ||
|
|
1289922c5f | ||
|
|
2839c352b8 | ||
|
|
6a01cbc312 | ||
|
|
6193156556 | ||
|
|
fe23aa917d | ||
|
|
eb8a29a332 | ||
|
|
0626902530 | ||
|
|
042bfe2597 | ||
|
|
97133f3591 | ||
|
|
d06cc4390e | ||
|
|
6047d3235f | ||
|
|
2cda6e35bd | ||
|
|
8f8d51e17b | ||
|
|
6919e3b274 | ||
|
|
10bf944410 | ||
|
|
9a2f6fdb53 | ||
|
|
761977d385 | ||
|
|
21248f15b5 | ||
|
|
4f32817f6d | ||
|
|
cc9e05e711 | ||
|
|
419d68d4db | ||
|
|
8450232448 | ||
|
|
cc51726590 | ||
|
|
318e22af51 | ||
|
|
68ac87dc24 | ||
|
|
acb8f00075 | ||
|
|
06d9183aaa | ||
|
|
75af25419d | ||
|
|
f6b90d2a31 | ||
|
|
a3822f4af0 | ||
|
|
bcdad769ff | ||
|
|
be4fb4fdbb | ||
|
|
65394f6301 | ||
|
|
b0beee03bc | ||
|
|
fe1de41b8e | ||
|
|
a37b431e87 | ||
|
|
98107016b9 | ||
|
|
8bccfc2863 | ||
|
|
f54a0a3452 | ||
|
|
6778ab0624 | ||
|
|
269d636785 | ||
|
|
39862c91ce | ||
|
|
523f4026b4 | ||
|
|
a6bf162431 | ||
|
|
826e8d181c | ||
|
|
16dfadbba0 | ||
|
|
5cbb97551f | ||
|
|
defee767c3 | ||
|
|
9560318521 | ||
|
|
ef09feb048 | ||
|
|
39f323fe09 | ||
|
|
11b95304ea | ||
|
|
b7c30a7916 | ||
|
|
4cae48fc77 | ||
|
|
d1a93f0557 | ||
|
|
b93059fad7 |
@@ -7,5 +7,3 @@ for next branch cut* header.
|
||||
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
||||
|
||||
## Release notes for next branch cut
|
||||
|
||||
- engine: Add experimental APIs `Engine::builder::paused()` and `Engine::setPaused()`
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.51.0'
|
||||
implementation 'com.google.android.filament:filament-android:1.51.4'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
||||
iOS projects can use CocoaPods to install the latest release:
|
||||
|
||||
```shell
|
||||
pod 'Filament', '~> 1.51.0'
|
||||
pod 'Filament', '~> 1.51.4'
|
||||
```
|
||||
|
||||
### Snapshots
|
||||
|
||||
@@ -7,6 +7,16 @@ A new header is inserted each time a *tag* is created.
|
||||
Instead, if you are authoring a PR for the main branch, add your release note to
|
||||
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
|
||||
|
||||
## v1.51.4
|
||||
|
||||
|
||||
## v1.51.3
|
||||
|
||||
|
||||
## v1.51.2
|
||||
|
||||
- engine: Add experimental APIs `Engine::builder::paused()` and `Engine::setPaused()`
|
||||
|
||||
## v1.51.1
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.51.0
|
||||
VERSION_NAME=1.51.4
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
+32
-1
@@ -1397,7 +1397,7 @@ Type
|
||||
: `string`
|
||||
|
||||
Value
|
||||
: Any of `opaque`, `transparent`, `fade`, `add`, `masked`, `multiply`, `screen`. Defaults to `opaque`.
|
||||
: Any of `opaque`, `transparent`, `fade`, `add`, `masked`, `multiply`, `screen`, `custom`. Defaults to `opaque`.
|
||||
|
||||
Description
|
||||
: Defines how/if the rendered object is blended with the content of the render target.
|
||||
@@ -1420,6 +1420,7 @@ Description
|
||||
of the material's output defines whether a fragment is discarded or not. Additionally,
|
||||
ALPHA_TO_COVERAGE is enabled for non-translucent views. See the maskThreshold section for more
|
||||
information.
|
||||
- **Custom**: blending is enabled. But the blending function is user specified. See `blendFunction`.
|
||||
|
||||
!!! Note
|
||||
When `blending` is set to `masked`, alpha to coverage is automatically enabled for the material.
|
||||
@@ -1432,6 +1433,36 @@ material {
|
||||
}
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
### Blending and transparency: blendFunction
|
||||
|
||||
Type
|
||||
: `object`
|
||||
|
||||
Fields
|
||||
: `srcRGB`, `srcA`, `dstRGB`, `dstA`
|
||||
|
||||
Description
|
||||
: - *srcRGB*: source function applied to the RGB channels
|
||||
- *srcA*: source function applied to the alpha channel
|
||||
- *srcRGB*: destination function applied to the RGB channels
|
||||
- *srcRGB*: destination function applied to the alpha channel
|
||||
The values possible for each functions are one of `zero`, `one`, `srcColor`, `oneMinusSrcColor`,
|
||||
`dstColor`, `oneMinusDstColor`, `srcAlpha`, `oneMinusSrcAlpha`, `dstAlpha`,
|
||||
`oneMinusDstAlpha`, `srcAlphaSaturate`
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
|
||||
material {
|
||||
blending : custom,
|
||||
blendFunction :
|
||||
{
|
||||
srcRGB: one,
|
||||
srcA: one,
|
||||
dstRGB: oneMinusSrcColor,
|
||||
dstA: oneMinusSrcAlpha
|
||||
}
|
||||
}
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
### Blending and transparency: postLightingBlending
|
||||
|
||||
Type
|
||||
|
||||
@@ -71,6 +71,8 @@ if (FILAMENT_SUPPORTS_OPENGL AND NOT FILAMENT_USE_EXTERNAL_GLES3 AND NOT FILAMEN
|
||||
include/backend/platforms/OpenGLPlatform.h
|
||||
src/opengl/gl_headers.cpp
|
||||
src/opengl/gl_headers.h
|
||||
src/opengl/GLBufferObject.h
|
||||
src/opengl/GLTexture.h
|
||||
src/opengl/GLUtils.cpp
|
||||
src/opengl/GLUtils.h
|
||||
src/opengl/OpenGLBlobCache.cpp
|
||||
@@ -166,8 +168,10 @@ endif()
|
||||
if (FILAMENT_SUPPORTS_VULKAN)
|
||||
list(APPEND SRCS
|
||||
include/backend/platforms/VulkanPlatform.h
|
||||
src/vulkan/caching/VulkanDescriptorSet.cpp
|
||||
src/vulkan/caching/VulkanDescriptorSet.h
|
||||
src/vulkan/caching/VulkanDescriptorSetManager.cpp
|
||||
src/vulkan/caching/VulkanDescriptorSetManager.h
|
||||
src/vulkan/caching/VulkanPipelineLayoutCache.cpp
|
||||
src/vulkan/caching/VulkanPipelineLayoutCache.h
|
||||
src/vulkan/platform/VulkanPlatform.cpp
|
||||
src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp
|
||||
src/vulkan/platform/VulkanPlatformSwapChainImpl.h
|
||||
|
||||
@@ -134,7 +134,7 @@ struct CommandType<void (Driver::*)(ARGS...)> {
|
||||
|
||||
public:
|
||||
template<typename M, typename D>
|
||||
static inline void execute(M&& method, D&& driver, CommandBase* base, intptr_t* next) noexcept {
|
||||
static inline void execute(M&& method, D&& driver, CommandBase* base, intptr_t* next) {
|
||||
Command* self = static_cast<Command*>(base);
|
||||
*next = align(sizeof(Command));
|
||||
#if DEBUG_COMMAND_STREAM
|
||||
@@ -168,7 +168,7 @@ struct CommandType<void (Driver::*)(ARGS...)> {
|
||||
|
||||
class CustomCommand : public CommandBase {
|
||||
std::function<void()> mCommand;
|
||||
static void execute(Driver&, CommandBase* base, intptr_t* next) noexcept;
|
||||
static void execute(Driver&, CommandBase* base, intptr_t* next);
|
||||
public:
|
||||
inline CustomCommand(CustomCommand&& rhs) = default;
|
||||
inline explicit CustomCommand(std::function<void()> cmd)
|
||||
|
||||
@@ -76,7 +76,7 @@ public:
|
||||
// the fn function will execute a batch of driver commands
|
||||
// this gives the driver a chance to wrap their execution in a meaningful manner
|
||||
// the default implementation simply calls fn
|
||||
virtual void execute(std::function<void(void)> const& fn) noexcept;
|
||||
virtual void execute(std::function<void(void)> const& fn);
|
||||
|
||||
// This is called on debug build, or when enabled manually on the backend thread side.
|
||||
virtual void debugCommandBegin(CommandStream* cmds,
|
||||
|
||||
@@ -65,7 +65,7 @@ public:
|
||||
*
|
||||
*/
|
||||
template<typename D, typename ... ARGS>
|
||||
Handle<D> allocateAndConstruct(ARGS&& ... args) noexcept {
|
||||
Handle<D> allocateAndConstruct(ARGS&& ... args) {
|
||||
Handle<D> h{ allocateHandle<D>() };
|
||||
D* addr = handle_cast<D*>(h);
|
||||
new(addr) D(std::forward<ARGS>(args)...);
|
||||
@@ -97,7 +97,7 @@ public:
|
||||
*/
|
||||
template<typename D, typename B, typename ... ARGS>
|
||||
typename std::enable_if_t<std::is_base_of_v<B, D>, D>*
|
||||
destroyAndConstruct(Handle<B> const& handle, ARGS&& ... args) noexcept {
|
||||
destroyAndConstruct(Handle<B> const& handle, ARGS&& ... args) {
|
||||
assert_invariant(handle);
|
||||
D* addr = handle_cast<D*>(const_cast<Handle<B>&>(handle));
|
||||
assert_invariant(addr);
|
||||
@@ -163,7 +163,7 @@ public:
|
||||
inline typename std::enable_if_t<
|
||||
std::is_pointer_v<Dp> &&
|
||||
std::is_base_of_v<B, typename std::remove_pointer_t<Dp>>, Dp>
|
||||
handle_cast(Handle<B>& handle) noexcept {
|
||||
handle_cast(Handle<B>& handle) {
|
||||
assert_invariant(handle);
|
||||
auto [p, tag] = handleToPointer(handle.getId());
|
||||
|
||||
@@ -185,7 +185,7 @@ public:
|
||||
inline typename std::enable_if_t<
|
||||
std::is_pointer_v<Dp> &&
|
||||
std::is_base_of_v<B, typename std::remove_pointer_t<Dp>>, Dp>
|
||||
handle_cast(Handle<B> const& handle) noexcept {
|
||||
handle_cast(Handle<B> const& handle) {
|
||||
return handle_cast<Dp>(const_cast<Handle<B>&>(handle));
|
||||
}
|
||||
|
||||
@@ -317,7 +317,7 @@ private:
|
||||
return (id & HANDLE_HEAP_FLAG) == 0u;
|
||||
}
|
||||
|
||||
HandleBase::HandleId allocateHandleSlow(size_t size) noexcept;
|
||||
HandleBase::HandleId allocateHandleSlow(size_t size);
|
||||
void deallocateHandleSlow(HandleBase::HandleId id, size_t size) noexcept;
|
||||
|
||||
// We inline this because it's just 4 instructions in the fast case
|
||||
|
||||
@@ -149,7 +149,7 @@ void CommandType<void (Driver::*)(ARGS...)>::Command<METHOD>::log() noexcept {
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void CustomCommand::execute(Driver&, CommandBase* base, intptr_t* next) noexcept {
|
||||
void CustomCommand::execute(Driver&, CommandBase* base, intptr_t* next) {
|
||||
*next = CustomCommand::align(sizeof(CustomCommand));
|
||||
static_cast<CustomCommand*>(base)->mCommand();
|
||||
static_cast<CustomCommand*>(base)->~CustomCommand();
|
||||
|
||||
@@ -214,7 +214,7 @@ size_t Driver::getElementTypeSize(ElementType type) noexcept {
|
||||
|
||||
Driver::~Driver() noexcept = default;
|
||||
|
||||
void Driver::execute(std::function<void(void)> const& fn) noexcept {
|
||||
void Driver::execute(std::function<void(void)> const& fn) {
|
||||
fn();
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ void* HandleAllocator<P0, P1, P2>::handleToPointerSlow(HandleBase::HandleId id)
|
||||
}
|
||||
|
||||
template <size_t P0, size_t P1, size_t P2>
|
||||
HandleBase::HandleId HandleAllocator<P0, P1, P2>::allocateHandleSlow(size_t size) noexcept {
|
||||
HandleBase::HandleId HandleAllocator<P0, P1, P2>::allocateHandleSlow(size_t size) {
|
||||
void* p = ::malloc(size);
|
||||
std::unique_lock lock(mLock);
|
||||
|
||||
|
||||
@@ -1634,7 +1634,7 @@ void MetalDriver::bindPipeline(PipelineState ps) {
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT_PRECONDITION(bool(functions), "Attempting to bind an invalid Metal program.");
|
||||
functions.validate();
|
||||
|
||||
auto [fragment, vertex] = functions.getRasterFunctions();
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_OPENGL_GLBUFFEROBJECT_H
|
||||
#define TNT_FILAMENT_BACKEND_OPENGL_GLBUFFEROBJECT_H
|
||||
|
||||
#include "DriverBase.h"
|
||||
|
||||
#include "gl_headers.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
struct GLBufferObject : public HwBufferObject {
|
||||
using HwBufferObject::HwBufferObject;
|
||||
GLBufferObject(uint32_t size,
|
||||
BufferObjectBinding bindingType, BufferUsage usage) noexcept
|
||||
: HwBufferObject(size), usage(usage), bindingType(bindingType) {
|
||||
}
|
||||
|
||||
struct {
|
||||
GLuint id;
|
||||
union {
|
||||
GLenum binding;
|
||||
void* buffer;
|
||||
};
|
||||
} gl;
|
||||
BufferUsage usage;
|
||||
BufferObjectBinding bindingType;
|
||||
uint16_t age = 0;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif //TNT_FILAMENT_BACKEND_OPENGL_GLBUFFEROBJECT_H
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_OPENGL_GLTEXTURE_H
|
||||
#define TNT_FILAMENT_BACKEND_OPENGL_GLTEXTURE_H
|
||||
|
||||
#include "DriverBase.h"
|
||||
|
||||
#include "gl_headers.h"
|
||||
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
struct GLTexture : public HwTexture {
|
||||
using HwTexture::HwTexture;
|
||||
struct GL {
|
||||
GL() noexcept : imported(false), sidecarSamples(1), reserved(0) {}
|
||||
GLuint id = 0; // texture or renderbuffer id
|
||||
GLenum target = 0;
|
||||
GLenum internalFormat = 0;
|
||||
GLuint sidecarRenderBufferMS = 0; // multi-sample sidecar renderbuffer
|
||||
|
||||
// texture parameters go here too
|
||||
GLfloat anisotropy = 1.0;
|
||||
int8_t baseLevel = 127;
|
||||
int8_t maxLevel = -1;
|
||||
uint8_t targetIndex = 0; // optimization: index corresponding to target
|
||||
bool imported : 1;
|
||||
uint8_t sidecarSamples : 4;
|
||||
uint8_t reserved : 3;
|
||||
} gl;
|
||||
|
||||
OpenGLPlatform::ExternalTexture* externalTexture = nullptr;
|
||||
};
|
||||
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif //TNT_FILAMENT_BACKEND_OPENGL_GLTEXTURE_H
|
||||
@@ -64,7 +64,8 @@ bool OpenGLContext::queryOpenGLVersion(GLint* major, GLint* minor) noexcept {
|
||||
}
|
||||
|
||||
OpenGLContext::OpenGLContext(OpenGLPlatform& platform) noexcept
|
||||
: mPlatform(platform) {
|
||||
: mPlatform(platform),
|
||||
mSamplerMap(32) {
|
||||
|
||||
state.vao.p = &mDefaultVAO;
|
||||
|
||||
@@ -252,6 +253,15 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform) noexcept
|
||||
}
|
||||
|
||||
OpenGLContext::~OpenGLContext() noexcept {
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
if (!isES2()) {
|
||||
for (auto& item: mSamplerMap) {
|
||||
unbindSampler(item.second);
|
||||
glDeleteSamplers(1, &item.second);
|
||||
}
|
||||
mSamplerMap.clear();
|
||||
}
|
||||
#endif
|
||||
delete mTimerQueryFactory;
|
||||
}
|
||||
|
||||
@@ -934,6 +944,37 @@ void OpenGLContext::deleteVertexArray(GLuint vao) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
GLuint OpenGLContext::getSamplerSlow(SamplerParams params) const noexcept {
|
||||
assert_invariant(mSamplerMap.find(params) == mSamplerMap.end());
|
||||
|
||||
using namespace GLUtils;
|
||||
|
||||
GLuint s;
|
||||
glGenSamplers(1, &s);
|
||||
glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, (GLint)getTextureFilter(params.filterMin));
|
||||
glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, (GLint)getTextureFilter(params.filterMag));
|
||||
glSamplerParameteri(s, GL_TEXTURE_WRAP_S, (GLint)getWrapMode(params.wrapS));
|
||||
glSamplerParameteri(s, GL_TEXTURE_WRAP_T, (GLint)getWrapMode(params.wrapT));
|
||||
glSamplerParameteri(s, GL_TEXTURE_WRAP_R, (GLint)getWrapMode(params.wrapR));
|
||||
glSamplerParameteri(s, GL_TEXTURE_COMPARE_MODE, (GLint)getTextureCompareMode(params.compareMode));
|
||||
glSamplerParameteri(s, GL_TEXTURE_COMPARE_FUNC, (GLint)getTextureCompareFunc(params.compareFunc));
|
||||
|
||||
#if defined(GL_EXT_texture_filter_anisotropic)
|
||||
if (ext.EXT_texture_filter_anisotropic &&
|
||||
!bugs.texture_filter_anisotropic_broken_on_sampler) {
|
||||
GLfloat const anisotropy = float(1u << params.anisotropyLog2);
|
||||
glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT,
|
||||
std::min(gets.max_anisotropy, anisotropy));
|
||||
}
|
||||
#endif
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
mSamplerMap[params] = s;
|
||||
return s;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void OpenGLContext::resetState() noexcept {
|
||||
// Force GL state to match the Filament state
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
@@ -467,6 +469,29 @@ public:
|
||||
|
||||
void unbindEverything() noexcept;
|
||||
void synchronizeStateAndCache(size_t index) noexcept;
|
||||
void setEs2UniformBinding(size_t index, GLuint id, void const* data, uint16_t age) noexcept {
|
||||
mUniformBindings[index] = { id, data, age };
|
||||
}
|
||||
auto getEs2UniformBinding(size_t index) const noexcept {
|
||||
return mUniformBindings[index];
|
||||
}
|
||||
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
GLuint getSamplerSlow(SamplerParams sp) const noexcept;
|
||||
|
||||
inline GLuint getSampler(SamplerParams sp) const noexcept {
|
||||
assert_invariant(!sp.padding0);
|
||||
assert_invariant(!sp.padding1);
|
||||
assert_invariant(!sp.padding2);
|
||||
auto& samplerMap = mSamplerMap;
|
||||
auto pos = samplerMap.find(sp);
|
||||
if (UTILS_UNLIKELY(pos == samplerMap.end())) {
|
||||
return getSamplerSlow(sp);
|
||||
}
|
||||
return pos->second;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
private:
|
||||
OpenGLPlatform& mPlatform;
|
||||
@@ -476,6 +501,11 @@ private:
|
||||
std::vector<std::function<void(OpenGLContext&)>> mDestroyWithNormalContext;
|
||||
RenderPrimitive mDefaultVAO;
|
||||
std::optional<GLuint> mDefaultFbo[2];
|
||||
std::array<
|
||||
std::tuple<GLuint, void const*, uint16_t>,
|
||||
CONFIG_UNIFORM_BINDING_COUNT> mUniformBindings = {};
|
||||
mutable tsl::robin_map<SamplerParams, GLuint,
|
||||
SamplerParams::Hasher, SamplerParams::EqualTo> mSamplerMap;
|
||||
|
||||
void bindFramebufferResolved(GLenum target, GLuint buffer) noexcept;
|
||||
|
||||
|
||||
@@ -208,7 +208,6 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfi
|
||||
mHandleAllocator("Handles",
|
||||
driverConfig.handleArenaSize,
|
||||
driverConfig.disableHandleUseAfterFreeCheck),
|
||||
mSamplerMap(32),
|
||||
mDriverConfig(driverConfig) {
|
||||
|
||||
std::fill(mSamplerBindings.begin(), mSamplerBindings.end(), nullptr);
|
||||
@@ -263,14 +262,6 @@ void OpenGLDriver::terminate() {
|
||||
|
||||
// because we called glFinish(), all callbacks should have been executed
|
||||
assert_invariant(mGpuCommandCompleteOps.empty());
|
||||
|
||||
if (!getContext().isES2()) {
|
||||
for (auto& item: mSamplerMap) {
|
||||
mContext.unbindSampler(item.second);
|
||||
glDeleteSamplers(1, &item.second);
|
||||
}
|
||||
mSamplerMap.clear();
|
||||
}
|
||||
#endif
|
||||
|
||||
mPlatform.terminate();
|
||||
@@ -307,7 +298,7 @@ bool OpenGLDriver::useProgram(OpenGLProgram* p) noexcept {
|
||||
|
||||
if (UTILS_UNLIKELY(mContext.isES2())) {
|
||||
for (uint32_t i = 0; i < Program::UNIFORM_BINDING_COUNT; i++) {
|
||||
auto [id, buffer, age] = mUniformBindings[i];
|
||||
auto [id, buffer, age] = mContext.getEs2UniformBinding(i);
|
||||
if (buffer) {
|
||||
p->updateUniforms(i, id, buffer, age);
|
||||
}
|
||||
@@ -724,10 +715,19 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
||||
GLenum internalFormat = getInternalFormat(format);
|
||||
assert_invariant(internalFormat);
|
||||
|
||||
if (UTILS_UNLIKELY(usage & TextureUsage::PROTECTED)) {
|
||||
if (any(usage & TextureUsage::PROTECTED)) {
|
||||
// renderbuffers don't have a protected mode, so we need to use a texture
|
||||
// because protected textures are only supported on GLES 3.2, MSAA will be available.
|
||||
usage |= TextureUsage::SAMPLEABLE;
|
||||
} else if (any(usage & TextureUsage::UPLOADABLE)) {
|
||||
// if we have the uploadable flag, we also need to use a texture
|
||||
usage |= TextureUsage::SAMPLEABLE;
|
||||
} else if (target != SamplerType::SAMPLER_2D) {
|
||||
// renderbuffers can only be 2D
|
||||
usage |= TextureUsage::SAMPLEABLE;
|
||||
} else if (levels > 1) {
|
||||
// renderbuffers can't have mip levels
|
||||
usage |= TextureUsage::SAMPLEABLE;
|
||||
}
|
||||
|
||||
auto& gl = mContext;
|
||||
@@ -803,13 +803,6 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
||||
textureStorage(t, w, h, depth, bool(usage & TextureUsage::PROTECTED));
|
||||
}
|
||||
} else {
|
||||
assert_invariant(any(usage & (
|
||||
TextureUsage::COLOR_ATTACHMENT |
|
||||
TextureUsage::DEPTH_ATTACHMENT |
|
||||
TextureUsage::STENCIL_ATTACHMENT)));
|
||||
assert_invariant(levels == 1);
|
||||
assert_invariant(target == SamplerType::SAMPLER_2D);
|
||||
assert_invariant(none(usage & TextureUsage::PROTECTED));
|
||||
t->gl.internalFormat = internalFormat;
|
||||
t->gl.target = GL_RENDERBUFFER;
|
||||
glGenRenderbuffers(1, &t->gl.id);
|
||||
@@ -2343,7 +2336,7 @@ void OpenGLDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
|
||||
#endif
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
if (UTILS_LIKELY(!es2)) {
|
||||
samplerId = getSampler(params);
|
||||
samplerId = mContext.getSampler(params);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
@@ -3068,11 +3061,10 @@ void OpenGLDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t ind
|
||||
assert_invariant(offset + size <= ub->byteCount);
|
||||
|
||||
if (UTILS_UNLIKELY(ub->bindingType == BufferObjectBinding::UNIFORM && gl.isES2())) {
|
||||
mUniformBindings[index] = {
|
||||
gl.setEs2UniformBinding(index,
|
||||
ub->gl.id,
|
||||
static_cast<uint8_t const*>(ub->gl.buffer) + offset,
|
||||
ub->age,
|
||||
};
|
||||
ub->age);
|
||||
} else {
|
||||
GLenum const target = GLUtils::getBufferBindingType(bindingType);
|
||||
|
||||
@@ -3090,7 +3082,7 @@ void OpenGLDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index)
|
||||
auto& gl = mContext;
|
||||
|
||||
if (UTILS_UNLIKELY(bindingType == BufferObjectBinding::UNIFORM && gl.isES2())) {
|
||||
mUniformBindings[index] = {};
|
||||
gl.setEs2UniformBinding(index, 0, nullptr, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3107,36 +3099,6 @@ void OpenGLDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
}
|
||||
|
||||
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
GLuint OpenGLDriver::getSamplerSlow(SamplerParams params) const noexcept {
|
||||
assert_invariant(mSamplerMap.find(params) == mSamplerMap.end());
|
||||
|
||||
GLuint s;
|
||||
glGenSamplers(1, &s);
|
||||
glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, (GLint)getTextureFilter(params.filterMin));
|
||||
glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, (GLint)getTextureFilter(params.filterMag));
|
||||
glSamplerParameteri(s, GL_TEXTURE_WRAP_S, (GLint)getWrapMode(params.wrapS));
|
||||
glSamplerParameteri(s, GL_TEXTURE_WRAP_T, (GLint)getWrapMode(params.wrapT));
|
||||
glSamplerParameteri(s, GL_TEXTURE_WRAP_R, (GLint)getWrapMode(params.wrapR));
|
||||
glSamplerParameteri(s, GL_TEXTURE_COMPARE_MODE, (GLint)getTextureCompareMode(params.compareMode));
|
||||
glSamplerParameteri(s, GL_TEXTURE_COMPARE_FUNC, (GLint)getTextureCompareFunc(params.compareFunc));
|
||||
|
||||
#if defined(GL_EXT_texture_filter_anisotropic)
|
||||
auto& gl = mContext;
|
||||
if (gl.ext.EXT_texture_filter_anisotropic &&
|
||||
!gl.bugs.texture_filter_anisotropic_broken_on_sampler) {
|
||||
GLfloat const anisotropy = float(1u << params.anisotropyLog2);
|
||||
glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT,
|
||||
std::min(gl.gets.max_anisotropy, anisotropy));
|
||||
}
|
||||
#endif
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
mSamplerMap[params] = s;
|
||||
return s;
|
||||
}
|
||||
#endif
|
||||
|
||||
void OpenGLDriver::insertEventMarker(char const* string, uint32_t len) {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#ifdef GL_EXT_debug_marker
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#include "DriverBase.h"
|
||||
#include "OpenGLContext.h"
|
||||
#include "OpenGLTimerQuery.h"
|
||||
#include "GLBufferObject.h"
|
||||
#include "GLTexture.h"
|
||||
#include "ShaderCompilerService.h"
|
||||
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
@@ -92,25 +94,6 @@ public:
|
||||
bool rec709 = false;
|
||||
};
|
||||
|
||||
struct GLBufferObject : public HwBufferObject {
|
||||
using HwBufferObject::HwBufferObject;
|
||||
GLBufferObject(uint32_t size,
|
||||
BufferObjectBinding bindingType, BufferUsage usage) noexcept
|
||||
: HwBufferObject(size), usage(usage), bindingType(bindingType) {
|
||||
}
|
||||
|
||||
struct {
|
||||
GLuint id;
|
||||
union {
|
||||
GLenum binding;
|
||||
void* buffer;
|
||||
};
|
||||
} gl;
|
||||
BufferUsage usage;
|
||||
BufferObjectBinding bindingType;
|
||||
uint16_t age = 0;
|
||||
};
|
||||
|
||||
struct GLVertexBufferInfo : public HwVertexBufferInfo {
|
||||
GLVertexBufferInfo() noexcept = default;
|
||||
GLVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
|
||||
@@ -140,7 +123,6 @@ public:
|
||||
} gl;
|
||||
};
|
||||
|
||||
struct GLTexture;
|
||||
struct GLSamplerGroup : public HwSamplerGroup {
|
||||
using HwSamplerGroup::HwSamplerGroup;
|
||||
struct Entry {
|
||||
@@ -157,27 +139,9 @@ public:
|
||||
Handle<HwVertexBufferInfo> vbih;
|
||||
};
|
||||
|
||||
struct GLTexture : public HwTexture {
|
||||
using HwTexture::HwTexture;
|
||||
struct GL {
|
||||
GL() noexcept : imported(false), sidecarSamples(1), reserved(0) {}
|
||||
GLuint id = 0; // texture or renderbuffer id
|
||||
GLenum target = 0;
|
||||
GLenum internalFormat = 0;
|
||||
GLuint sidecarRenderBufferMS = 0; // multi-sample sidecar renderbuffer
|
||||
using GLBufferObject = filament::backend::GLBufferObject;
|
||||
|
||||
// texture parameters go here too
|
||||
GLfloat anisotropy = 1.0;
|
||||
int8_t baseLevel = 127;
|
||||
int8_t maxLevel = -1;
|
||||
uint8_t targetIndex = 0; // optimization: index corresponding to target
|
||||
bool imported : 1;
|
||||
uint8_t sidecarSamples : 4;
|
||||
uint8_t reserved : 3;
|
||||
} gl;
|
||||
|
||||
OpenGLPlatform::ExternalTexture* externalTexture = nullptr;
|
||||
};
|
||||
using GLTexture = filament::backend::GLTexture;
|
||||
|
||||
using GLTimerQuery = filament::backend::GLTimerQuery;
|
||||
|
||||
@@ -268,13 +232,13 @@ private:
|
||||
HandleAllocatorGL mHandleAllocator;
|
||||
|
||||
template<typename D, typename ... ARGS>
|
||||
Handle<D> initHandle(ARGS&& ... args) noexcept {
|
||||
Handle<D> initHandle(ARGS&& ... args) {
|
||||
return mHandleAllocator.allocateAndConstruct<D>(std::forward<ARGS>(args) ...);
|
||||
}
|
||||
|
||||
template<typename D, typename B, typename ... ARGS>
|
||||
typename std::enable_if<std::is_base_of<B, D>::value, D>::type*
|
||||
construct(Handle<B> const& handle, ARGS&& ... args) noexcept {
|
||||
construct(Handle<B> const& handle, ARGS&& ... args) {
|
||||
return mHandleAllocator.destroyAndConstruct<D, B>(handle, std::forward<ARGS>(args) ...);
|
||||
}
|
||||
|
||||
@@ -288,7 +252,7 @@ private:
|
||||
typename std::enable_if_t<
|
||||
std::is_pointer_v<Dp> &&
|
||||
std::is_base_of_v<B, typename std::remove_pointer_t<Dp>>, Dp>
|
||||
handle_cast(Handle<B>& handle) noexcept {
|
||||
handle_cast(Handle<B>& handle) {
|
||||
return mHandleAllocator.handle_cast<Dp, B>(handle);
|
||||
}
|
||||
|
||||
@@ -296,7 +260,7 @@ private:
|
||||
inline typename std::enable_if_t<
|
||||
std::is_pointer_v<Dp> &&
|
||||
std::is_base_of_v<B, typename std::remove_pointer_t<Dp>>, Dp>
|
||||
handle_cast(Handle<B> const& handle) noexcept {
|
||||
handle_cast(Handle<B> const& handle) {
|
||||
return mHandleAllocator.handle_cast<Dp, B>(handle);
|
||||
}
|
||||
|
||||
@@ -348,22 +312,6 @@ private:
|
||||
void resolvePass(ResolveAction action, GLRenderTarget const* rt,
|
||||
TargetBufferFlags discardFlags) noexcept;
|
||||
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
GLuint getSamplerSlow(SamplerParams sp) const noexcept;
|
||||
|
||||
inline GLuint getSampler(SamplerParams sp) const noexcept {
|
||||
assert_invariant(!sp.padding0);
|
||||
assert_invariant(!sp.padding1);
|
||||
assert_invariant(!sp.padding2);
|
||||
auto& samplerMap = mSamplerMap;
|
||||
auto pos = samplerMap.find(sp);
|
||||
if (UTILS_UNLIKELY(pos == samplerMap.end())) {
|
||||
return getSamplerSlow(sp);
|
||||
}
|
||||
return pos->second;
|
||||
}
|
||||
#endif
|
||||
|
||||
const std::array<GLSamplerGroup*, Program::SAMPLER_BINDING_COUNT>& getSamplerBindings() const {
|
||||
return mSamplerBindings;
|
||||
}
|
||||
@@ -390,14 +338,10 @@ private:
|
||||
|
||||
// ES2 only. Uniform buffer emulation binding points
|
||||
GLuint mLastAssignedEmulatedUboId = 0;
|
||||
std::array<std::tuple<GLuint, void const*, uint16_t>, Program::UNIFORM_BINDING_COUNT> mUniformBindings = {};
|
||||
|
||||
// sampler buffer binding points (nullptr if not used)
|
||||
std::array<GLSamplerGroup*, Program::SAMPLER_BINDING_COUNT> mSamplerBindings = {}; // 4 pointers
|
||||
|
||||
mutable tsl::robin_map<SamplerParams, GLuint,
|
||||
SamplerParams::Hasher, SamplerParams::EqualTo> mSamplerMap;
|
||||
|
||||
// this must be accessed from the driver thread only
|
||||
std::vector<GLTexture*> mTexturesWithStreamsAttached;
|
||||
|
||||
|
||||
@@ -70,14 +70,14 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
struct {
|
||||
GLuint program = 0;
|
||||
} gl; // 12 bytes
|
||||
|
||||
// For ES2 only
|
||||
void updateUniforms(uint32_t index, GLuint id, void const* buffer, uint16_t age) noexcept;
|
||||
void setRec709ColorSpace(bool rec709) const noexcept;
|
||||
|
||||
struct {
|
||||
GLuint program = 0;
|
||||
} gl; // 4 bytes
|
||||
|
||||
private:
|
||||
// keep these away from of other class attributes
|
||||
struct LazyInitializationData;
|
||||
@@ -89,14 +89,17 @@ private:
|
||||
|
||||
void updateSamplers(OpenGLDriver* gld) const noexcept;
|
||||
|
||||
ShaderCompilerService::program_token_t mToken{};
|
||||
|
||||
// number of bindings actually used by this program
|
||||
uint8_t mUsedBindingsCount = 0u;
|
||||
UTILS_UNUSED uint8_t padding[3] = {};
|
||||
std::array<uint8_t, Program::SAMPLER_BINDING_COUNT> mUsedSamplerBindingPoints; // 4 bytes
|
||||
|
||||
ShaderCompilerService::program_token_t mToken{}; // 16 bytes
|
||||
|
||||
uint8_t mUsedBindingsCount = 0u; // 1 byte
|
||||
UTILS_UNUSED uint8_t padding[3] = {}; // 3 bytes
|
||||
|
||||
|
||||
// only needed for ES2
|
||||
GLint mRec709Location = -1; // 4 bytes
|
||||
using LocationInfo = utils::FixedCapacityVector<GLint>;
|
||||
struct UniformsRecord {
|
||||
Program::UniformInfo uniforms;
|
||||
@@ -105,11 +108,10 @@ private:
|
||||
mutable uint16_t age = std::numeric_limits<uint16_t>::max();
|
||||
};
|
||||
UniformsRecord const* mUniformsRecords = nullptr;
|
||||
GLint mRec709Location = -1;
|
||||
};
|
||||
|
||||
// if OpenGLProgram is larger tha 64 bytes, it'll fall in a larger Handle bucket.
|
||||
static_assert(sizeof(OpenGLProgram) <= 64);
|
||||
static_assert(sizeof(OpenGLProgram) <= 64); // currently 48 bytes
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANBLITTER_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANBLITTER_H
|
||||
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanContext.h"
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "VulkanContext.h"
|
||||
#include "VulkanStagePool.h"
|
||||
#include "VulkanMemory.h"
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
#include "VulkanConstants.h"
|
||||
#include "VulkanImageUtility.h"
|
||||
#include "VulkanPipelineCache.h"
|
||||
#include "VulkanUtility.h"
|
||||
|
||||
#include <utils/bitset.h>
|
||||
@@ -42,7 +41,7 @@ struct VulkanTimerQuery;
|
||||
struct VulkanCommandBuffer;
|
||||
|
||||
struct VulkanAttachment {
|
||||
VulkanTexture* texture;
|
||||
VulkanTexture* texture = nullptr;
|
||||
uint8_t level = 0;
|
||||
uint16_t layer = 0;
|
||||
VkImage getImage() const;
|
||||
|
||||
@@ -1771,14 +1771,13 @@ void VulkanDriver::bindPipeline(PipelineState pipelineState) {
|
||||
VulkanTexture* samplerTextures[VulkanPipelineCache::SAMPLER_BINDING_COUNT] = {nullptr};
|
||||
|
||||
auto const& bindingToSamplerIndex = program->getBindingToSamplerIndex();
|
||||
VulkanPipelineCache::UsageFlags usage = program->getUsage();
|
||||
UsageFlags usage = program->getUsage();
|
||||
|
||||
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
|
||||
auto const& bindingToName = program->getBindingToName();
|
||||
#endif
|
||||
|
||||
UTILS_NOUNROLL
|
||||
for (uint8_t binding = 0; binding < VulkanPipelineCache::SAMPLER_BINDING_COUNT; binding++) {
|
||||
for (auto binding: program->getBindings()) {
|
||||
uint16_t const indexPair = bindingToSamplerIndex[binding];
|
||||
|
||||
if (indexPair == 0xffff) {
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
#include "VulkanConstants.h"
|
||||
#include "VulkanDriver.h"
|
||||
#include "VulkanMemory.h"
|
||||
#include "VulkanUtility.h"
|
||||
#include "spirv/VulkanSpirvUtils.h"
|
||||
#include "utils/Log.h"
|
||||
|
||||
#include <backend/platforms/VulkanPlatform.h>
|
||||
|
||||
@@ -30,15 +32,17 @@ using namespace bluevk;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
static void flipVertically(VkRect2D* rect, uint32_t framebufferHeight) {
|
||||
namespace {
|
||||
|
||||
void flipVertically(VkRect2D* rect, uint32_t framebufferHeight) {
|
||||
rect->offset.y = framebufferHeight - rect->offset.y - rect->extent.height;
|
||||
}
|
||||
|
||||
static void flipVertically(VkViewport* rect, uint32_t framebufferHeight) {
|
||||
void flipVertically(VkViewport* rect, uint32_t framebufferHeight) {
|
||||
rect->y = framebufferHeight - rect->y - rect->height;
|
||||
}
|
||||
|
||||
static void clampToFramebuffer(VkRect2D* rect, uint32_t fbWidth, uint32_t fbHeight) {
|
||||
void clampToFramebuffer(VkRect2D* rect, uint32_t fbWidth, uint32_t fbHeight) {
|
||||
int32_t x = std::max(rect->offset.x, 0);
|
||||
int32_t y = std::max(rect->offset.y, 0);
|
||||
int32_t right = std::min(rect->offset.x + (int32_t) rect->extent.width, (int32_t) fbWidth);
|
||||
@@ -49,17 +53,103 @@ static void clampToFramebuffer(VkRect2D* rect, uint32_t fbWidth, uint32_t fbHeig
|
||||
rect->extent.height = std::max(top - y, 0);
|
||||
}
|
||||
|
||||
template<typename Bitmask>
|
||||
static constexpr Bitmask fromStageFlags(ShaderStageFlags2 flags, uint8_t binding) {
|
||||
Bitmask ret = 0;
|
||||
if (flags & ShaderStageFlags2::VERTEX) {
|
||||
ret |= (getVertexStage<Bitmask>() << binding);
|
||||
}
|
||||
if (flags & ShaderStageFlags2::FRAGMENT) {
|
||||
ret |= (getFragmentStage<Bitmask>() << binding);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
UsageFlags getUsageFlags(uint16_t binding, ShaderStageFlags flags, UsageFlags src) {
|
||||
// NOTE: if you modify this function, you also need to modify getShaderStageFlags.
|
||||
assert_invariant(binding < MAX_SAMPLER_COUNT);
|
||||
if (any(flags & ShaderStageFlags::VERTEX)) {
|
||||
src.set(binding);
|
||||
}
|
||||
if (any(flags & ShaderStageFlags::FRAGMENT)) {
|
||||
src.set(MAX_SAMPLER_COUNT + binding);
|
||||
}
|
||||
// TODO: add support for compute by extending SHADER_MODULE_COUNT and ensuring UsageFlags
|
||||
// has 186 bits (MAX_SAMPLER_COUNT * 3)
|
||||
// assert_invariant(!any(flags & ~(ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT)));
|
||||
return src;
|
||||
}
|
||||
|
||||
constexpr decltype(VulkanProgram::MAX_SHADER_MODULES) MAX_SHADER_MODULES =
|
||||
VulkanProgram::MAX_SHADER_MODULES;
|
||||
|
||||
using LayoutDescriptionList = VulkanProgram::LayoutDescriptionList;
|
||||
|
||||
template<typename Bitmask>
|
||||
void addDescriptors(Bitmask mask,
|
||||
utils::FixedCapacityVector<DescriptorSetLayoutBinding>& outputList) {
|
||||
constexpr uint8_t MODULE_OFFSET = (sizeof(Bitmask) * 8) / MAX_SHADER_MODULES;
|
||||
for (uint8_t i = 0; i < MODULE_OFFSET; ++i) {
|
||||
bool const hasVertex = (mask & (1ULL << i)) != 0;
|
||||
bool const hasFragment = (mask & (1ULL << (MODULE_OFFSET + i))) != 0;
|
||||
if (!hasVertex && !hasFragment) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DescriptorSetLayoutBinding binding{
|
||||
.binding = i,
|
||||
.flags = DescriptorFlags::NONE,
|
||||
.count = 0,// This is always 0 for now as we pass the size of the UBOs in the Driver API
|
||||
// instead.
|
||||
};
|
||||
if (hasVertex) {
|
||||
binding.stageFlags = ShaderStageFlags2::VERTEX;
|
||||
}
|
||||
if (hasFragment) {
|
||||
binding.stageFlags = static_cast<ShaderStageFlags2>(
|
||||
binding.stageFlags | ShaderStageFlags2::FRAGMENT);
|
||||
}
|
||||
if constexpr (std::is_same_v<Bitmask, UniformBufferBitmask>) {
|
||||
binding.type = DescriptorType::UNIFORM_BUFFER;
|
||||
} else if constexpr (std::is_same_v<Bitmask, SamplerBitmask>) {
|
||||
binding.type = DescriptorType::SAMPLER;
|
||||
} else if constexpr (std::is_same_v<Bitmask, InputAttachmentBitmask>) {
|
||||
binding.type = DescriptorType::INPUT_ATTACHMENT;
|
||||
}
|
||||
outputList.push_back(binding);
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
|
||||
: HwProgram(builder.getName()),
|
||||
VulkanResource(VulkanResourceType::PROGRAM),
|
||||
mInfo(new PipelineInfo()),
|
||||
mDevice(device) {
|
||||
|
||||
constexpr uint8_t UBO_MODULE_OFFSET = (sizeof(UniformBufferBitmask) * 8) / MAX_SHADER_MODULES;
|
||||
constexpr uint8_t SAMPLER_MODULE_OFFSET = (sizeof(SamplerBitmask) * 8) / MAX_SHADER_MODULES;
|
||||
constexpr uint8_t INPUT_ATTACHMENT_MODULE_OFFSET =
|
||||
(sizeof(InputAttachmentBitmask) * 8) / MAX_SHADER_MODULES;
|
||||
|
||||
Program::ShaderSource const& blobs = builder.getShadersSource();
|
||||
auto& modules = mInfo->shaders;
|
||||
|
||||
auto const& specializationConstants = builder.getSpecializationConstants();
|
||||
|
||||
std::vector<uint32_t> shader;
|
||||
|
||||
// TODO: this will be moved out of the shader as the descriptor set layout will be provided by
|
||||
// Filament instead of parsed from the shaders. See [GDSR] in VulkanDescriptorSetManager.h
|
||||
UniformBufferBitmask uboMask = 0;
|
||||
SamplerBitmask samplerMask = 0;
|
||||
InputAttachmentBitmask inputAttachmentMask = 0;
|
||||
|
||||
static_assert(static_cast<ShaderStage>(0) == ShaderStage::VERTEX &&
|
||||
static_cast<ShaderStage>(1) == ShaderStage::FRAGMENT &&
|
||||
MAX_SHADER_MODULES == 2);
|
||||
|
||||
for (size_t i = 0; i < MAX_SHADER_MODULES; i++) {
|
||||
Program::ShaderBlob const& blob = blobs[i];
|
||||
|
||||
@@ -72,6 +162,12 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
|
||||
dataSize = shader.size() * 4;
|
||||
}
|
||||
|
||||
auto const [ubo, sampler, inputAttachment] = getProgramBindings(blob);
|
||||
uboMask |= (static_cast<UniformBufferBitmask>(ubo) << (UBO_MODULE_OFFSET * i));
|
||||
samplerMask |= (static_cast<SamplerBitmask>(sampler) << (SAMPLER_MODULE_OFFSET * i));
|
||||
inputAttachmentMask |= (static_cast<InputAttachmentBitmask>(inputAttachment)
|
||||
<< (INPUT_ATTACHMENT_MODULE_OFFSET * i));
|
||||
|
||||
VkShaderModule& module = modules[i];
|
||||
VkShaderModuleCreateInfo moduleInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
||||
@@ -99,12 +195,25 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
|
||||
#endif
|
||||
}
|
||||
|
||||
LayoutDescriptionList& layouts = mInfo->layouts;
|
||||
layouts[0].bindings = utils::FixedCapacityVector<DescriptorSetLayoutBinding>::with_capacity(
|
||||
countBits(collapseStages(uboMask)));
|
||||
layouts[1].bindings = utils::FixedCapacityVector<DescriptorSetLayoutBinding>::with_capacity(
|
||||
countBits(collapseStages(samplerMask)));
|
||||
layouts[2].bindings = utils::FixedCapacityVector<DescriptorSetLayoutBinding>::with_capacity(
|
||||
countBits(collapseStages(inputAttachmentMask)));
|
||||
|
||||
addDescriptors(uboMask, layouts[0].bindings);
|
||||
addDescriptors(samplerMask, layouts[1].bindings);
|
||||
addDescriptors(inputAttachmentMask, layouts[2].bindings);
|
||||
|
||||
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
|
||||
auto& bindingToName = mInfo->bindingToName;
|
||||
#endif
|
||||
|
||||
auto& groupInfo = builder.getSamplerGroupInfo();
|
||||
auto& bindingToSamplerIndex = mInfo->bindingToSamplerIndex;
|
||||
auto& bindings = mInfo->bindings;
|
||||
auto& usage = mInfo->usage;
|
||||
for (uint8_t groupInd = 0; groupInd < Program::SAMPLER_BINDING_COUNT; groupInd++) {
|
||||
auto const& group = groupInfo[groupInd];
|
||||
@@ -112,7 +221,9 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
|
||||
for (size_t i = 0; i < samplers.size(); ++i) {
|
||||
uint32_t const binding = samplers[i].binding;
|
||||
bindingToSamplerIndex[binding] = (groupInd << 8) | (0xff & i);
|
||||
usage = VulkanPipelineCache::getUsageFlags(binding, group.stageFlags, usage);
|
||||
assert_invariant(bindings.find(binding) == bindings.end());
|
||||
bindings.insert(binding);
|
||||
usage = getUsageFlags(binding, group.stageFlags, usage);
|
||||
|
||||
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
|
||||
bindingToName[binding] = samplers[i].name.c_str();
|
||||
@@ -126,24 +237,6 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
|
||||
#endif
|
||||
}
|
||||
|
||||
VulkanProgram::VulkanProgram(VkDevice device, VkShaderModule vs, VkShaderModule fs,
|
||||
CustomSamplerInfoList const& samplerInfo) noexcept
|
||||
: VulkanResource(VulkanResourceType::PROGRAM),
|
||||
mInfo(new PipelineInfo()),
|
||||
mDevice(device) {
|
||||
mInfo->shaders[0] = vs;
|
||||
mInfo->shaders[1] = fs;
|
||||
auto& bindingToSamplerIndex = mInfo->bindingToSamplerIndex;
|
||||
auto& usage = mInfo->usage;
|
||||
bindingToSamplerIndex.resize(samplerInfo.size());
|
||||
for (uint16_t binding = 0; binding < samplerInfo.size(); ++binding) {
|
||||
auto const& sampler = samplerInfo[binding];
|
||||
bindingToSamplerIndex[binding]
|
||||
= (sampler.groupIndex << 8) | (0xff & sampler.samplerIndex);
|
||||
usage = VulkanPipelineCache::getUsageFlags(binding, sampler.flags, usage);
|
||||
}
|
||||
}
|
||||
|
||||
VulkanProgram::~VulkanProgram() {
|
||||
for (auto shader: mInfo->shaders) {
|
||||
vkDestroyShaderModule(mDevice, shader, VKALLOC);
|
||||
@@ -405,4 +498,35 @@ VulkanRenderPrimitive::VulkanRenderPrimitive(VulkanResourceAllocator* resourceAl
|
||||
mResources.acquire(indexBuffer);
|
||||
}
|
||||
|
||||
using Bitmask = VulkanDescriptorSetLayout::Bitmask;
|
||||
|
||||
Bitmask Bitmask::fromBackendLayout(descset::DescriptorSetLayout const& layout) {
|
||||
Bitmask mask;
|
||||
for (auto const& binding: layout.bindings) {
|
||||
switch (binding.type) {
|
||||
case descset::DescriptorType::UNIFORM_BUFFER: {
|
||||
if (binding.flags == descset::DescriptorFlags::DYNAMIC_OFFSET) {
|
||||
mask.dynamicUbo |= fromStageFlags<UniformBufferBitmask>(binding.stageFlags,
|
||||
binding.binding);
|
||||
} else {
|
||||
mask.ubo |= fromStageFlags<UniformBufferBitmask>(binding.stageFlags,
|
||||
binding.binding);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case descset::DescriptorType::SAMPLER: {
|
||||
mask.sampler |= fromStageFlags<SamplerBitmask>(binding.stageFlags, binding.binding);
|
||||
break;
|
||||
}
|
||||
case descset::DescriptorType::INPUT_ATTACHMENT: {
|
||||
mask.inputAttachment |=
|
||||
fromStageFlags<InputAttachmentBitmask>(binding.stageFlags, binding.binding);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -21,35 +21,172 @@
|
||||
#include "DriverBase.h"
|
||||
|
||||
#include "VulkanBuffer.h"
|
||||
#include "VulkanPipelineCache.h"
|
||||
#include "VulkanResources.h"
|
||||
#include "VulkanSwapChain.h"
|
||||
#include "VulkanTexture.h"
|
||||
#include "VulkanUtility.h"
|
||||
|
||||
#include "private/backend/SamplerGroup.h"
|
||||
#include "utils/FixedCapacityVector.h"
|
||||
#include "vulkan/vulkan_core.h"
|
||||
|
||||
#include <utils/Mutex.h>
|
||||
#include <utils/StructureOfArrays.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
using namespace descset;
|
||||
|
||||
class VulkanTimestamps;
|
||||
|
||||
struct VulkanDescriptorSetLayout : public VulkanResource {
|
||||
static constexpr uint8_t UNIQUE_DESCRIPTOR_SET_COUNT = 3;
|
||||
|
||||
// The bitmask representation of a set layout.
|
||||
struct Bitmask {
|
||||
UniformBufferBitmask ubo = 0; // 4 bytes
|
||||
UniformBufferBitmask dynamicUbo = 0; // 4 bytes
|
||||
SamplerBitmask sampler = 0; // 8 bytes
|
||||
InputAttachmentBitmask inputAttachment = 0; // 1 bytes
|
||||
|
||||
// Because we're using this struct as hash key, must make it's 8-bytes aligned, with no
|
||||
// unaccounted bytes.
|
||||
uint8_t padding0 = 0; // 1 bytes
|
||||
uint16_t padding1 = 0;// 2 bytes
|
||||
uint32_t padding2 = 0;// 4 bytes
|
||||
|
||||
bool operator==(Bitmask const& right) const {
|
||||
return ubo == right.ubo && dynamicUbo == right.dynamicUbo && sampler == right.sampler &&
|
||||
inputAttachment == right.inputAttachment;
|
||||
}
|
||||
|
||||
static Bitmask fromBackendLayout(descset::DescriptorSetLayout const& layout);
|
||||
};
|
||||
|
||||
// This is a convenience struct to quickly check layout compatibility in terms of descriptor set
|
||||
// pools.
|
||||
struct Count {
|
||||
uint32_t ubo = 0;
|
||||
uint32_t dynamicUbo = 0;
|
||||
uint32_t sampler = 0;
|
||||
uint32_t inputAttachment = 0;
|
||||
|
||||
bool operator==(Count const& right) const noexcept {
|
||||
return ubo == right.ubo && dynamicUbo == right.dynamicUbo && sampler == right.sampler &&
|
||||
inputAttachment == right.inputAttachment;
|
||||
}
|
||||
|
||||
static inline Count fromLayoutBitmask(Bitmask const& mask) {
|
||||
return {
|
||||
.ubo = countBits(collapseStages(mask.ubo)),
|
||||
.dynamicUbo = countBits(collapseStages(mask.dynamicUbo)),
|
||||
.sampler = countBits(collapseStages(mask.sampler)),
|
||||
.inputAttachment = countBits(collapseStages(mask.inputAttachment)),
|
||||
};
|
||||
}
|
||||
|
||||
Count operator*(uint16_t mult) const noexcept {
|
||||
// TODO: check for overflow.
|
||||
|
||||
Count ret;
|
||||
ret.ubo = ubo * mult;
|
||||
ret.dynamicUbo = dynamicUbo * mult;
|
||||
ret.sampler = sampler * mult;
|
||||
ret.inputAttachment = inputAttachment * mult;
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(Bitmask) % 8 == 0);
|
||||
|
||||
explicit VulkanDescriptorSetLayout(VkDescriptorSetLayout layout, Bitmask const& bitmask)
|
||||
: VulkanResource(VulkanResourceType::DESCRIPTOR_SET_LAYOUT),
|
||||
vklayout(layout),
|
||||
bitmask(bitmask),
|
||||
bindings(getBindings(bitmask)),
|
||||
count(Count::fromLayoutBitmask(bitmask)) {}
|
||||
|
||||
VkDescriptorSetLayout const vklayout;
|
||||
Bitmask const bitmask;
|
||||
|
||||
// This is a convenience struct so that we don't have to iterate through all the bits of the
|
||||
// bitmask (which correspondings to binding indices).
|
||||
struct _Bindings {
|
||||
utils::FixedCapacityVector<uint8_t> const ubo;
|
||||
utils::FixedCapacityVector<uint8_t> const dynamicUbo;
|
||||
utils::FixedCapacityVector<uint8_t> const sampler;
|
||||
utils::FixedCapacityVector<uint8_t> const inputAttachment;
|
||||
} bindings;
|
||||
|
||||
Count const count;
|
||||
|
||||
private:
|
||||
|
||||
template <typename MaskType>
|
||||
utils::FixedCapacityVector<uint8_t> bits(MaskType mask) {
|
||||
utils::FixedCapacityVector<uint8_t> ret =
|
||||
utils::FixedCapacityVector<uint8_t>::with_capacity(countBits(mask));
|
||||
for (uint8_t i = 0; i < sizeof(mask) * 4; ++i) {
|
||||
if (mask & (1 << i)) {
|
||||
ret.push_back(i);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
_Bindings getBindings(Bitmask const& bitmask) {
|
||||
auto const uboCollapsed = collapseStages(bitmask.ubo);
|
||||
auto const dynamicUboCollapsed = collapseStages(bitmask.dynamicUbo);
|
||||
auto const samplerCollapsed = collapseStages(bitmask.sampler);
|
||||
auto const inputAttachmentCollapsed = collapseStages(bitmask.inputAttachment);
|
||||
return {
|
||||
bits(uboCollapsed),
|
||||
bits(dynamicUboCollapsed),
|
||||
bits(samplerCollapsed),
|
||||
bits(inputAttachmentCollapsed),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
using VulkanDescriptorSetLayoutList = std::array<Handle<VulkanDescriptorSetLayout>,
|
||||
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
|
||||
|
||||
struct VulkanDescriptorSet : public VulkanResource {
|
||||
public:
|
||||
// Because we need to recycle descriptor sets not used, we allow for a callback that the "Pool"
|
||||
// can use to repackage the vk handle.
|
||||
using OnRecycle = std::function<void()>;
|
||||
|
||||
VulkanDescriptorSet(VulkanResourceAllocator* allocator,
|
||||
VkDescriptorSet rawSet, OnRecycle&& onRecycleFn)
|
||||
: VulkanResource(VulkanResourceType::DESCRIPTOR_SET),
|
||||
resources(allocator),
|
||||
vkSet(rawSet),
|
||||
mOnRecycleFn(std::move(onRecycleFn)) {}
|
||||
|
||||
~VulkanDescriptorSet() {
|
||||
if (mOnRecycleFn) {
|
||||
mOnRecycleFn();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: maybe change to fixed size for performance.
|
||||
VulkanAcquireOnlyResourceManager resources;
|
||||
VkDescriptorSet const vkSet;
|
||||
|
||||
private:
|
||||
OnRecycle mOnRecycleFn;
|
||||
};
|
||||
|
||||
using VulkanDescriptorSetList = std::array<Handle<VulkanDescriptorSet>,
|
||||
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
|
||||
|
||||
struct VulkanProgram : public HwProgram, VulkanResource {
|
||||
|
||||
using BindingList = CappedArray<uint16_t, MAX_SAMPLER_COUNT>;
|
||||
|
||||
VulkanProgram(VkDevice device, Program const& builder) noexcept;
|
||||
|
||||
struct CustomSamplerInfo {
|
||||
uint8_t groupIndex;
|
||||
uint8_t samplerIndex;
|
||||
ShaderStageFlags flags;
|
||||
};
|
||||
using CustomSamplerInfoList = utils::FixedCapacityVector<CustomSamplerInfo>;
|
||||
|
||||
// We allow custom descriptor of the samplers within shaders. This is needed if we want to use
|
||||
// a program that exists only in the backend - for example, for shader-based bliting.
|
||||
VulkanProgram(VkDevice device, VkShaderModule vs, VkShaderModule fs,
|
||||
CustomSamplerInfoList const& samplerInfo) noexcept;
|
||||
~VulkanProgram();
|
||||
|
||||
inline VkShaderModule getVertexShader() const {
|
||||
@@ -58,39 +195,61 @@ struct VulkanProgram : public HwProgram, VulkanResource {
|
||||
|
||||
inline VkShaderModule getFragmentShader() const { return mInfo->shaders[1]; }
|
||||
|
||||
inline VulkanPipelineCache::UsageFlags getUsage() const { return mInfo->usage; }
|
||||
|
||||
inline utils::FixedCapacityVector<uint16_t> const& getBindingToSamplerIndex() const {
|
||||
return mInfo->bindingToSamplerIndex;
|
||||
}
|
||||
|
||||
inline UsageFlags getUsage() const {
|
||||
return mInfo->usage;
|
||||
}
|
||||
|
||||
// Get a list of the sampler binding indices so that we don't have to loop through all possible
|
||||
// samplers.
|
||||
inline BindingList const& getBindings() const { return mInfo->bindings; }
|
||||
|
||||
// TODO: this is currently not used. This will replace getLayoutDescriptionList below.
|
||||
// inline descset::DescriptorSetLayout const& getLayoutDescription() const {
|
||||
// return mInfo->layout;
|
||||
// }
|
||||
// In the usual case, we would have just one layout per program. But in the current setup, we
|
||||
// have a set/layout for each descriptor type. This will be changed in the future.
|
||||
using LayoutDescriptionList = std::array<descset::DescriptorSetLayout,
|
||||
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
|
||||
inline LayoutDescriptionList const& getLayoutDescriptionList() const { return mInfo->layouts; }
|
||||
|
||||
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
|
||||
inline utils::FixedCapacityVector<std::string> const& getBindingToName() const {
|
||||
return mInfo->bindingToName;
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
// TODO: handle compute shaders.
|
||||
// The expected order of shaders - from frontend to backend - is vertex, fragment, compute.
|
||||
static constexpr uint8_t const MAX_SHADER_MODULES = 2;
|
||||
|
||||
private:
|
||||
struct PipelineInfo {
|
||||
PipelineInfo()
|
||||
: bindingToSamplerIndex(MAX_SAMPLER_COUNT, 0xffff)
|
||||
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
|
||||
, bindingToName(MAX_SAMPLER_COUNT, "")
|
||||
#endif
|
||||
#endif
|
||||
{}
|
||||
|
||||
// This bitset maps to each of the sampler in the sampler groups associated with this
|
||||
// program, and whether each sampler is used in which shader (i.e. vert, frag, compute).
|
||||
VulkanPipelineCache::UsageFlags usage;
|
||||
UsageFlags usage;
|
||||
|
||||
BindingList bindings;
|
||||
|
||||
// We store the samplerGroupIndex as the top 8-bit and the index within each group as the lower 8-bit.
|
||||
utils::FixedCapacityVector<uint16_t> bindingToSamplerIndex;
|
||||
VkShaderModule shaders[MAX_SHADER_MODULES] = { VK_NULL_HANDLE };
|
||||
|
||||
// TODO: Use this instead of `layouts` after Filament-side Descriptor Set API is in place.
|
||||
// descset::DescriptorSetLayout layout;
|
||||
LayoutDescriptionList layouts;
|
||||
|
||||
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
|
||||
// We store the sampler name mapped from binding index (only for debug purposes).
|
||||
utils::FixedCapacityVector<std::string> bindingToName;
|
||||
|
||||
@@ -31,4 +31,8 @@
|
||||
|
||||
#include "vk_mem_alloc.h"
|
||||
|
||||
VK_DEFINE_HANDLE(VmaAllocator)
|
||||
VK_DEFINE_HANDLE(VmaAllocation)
|
||||
VK_DEFINE_HANDLE(VmaPool)
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_VULKANMEMORY_H
|
||||
|
||||
@@ -34,7 +34,7 @@ using namespace bluevk;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
static VkShaderStageFlags getShaderStageFlags(VulkanPipelineCache::UsageFlags key, uint16_t binding) {
|
||||
static VkShaderStageFlags getShaderStageFlags(UsageFlags key, uint16_t binding) {
|
||||
// NOTE: if you modify this function, you also need to modify getUsageFlags.
|
||||
assert_invariant(binding < MAX_SAMPLER_COUNT);
|
||||
VkShaderStageFlags flags = 0;
|
||||
@@ -47,24 +47,7 @@ static VkShaderStageFlags getShaderStageFlags(VulkanPipelineCache::UsageFlags ke
|
||||
return flags;
|
||||
}
|
||||
|
||||
VulkanPipelineCache::UsageFlags
|
||||
VulkanPipelineCache::getUsageFlags(uint16_t binding, ShaderStageFlags flags, UsageFlags src) {
|
||||
// NOTE: if you modify this function, you also need to modify getShaderStageFlags.
|
||||
assert_invariant(binding < MAX_SAMPLER_COUNT);
|
||||
if (any(flags & ShaderStageFlags::VERTEX)) {
|
||||
src.set(binding);
|
||||
}
|
||||
if (any(flags & ShaderStageFlags::FRAGMENT)) {
|
||||
src.set(MAX_SAMPLER_COUNT + binding);
|
||||
}
|
||||
// TODO: add support for compute by extending SHADER_MODULE_COUNT and ensuring UsageFlags
|
||||
// has 186 bits (MAX_SAMPLER_COUNT * 3)
|
||||
// assert_invariant(!any(flags & ~(ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT)));
|
||||
return src;
|
||||
}
|
||||
|
||||
VulkanPipelineCache::UsageFlags VulkanPipelineCache::disableUsageFlags(uint16_t binding,
|
||||
UsageFlags src) {
|
||||
UsageFlags VulkanPipelineCache::disableUsageFlags(uint16_t binding, UsageFlags src) {
|
||||
src.unset(binding);
|
||||
src.unset(MAX_SAMPLER_COUNT + binding);
|
||||
return src;
|
||||
|
||||
@@ -89,8 +89,6 @@ public:
|
||||
VkSpecializationInfo* specializationInfos = nullptr;
|
||||
};
|
||||
|
||||
using UsageFlags = utils::bitset128;
|
||||
static UsageFlags getUsageFlags(uint16_t binding, ShaderStageFlags stages, UsageFlags src = {});
|
||||
static UsageFlags disableUsageFlags(uint16_t binding, UsageFlags src);
|
||||
|
||||
#pragma clang diagnostic push
|
||||
|
||||
@@ -49,10 +49,10 @@ namespace filament::backend {
|
||||
#endif
|
||||
|
||||
class VulkanResourceAllocator {
|
||||
|
||||
public:
|
||||
VulkanResourceAllocator(size_t arenaSize, bool disableUseAfterFreeCheck)
|
||||
: mHandleAllocatorImpl("Handles", arenaSize, disableUseAfterFreeCheck)
|
||||
using AllocatorImpl = HandleAllocatorVK;
|
||||
VulkanResourceAllocator(size_t arenaSize, bool disableUseAfterFreeCheck)
|
||||
: mHandleAllocatorImpl("Handles", arenaSize, disableUseAfterFreeCheck)
|
||||
#if DEBUG_RESOURCE_LEAKS
|
||||
, mDebugOnlyResourceCount(RESOURCE_TYPE_COUNT) {
|
||||
std::memset(mDebugOnlyResourceCount.data(), 0, sizeof(size_t) * RESOURCE_TYPE_COUNT);
|
||||
@@ -106,7 +106,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
HandleAllocatorVK mHandleAllocatorImpl;
|
||||
AllocatorImpl mHandleAllocatorImpl;
|
||||
|
||||
#if DEBUG_RESOURCE_LEAKS
|
||||
public:
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include "VulkanResources.h"
|
||||
#include "VulkanHandles.h"
|
||||
#include "VulkanResourceAllocator.h"
|
||||
#include "caching/VulkanDescriptorSet.h"
|
||||
#include "VulkanPipelineCache.h"
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -62,6 +62,9 @@ void deallocateResource(VulkanResourceAllocator* allocator, VulkanResourceType t
|
||||
case VulkanResourceType::RENDER_PRIMITIVE:
|
||||
allocator->destruct<VulkanRenderPrimitive>(Handle<VulkanRenderPrimitive>(id));
|
||||
break;
|
||||
case VulkanResourceType::DESCRIPTOR_SET_LAYOUT:
|
||||
allocator->destruct<VulkanDescriptorSetLayout>(Handle<VulkanDescriptorSetLayout>(id));
|
||||
break;
|
||||
case VulkanResourceType::DESCRIPTOR_SET:
|
||||
allocator->destruct<VulkanDescriptorSet>(Handle<VulkanDescriptorSet>(id));
|
||||
break;
|
||||
|
||||
@@ -35,24 +35,25 @@ struct VulkanThreadSafeResource;
|
||||
|
||||
// Subclasses of VulkanResource must provide this enum in their construction.
|
||||
enum class VulkanResourceType : uint8_t {
|
||||
BUFFER_OBJECT,
|
||||
INDEX_BUFFER,
|
||||
PROGRAM,
|
||||
RENDER_TARGET,
|
||||
SAMPLER_GROUP,
|
||||
SWAP_CHAIN,
|
||||
RENDER_PRIMITIVE,
|
||||
TEXTURE,
|
||||
TIMER_QUERY,
|
||||
VERTEX_BUFFER,
|
||||
VERTEX_BUFFER_INFO,
|
||||
DESCRIPTOR_SET,
|
||||
BUFFER_OBJECT = 0,
|
||||
INDEX_BUFFER = 1,
|
||||
PROGRAM = 2,
|
||||
RENDER_TARGET = 3,
|
||||
SAMPLER_GROUP = 4,
|
||||
SWAP_CHAIN = 5,
|
||||
RENDER_PRIMITIVE = 6,
|
||||
TEXTURE = 7,
|
||||
TIMER_QUERY = 8,
|
||||
VERTEX_BUFFER = 9,
|
||||
VERTEX_BUFFER_INFO = 10,
|
||||
DESCRIPTOR_SET_LAYOUT = 11,
|
||||
DESCRIPTOR_SET = 12,
|
||||
|
||||
// Below are resources that are managed manually (i.e. not ref counted).
|
||||
FENCE,
|
||||
HEAP_ALLOCATED,
|
||||
FENCE = 13,
|
||||
HEAP_ALLOCATED = 14,
|
||||
|
||||
END_TYPE, // A placeholder
|
||||
END_TYPE = 15, // A placeholder
|
||||
};
|
||||
|
||||
#define IS_HEAP_ALLOC_TYPE(f) \
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANSTAGEPOOL_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANSTAGEPOOL_H
|
||||
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanContext.h"
|
||||
#include "VulkanMemory.h"
|
||||
|
||||
#include <map>
|
||||
#include <unordered_set>
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "DriverBase.h"
|
||||
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanContext.h"
|
||||
#include "VulkanResources.h"
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "VulkanResources.h"
|
||||
#include "VulkanImageUtility.h"
|
||||
|
||||
#include <utils/Hash.h>
|
||||
#include <utils/RangeMap.h>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
@@ -655,4 +655,6 @@ uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask) {
|
||||
return mostSignificantBit((sampleCount - 1) & mask);
|
||||
}
|
||||
|
||||
_BitCountHelper _BitCountHelper::BitCounter = {};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -90,6 +90,9 @@ utils::FixedCapacityVector<OutType> enumerate(
|
||||
#undef EXPAND_ENUM_NO_ARGS
|
||||
#undef EXPAND_ENUM_ARGS
|
||||
|
||||
// Used across pipeline related classes.
|
||||
using UsageFlags = utils::bitset128;
|
||||
|
||||
// Useful shorthands
|
||||
using VkFormatList = utils::FixedCapacityVector<VkFormat>;
|
||||
|
||||
@@ -122,14 +125,14 @@ public:
|
||||
clear();
|
||||
}
|
||||
|
||||
inline const_iterator begin() {
|
||||
inline const_iterator begin() const {
|
||||
if (mInd == 0) {
|
||||
return mArray.cend();
|
||||
}
|
||||
return mArray.cbegin();
|
||||
}
|
||||
|
||||
inline const_iterator end() {
|
||||
inline const_iterator end() const {
|
||||
if (mInd > 0 && mInd < CAPACITY) {
|
||||
return mArray.begin() + mInd;
|
||||
}
|
||||
@@ -170,10 +173,26 @@ public:
|
||||
return mArray[ind];
|
||||
}
|
||||
|
||||
inline size_t size() {
|
||||
inline T const& operator[](uint16_t ind) const {
|
||||
return mArray[ind];
|
||||
}
|
||||
|
||||
inline uint32_t size() const {
|
||||
return mInd;
|
||||
}
|
||||
|
||||
T* data() {
|
||||
return mArray.data();
|
||||
}
|
||||
|
||||
T const* data() const {
|
||||
return mArray.data();
|
||||
}
|
||||
|
||||
bool operator==(CappedArray const& b) const {
|
||||
return this->mArray == b.mArray;
|
||||
}
|
||||
|
||||
private:
|
||||
void swap(CappedArray& rhs) {
|
||||
std::swap(mArray, rhs.mArray);
|
||||
@@ -181,9 +200,127 @@ private:
|
||||
}
|
||||
|
||||
FixedSizeArray mArray;
|
||||
size_t mInd = 0;
|
||||
uint32_t mInd = 0;
|
||||
};
|
||||
|
||||
// TODO: ok to remove once Filament-side API is complete
|
||||
namespace descset {
|
||||
|
||||
// Used to describe the descriptor binding in shader stages. We assume that the binding index does
|
||||
// not exceed 31. We also assume that we have two shader stages - vertex and fragment. The below
|
||||
// types and struct are used across VulkanDescriptorSet and VulkanProgram.
|
||||
using UniformBufferBitmask = uint32_t;
|
||||
using SamplerBitmask = uint64_t;
|
||||
|
||||
// We only have at most one input attachment, so this bitmask exists only to make the code more
|
||||
// general.
|
||||
using InputAttachmentBitmask = uint8_t;
|
||||
|
||||
constexpr UniformBufferBitmask UBO_VERTEX_STAGE = 0x1;
|
||||
constexpr UniformBufferBitmask UBO_FRAGMENT_STAGE = (0x1ULL << (sizeof(UniformBufferBitmask) * 4));
|
||||
constexpr SamplerBitmask SAMPLER_VERTEX_STAGE = 0x1;
|
||||
constexpr SamplerBitmask SAMPLER_FRAGMENT_STAGE = (0x1ULL << (sizeof(SamplerBitmask) * 4));
|
||||
constexpr InputAttachmentBitmask INPUT_ATTACHMENT_VERTEX_STAGE = 0x1;
|
||||
constexpr InputAttachmentBitmask INPUT_ATTACHMENT_FRAGMENT_STAGE =
|
||||
(0x1ULL << (sizeof(InputAttachmentBitmask) * 4));
|
||||
|
||||
template<typename Bitmask>
|
||||
static constexpr Bitmask getVertexStage() noexcept {
|
||||
if constexpr (std::is_same_v<Bitmask, UniformBufferBitmask>) {
|
||||
return UBO_VERTEX_STAGE;
|
||||
}
|
||||
if constexpr (std::is_same_v<Bitmask, SamplerBitmask>) {
|
||||
return SAMPLER_VERTEX_STAGE;
|
||||
}
|
||||
if constexpr (std::is_same_v<Bitmask, InputAttachmentBitmask>) {
|
||||
return INPUT_ATTACHMENT_VERTEX_STAGE;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Bitmask>
|
||||
static constexpr Bitmask getFragmentStage() noexcept {
|
||||
if constexpr (std::is_same_v<Bitmask, UniformBufferBitmask>) {
|
||||
return UBO_FRAGMENT_STAGE;
|
||||
}
|
||||
if constexpr (std::is_same_v<Bitmask, SamplerBitmask>) {
|
||||
return SAMPLER_FRAGMENT_STAGE;
|
||||
}
|
||||
if constexpr (std::is_same_v<Bitmask, InputAttachmentBitmask>) {
|
||||
return INPUT_ATTACHMENT_FRAGMENT_STAGE;
|
||||
}
|
||||
}
|
||||
|
||||
typedef enum ShaderStageFlags2 : uint8_t {
|
||||
NONE = 0,
|
||||
VERTEX = 0x1,
|
||||
FRAGMENT = 0x2,
|
||||
} ShaderStageFlags2;
|
||||
|
||||
enum class DescriptorType : uint8_t {
|
||||
UNIFORM_BUFFER,
|
||||
SAMPLER,
|
||||
INPUT_ATTACHMENT,
|
||||
};
|
||||
|
||||
enum class DescriptorFlags : uint8_t {
|
||||
NONE = 0x00,
|
||||
DYNAMIC_OFFSET = 0x01
|
||||
};
|
||||
|
||||
struct DescriptorSetLayoutBinding {
|
||||
DescriptorType type;
|
||||
ShaderStageFlags2 stageFlags;
|
||||
uint8_t binding;
|
||||
DescriptorFlags flags;
|
||||
uint16_t count;
|
||||
};
|
||||
|
||||
struct DescriptorSetLayout {
|
||||
utils::FixedCapacityVector<DescriptorSetLayoutBinding> bindings;
|
||||
};
|
||||
|
||||
} // namespace descset
|
||||
|
||||
// Use constexpr to statically generate a bit count table for 8-bit numbers.
|
||||
struct _BitCountHelper {
|
||||
constexpr _BitCountHelper() : data{} {
|
||||
for (uint16_t i = 0; i < 256; ++i) {
|
||||
data[i] = 0;
|
||||
for (auto j = i; j > 0; j /= 2) {
|
||||
if (j & 1) {
|
||||
data[i]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename MaskType>
|
||||
constexpr uint8_t count(MaskType num) {
|
||||
uint8_t count = 0;
|
||||
for (uint8_t i = 0; i < sizeof(MaskType) * 8; i+=8) {
|
||||
count += data[(num >> i) & 0xFF];
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static _BitCountHelper BitCounter;
|
||||
private:
|
||||
uint8_t data[256];
|
||||
};
|
||||
|
||||
template<typename MaskType>
|
||||
inline uint8_t countBits(MaskType num) {
|
||||
return _BitCountHelper::BitCounter.count(num);
|
||||
}
|
||||
|
||||
// This is useful for counting the total number of descriptors for both vertex and fragment stages.
|
||||
template<typename MaskType>
|
||||
inline MaskType collapseStages(MaskType mask) {
|
||||
constexpr uint8_t NBITS_DIV_2 = sizeof(MaskType) * 4;
|
||||
// First zero out the top-half and then or the bottom-half against the original top-half.
|
||||
return ((mask << NBITS_DIV_2) >> NBITS_DIV_2) | (mask >> NBITS_DIV_2);
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_VULKANUTILITY_H
|
||||
|
||||
@@ -1,647 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "VulkanDescriptorSet.h"
|
||||
|
||||
#include <vulkan/VulkanConstants.h>
|
||||
#include <vulkan/VulkanResources.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename MaskType>
|
||||
using LayoutMap = tsl::robin_map<MaskType, VkDescriptorSetLayout>;
|
||||
using LayoutArray = VulkanDescriptorSetManager::LayoutArray;
|
||||
using UniformBufferBitmask = VulkanDescriptorSetManager::UniformBufferBitmask;
|
||||
using SamplerBitmask = VulkanDescriptorSetManager::SamplerBitmask;
|
||||
|
||||
constexpr uint8_t EXPECTED_IN_FLIGHT_FRAMES = 3; // Asssume triple buffering
|
||||
|
||||
#define PORT_CONSTANT(NameSpace, K) constexpr decltype(NameSpace::K) K = NameSpace::K;
|
||||
|
||||
PORT_CONSTANT(VulkanDescriptorSetManager, MAX_SUPPORTED_SHADER_STAGE)
|
||||
PORT_CONSTANT(VulkanDescriptorSetManager, VERTEX_STAGE)
|
||||
PORT_CONSTANT(VulkanDescriptorSetManager, FRAGMENT_STAGE)
|
||||
|
||||
PORT_CONSTANT(Program, UNIFORM_BINDING_COUNT)
|
||||
PORT_CONSTANT(Program, SAMPLER_BINDING_COUNT)
|
||||
|
||||
#undef PORT_CONSTANT
|
||||
|
||||
// Use constexpr to statically generate a bit count table for 8-bit numbers.
|
||||
struct BitCountHelper {
|
||||
constexpr BitCountHelper() : data{} {
|
||||
for (uint16_t i = 0; i < 256; ++i) {
|
||||
data[i] = 0;
|
||||
for (auto j = i; j > 0; j /= 2) {
|
||||
if (j & 1) {
|
||||
data[i]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
uint8_t data[256];
|
||||
} BitCountImpl;
|
||||
|
||||
inline uint8_t countBits(uint32_t num) {
|
||||
static constexpr uint8_t const* BIT_COUNT = BitCountImpl.data;
|
||||
return BIT_COUNT[num & 0xFF] + BIT_COUNT[(num >> 8) & 0xFF] + BIT_COUNT[(num >> 16) & 0xFF] +
|
||||
BIT_COUNT[(num >> 24) & 0xFF];
|
||||
}
|
||||
|
||||
struct SamplerSet {
|
||||
using Mask = UniformBufferBitmask;
|
||||
struct Key {
|
||||
Mask mask;
|
||||
static_assert(sizeof(Mask) == 4);
|
||||
uint32_t padding;// We need a padding to ensure 8-byte alignement.
|
||||
VkDescriptorSetLayout layout;
|
||||
VkSampler sampler[SAMPLER_BINDING_COUNT];
|
||||
VkImageView imageView[SAMPLER_BINDING_COUNT];
|
||||
VkImageLayout imageLayout[SAMPLER_BINDING_COUNT];
|
||||
};
|
||||
|
||||
struct Equal {
|
||||
bool operator()(Key const& k1, Key const& k2) const {
|
||||
if (k1.mask != k2.mask) return false;
|
||||
if (k1.layout != k2.layout) return false;
|
||||
|
||||
for (uint8_t i = 0, bitCount = countBits(k1.mask); i < bitCount; ++i) {
|
||||
if (k1.sampler[i] != k2.sampler[i] || k1.imageView[i] != k2.imageView[i] ||
|
||||
k1.imageLayout[i] != k2.imageLayout[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
using HashFn = utils::hash::MurmurHashFn<Key>;
|
||||
using Cache = std::unordered_map<Key, VulkanDescriptorSet*, HashFn, Equal>;
|
||||
};
|
||||
|
||||
struct UBOSet {
|
||||
using Mask = SamplerBitmask;
|
||||
|
||||
struct Key {
|
||||
Mask mask;
|
||||
static_assert(sizeof(Mask) == 4);
|
||||
uint32_t padding;// We need a padding to ensure 8-byte alignement.
|
||||
VkDescriptorSetLayout layout;
|
||||
VkBuffer buffers[UNIFORM_BINDING_COUNT]; // 80 0
|
||||
VkDeviceSize offsets[UNIFORM_BINDING_COUNT];// 40 1592
|
||||
VkDeviceSize sizes[UNIFORM_BINDING_COUNT]; // 40 1632
|
||||
};
|
||||
|
||||
struct Equal {
|
||||
bool operator()(Key const& k1, Key const& k2) const {
|
||||
if (k1.mask != k2.mask) return false;
|
||||
if (k1.layout != k2.layout) return false;
|
||||
|
||||
for (uint8_t i = 0, bitCount = countBits(k1.mask); i < bitCount; ++i) {
|
||||
if (k1.buffers[i] != k2.buffers[i] || k1.offsets[i] != k2.offsets[i] ||
|
||||
k1.sizes[i] != k2.sizes[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
using HashFn = utils::hash::MurmurHashFn<Key>;
|
||||
using Cache = std::unordered_map<Key, VulkanDescriptorSet*, HashFn, Equal>;
|
||||
};
|
||||
|
||||
constexpr uint8_t MAX_BINDINGS = UNIFORM_BINDING_COUNT * MAX_SUPPORTED_SHADER_STAGE;
|
||||
static_assert(MAX_BINDINGS >= SAMPLER_BINDING_COUNT * MAX_SUPPORTED_SHADER_STAGE);
|
||||
|
||||
template<typename TYPE>
|
||||
class DescriptorPool {
|
||||
private:
|
||||
static constexpr uint32_t COUNT_MULTIPLIER =
|
||||
MAX_SUPPORTED_SHADER_STAGE * EXPECTED_IN_FLIGHT_FRAMES;
|
||||
static constexpr uint32_t CAPACITY =
|
||||
std::is_same_v<TYPE, UBOSet> ? Program::UNIFORM_BINDING_COUNT * COUNT_MULTIPLIER
|
||||
: Program::SAMPLER_BINDING_COUNT * COUNT_MULTIPLIER;
|
||||
|
||||
public:
|
||||
DescriptorPool()
|
||||
: mDevice(VK_NULL_HANDLE),
|
||||
mResourceAllocator(nullptr) {}
|
||||
|
||||
DescriptorPool(VkDevice device, VulkanResourceAllocator* allocator)
|
||||
: mDevice(device),
|
||||
mResourceAllocator(allocator) {
|
||||
VkDescriptorPoolSize sizes[1] = {
|
||||
{
|
||||
.type = std::is_same_v<TYPE, UBOSet> ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
|
||||
: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.descriptorCount = CAPACITY,
|
||||
},
|
||||
};
|
||||
VkDescriptorPoolCreateInfo info{
|
||||
.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
|
||||
.maxSets = EXPECTED_IN_FLIGHT_FRAMES,
|
||||
.poolSizeCount = 1,
|
||||
.pPoolSizes = sizes,
|
||||
};
|
||||
vkCreateDescriptorPool(mDevice, &info, VKALLOC, &mPool);
|
||||
}
|
||||
|
||||
DescriptorPool(DescriptorPool const&) = delete;
|
||||
DescriptorPool& operator=(DescriptorPool const&) = delete;
|
||||
|
||||
~DescriptorPool() {
|
||||
vkDestroyDescriptorPool(mDevice, mPool, VKALLOC);
|
||||
}
|
||||
|
||||
VulkanDescriptorSet* obtainSet(VkDescriptorSetLayout layout) {
|
||||
if (mCount == CAPACITY) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (auto unused = mUnused.find(layout);
|
||||
unused != mUnused.end() && !unused->second.empty()) {
|
||||
auto& unusedList = unused->second;
|
||||
auto set = unusedList.back();
|
||||
unusedList.pop_back();
|
||||
mCount++;
|
||||
return set;
|
||||
}
|
||||
|
||||
// Creating a new set
|
||||
VkDescriptorSetLayout layouts[1] = {layout};
|
||||
VkDescriptorSetAllocateInfo allocInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
||||
.descriptorPool = mPool,
|
||||
.descriptorSetCount = 1,
|
||||
.pSetLayouts = layouts,
|
||||
};
|
||||
VkDescriptorSet vkSet;
|
||||
UTILS_UNUSED VkResult result = vkAllocateDescriptorSets(mDevice, &allocInfo, &vkSet);
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Cannot allocate descriptor set. error=%d",
|
||||
(int) result);
|
||||
|
||||
mCount++;
|
||||
return createSet(layout, vkSet);
|
||||
}
|
||||
|
||||
private:
|
||||
inline VulkanDescriptorSet* createSet(VkDescriptorSetLayout layout, VkDescriptorSet vkSet) {
|
||||
return VulkanDescriptorSet::create(mResourceAllocator, vkSet, layout,
|
||||
[this](VulkanDescriptorSet* set) {
|
||||
auto const layout = set->layout;
|
||||
auto const vkSet = set->vkSet;
|
||||
auto listIter = mUnused.find(layout);
|
||||
assert_invariant(listIter != mUnused.end());
|
||||
auto& unusedList = listIter->second;
|
||||
|
||||
// At this point, we know that the pointer will be stale, and we need to remove
|
||||
// any non-ref-counted references to it.
|
||||
// TODO: this will be inefficient
|
||||
auto iter = std::find(unusedList.begin(), unusedList.end(), set);
|
||||
unusedList.erase(iter);
|
||||
|
||||
// We are recycling - release the vk resource back into the pool. Note that the
|
||||
// vk handle has not changed, but we need to change the backend handle to allow
|
||||
// for proper refcounting of resources referenced in this set.
|
||||
unusedList.push_back(createSet(layout, vkSet));
|
||||
mCount--;
|
||||
});
|
||||
}
|
||||
|
||||
VkDevice mDevice;
|
||||
VulkanResourceAllocator* mResourceAllocator;
|
||||
VkDescriptorPool mPool;
|
||||
|
||||
uint32_t mCount = 0;
|
||||
std::unordered_map<VkDescriptorSetLayout, std::vector<VulkanDescriptorSet*>> mUnused;
|
||||
};
|
||||
|
||||
template<typename TYPE>
|
||||
class DescriptorInfinitePool {
|
||||
public:
|
||||
DescriptorInfinitePool(VkDevice device, VulkanResourceAllocator* allocator)
|
||||
: mDevice(device),
|
||||
mResourceAllocator(allocator) {}
|
||||
|
||||
~DescriptorInfinitePool() {
|
||||
for (auto pool: mPools) {
|
||||
delete pool;
|
||||
}
|
||||
}
|
||||
|
||||
VulkanDescriptorSet* obtainSet(VkDescriptorSetLayout layout) {
|
||||
for (auto pool: mPools) {
|
||||
auto set = pool->obtainSet(layout);
|
||||
if (set) {
|
||||
return set;
|
||||
}
|
||||
}
|
||||
// We need to increase the number of pools
|
||||
mPools.push_back(new DescriptorPool<TYPE>{mDevice, mResourceAllocator});
|
||||
auto pool = mPools.back();
|
||||
return pool->obtainSet(layout);
|
||||
}
|
||||
|
||||
private:
|
||||
VkDevice mDevice;
|
||||
VulkanResourceAllocator* mResourceAllocator;
|
||||
std::vector<DescriptorPool<TYPE>*> mPools;
|
||||
};
|
||||
|
||||
// This holds a cache of descriptor sets of a TYPE (UBO or Sampler). The Key is defined as the
|
||||
// layout and the content (for example specific samplers).
|
||||
template<typename TYPE, typename Cache, typename Key>
|
||||
class DescriptorSetCache {
|
||||
public:
|
||||
DescriptorSetCache(VkDevice device, VulkanResourceAllocator* allocator)
|
||||
: mPool(device, allocator),
|
||||
mResourceManager(allocator) {}
|
||||
|
||||
std::pair<VulkanDescriptorSet*, bool> obtainSet(Key const& key) {
|
||||
mAge++;
|
||||
if (auto result = mCache.find(key); result != mCache.end()) {
|
||||
auto set = result->second;
|
||||
auto const oldAge = mReverseHistory[set];
|
||||
mHistory.erase(oldAge);
|
||||
mHistory[mAge] = set;
|
||||
mReverseHistory[set] = mAge;
|
||||
return {set, true};
|
||||
}
|
||||
|
||||
VulkanDescriptorSet* set = mPool.obtainSet(key.layout);
|
||||
mCache[key] = set;
|
||||
mReverseCache[set] = key;
|
||||
mHistory[mAge] = set;
|
||||
mReverseHistory[set] = mAge;
|
||||
mResourceManager.acquire(set);
|
||||
return {set, false};
|
||||
}
|
||||
|
||||
// We need to periodically purge the descriptor sets so that we're not holding on to resources
|
||||
// unnecessarily. The strategy for purging needs to be examined more.
|
||||
void gc() noexcept {
|
||||
constexpr uint32_t ALLOWED_ENTRIES = EXPECTED_IN_FLIGHT_FRAMES * 3;
|
||||
int32_t const toCutCount = mHistory.size() - ALLOWED_ENTRIES;
|
||||
|
||||
if (toCutCount <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint64_t> remove;
|
||||
for (auto entry = mHistory.begin(); entry != mHistory.end(); entry++) {
|
||||
auto const& set = entry->second;
|
||||
Key const& key = mReverseCache[set];
|
||||
mCache.erase(key);
|
||||
mReverseCache.erase(set);
|
||||
mReverseHistory.erase(set);
|
||||
|
||||
remove.push_back(entry->first);
|
||||
mResourceManager.release(set);
|
||||
}
|
||||
for (auto removeAge: remove) {
|
||||
mHistory.erase(removeAge);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
DescriptorInfinitePool<TYPE> mPool;
|
||||
|
||||
// TODO: combine some of these data structures
|
||||
Cache mCache;
|
||||
std::unordered_map<VulkanDescriptorSet*, Key> mReverseCache;
|
||||
|
||||
// Use the ordering for purging if needed;
|
||||
std::map<uint64_t, VulkanDescriptorSet*> mHistory;
|
||||
std::unordered_map<VulkanDescriptorSet*, uint64_t> mReverseHistory;
|
||||
uint64_t mAge;
|
||||
|
||||
// Note that instead of owning the resources (i.e. descriptor set) in the pools, we keep them
|
||||
// here since all the allocated sets have to pass through this class, and this class has better
|
||||
// knowledge to make decisions about gc.
|
||||
VulkanResourceManager mResourceManager;
|
||||
};
|
||||
|
||||
using UBOSetCache = DescriptorSetCache<UBOSet, UBOSet::Cache, UBOSet::Key>;
|
||||
using SamplerSetCache = DescriptorSetCache<SamplerSet, SamplerSet::Cache, SamplerSet::Key>;
|
||||
|
||||
template<typename MaskType>
|
||||
class LayoutCache {
|
||||
public:
|
||||
explicit LayoutCache(VkDevice device)
|
||||
: mDevice(device) {}
|
||||
|
||||
~LayoutCache() {
|
||||
for (auto [mask, layout]: mLayouts) {
|
||||
vkDestroyDescriptorSetLayout(mDevice, layout, VKALLOC);
|
||||
}
|
||||
mLayouts.clear();
|
||||
}
|
||||
|
||||
VkDescriptorSetLayout getLayout(MaskType stageMask) noexcept {
|
||||
if (stageMask == 0) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
if (auto iter = mLayouts.find(stageMask); iter != mLayouts.end()) {
|
||||
return iter->second;
|
||||
}
|
||||
VkDescriptorType descriptorType;
|
||||
if constexpr (std::is_same_v<MaskType, UBOSet::Mask>) {
|
||||
descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
} else if constexpr (std::is_same_v<MaskType, SamplerSet::Mask>) {
|
||||
descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
}
|
||||
|
||||
VkDescriptorSetLayoutBinding toBind[MAX_BINDINGS];
|
||||
uint32_t count = 0;
|
||||
for (uint8_t i = 0, maxBindings = sizeof(stageMask) * 4; i < maxBindings; i++) {
|
||||
VkShaderStageFlags stages = 0;
|
||||
if ((stageMask & (VERTEX_STAGE << i)) != 0) {
|
||||
stages |= VK_SHADER_STAGE_VERTEX_BIT;
|
||||
}
|
||||
if ((stageMask & (FRAGMENT_STAGE << i)) == 0) {
|
||||
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
}
|
||||
if (stages == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& bindInfo = toBind[count++];
|
||||
bindInfo = {
|
||||
.binding = i,
|
||||
.descriptorType = descriptorType,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = stages,
|
||||
};
|
||||
}
|
||||
VkDescriptorSetLayoutCreateInfo dlinfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.bindingCount = count,
|
||||
.pBindings = toBind,
|
||||
};
|
||||
|
||||
VkDescriptorSetLayout handle;
|
||||
vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &handle);
|
||||
mLayouts[stageMask] = handle;
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
private:
|
||||
VkDevice mDevice;
|
||||
LayoutMap<MaskType> mLayouts;
|
||||
};
|
||||
|
||||
using UBOSetLayoutCache = LayoutCache<UBOSet::Mask>;
|
||||
using SamplerSetLayoutCache = LayoutCache<SamplerSet::Mask>;
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
VulkanDescriptorSet::VulkanDescriptorSet(VulkanResourceAllocator* allocator, VkDescriptorSet rawSet,
|
||||
VkDescriptorSetLayout layout, OnRecycle&& onRecycleFn)
|
||||
: VulkanResource(VulkanResourceType::DESCRIPTOR_SET),
|
||||
resources(allocator),
|
||||
vkSet(rawSet),
|
||||
layout(layout),
|
||||
mOnRecycleFn(std::move(onRecycleFn)) {}
|
||||
|
||||
VulkanDescriptorSet::~VulkanDescriptorSet() {
|
||||
if (mOnRecycleFn) {
|
||||
mOnRecycleFn(this);
|
||||
}
|
||||
}
|
||||
|
||||
VulkanDescriptorSet* VulkanDescriptorSet::create(VulkanResourceAllocator* allocator,
|
||||
VkDescriptorSet rawSet, VkDescriptorSetLayout layout, OnRecycle&& onRecycleFn) {
|
||||
auto handle = allocator->allocHandle<VulkanDescriptorSet>();
|
||||
auto set = allocator->construct<VulkanDescriptorSet>(handle, allocator, rawSet, layout,
|
||||
std::move(onRecycleFn));
|
||||
return set;
|
||||
}
|
||||
|
||||
class VulkanDescriptorSetManager::Impl {
|
||||
public:
|
||||
Impl(VkDevice device, VulkanResourceAllocator* allocator)
|
||||
: mDevice(device),
|
||||
mUBOLayoutCache(device),
|
||||
mUBOCache(device, allocator),
|
||||
mSamplerLayoutCache(device),
|
||||
mSamplerCache(device, allocator),
|
||||
mResources(allocator) {}
|
||||
|
||||
void setUniformBufferObject(uint32_t bindingIndex, VulkanBufferObject* bufferObject,
|
||||
VkDeviceSize offset, VkDeviceSize size) noexcept {
|
||||
mUbos.push_back({bindingIndex, bufferObject, offset, size});
|
||||
|
||||
// Between "set" and "commit", we need to ref the buffer object to avoid it being collected.
|
||||
mResources.acquire(bufferObject);
|
||||
}
|
||||
|
||||
void setSamplers(SamplerArray&& samplers) {
|
||||
mSamplers = std::move(samplers);
|
||||
for (auto const& sampler: mSamplers) {
|
||||
mResources.acquire(sampler.texture);
|
||||
}
|
||||
}
|
||||
|
||||
void gc() noexcept {
|
||||
mUBOCache.gc();
|
||||
mSamplerCache.gc();
|
||||
}
|
||||
|
||||
// This will write/update all of the descriptor set (and create a set if a one of the same
|
||||
// layout is not available).
|
||||
void bind(VulkanCommandBuffer* commands, GetPipelineLayoutFunction& getPipelineLayoutFn) {
|
||||
LayoutArray layouts = {VK_NULL_HANDLE, VK_NULL_HANDLE};
|
||||
std::array<VkDescriptorSet, 2> descSets = {VK_NULL_HANDLE, VK_NULL_HANDLE};
|
||||
uint8_t descSetCount = 0;
|
||||
|
||||
VkWriteDescriptorSet descriptorWrites[UNIFORM_BINDING_COUNT + SAMPLER_BINDING_COUNT];
|
||||
VkDescriptorBufferInfo uboWrite[UNIFORM_BINDING_COUNT];
|
||||
VkDescriptorImageInfo samplerWrite[SAMPLER_BINDING_COUNT];
|
||||
uint32_t nwrites = 0;
|
||||
|
||||
UBOSet::Key uboKey = {};
|
||||
auto& uboMask = uboKey.mask;
|
||||
uint8_t uboCount = 0;
|
||||
for (auto const& ubo: mUbos) {
|
||||
auto const& binding = std::get<0>(ubo);
|
||||
|
||||
uboKey.buffers[uboCount] = std::get<1>(ubo)->buffer.getGpuBuffer();
|
||||
uboKey.offsets[uboCount] = std::get<2>(ubo);
|
||||
uboKey.sizes[uboCount] = std::get<3>(ubo);
|
||||
uboCount++;
|
||||
|
||||
// Currently we let ubo be visible in all stages.
|
||||
uboMask |= (VERTEX_STAGE << binding);
|
||||
uboMask |= (FRAGMENT_STAGE << binding);
|
||||
}
|
||||
|
||||
if (uboMask) {
|
||||
layouts[UBO_SET_INDEX] = uboKey.layout = mUBOLayoutCache.getLayout(uboMask);
|
||||
|
||||
auto [descriptorSet, cached] = mUBOCache.obtainSet(uboKey);
|
||||
descSets[descSetCount++] = descriptorSet->vkSet;
|
||||
|
||||
// We need to write to the descriptor set since it wasn't cached.
|
||||
if (!cached) {
|
||||
// If it wasn't cached before, we need to ref the resources that it touches.
|
||||
for (auto const& ubo: mUbos) {
|
||||
auto const buffer = std::get<1>(ubo);
|
||||
descriptorSet->resources.acquire(buffer);
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < uboCount; ++i) {
|
||||
auto const& binding = std::get<0>(mUbos[i]);
|
||||
VkWriteDescriptorSet& descriptorWrite = descriptorWrites[nwrites++];
|
||||
auto& writeInfo = uboWrite[i];
|
||||
writeInfo = {
|
||||
.buffer = uboKey.buffers[i],
|
||||
.offset = uboKey.offsets[i],
|
||||
.range = uboKey.sizes[i],
|
||||
};
|
||||
descriptorWrite = {
|
||||
.dstSet = descriptorSet->vkSet,
|
||||
.dstBinding = binding,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
||||
.pBufferInfo = &writeInfo,
|
||||
};
|
||||
}
|
||||
}
|
||||
commands->acquire(descriptorSet);
|
||||
}
|
||||
|
||||
SamplerSet::Key samplerKey = {};
|
||||
auto& samplerMask = samplerKey.mask;
|
||||
uint8_t samplerCount = 0;
|
||||
for (auto const sampler: mSamplers) {
|
||||
samplerMask |= (sampler.stage << sampler.binding);
|
||||
|
||||
samplerKey.sampler[samplerCount] = sampler.info.sampler;
|
||||
samplerKey.imageView[samplerCount] = sampler.info.imageView;
|
||||
samplerKey.imageLayout[samplerCount] = sampler.info.imageLayout;
|
||||
samplerCount++;
|
||||
}
|
||||
|
||||
if (samplerMask) {
|
||||
layouts[SAMPLER_SET_INDEX] = samplerKey.layout =
|
||||
mSamplerLayoutCache.getLayout(samplerMask);
|
||||
|
||||
auto [descriptorSet, cached] = mSamplerCache.obtainSet(samplerKey);
|
||||
descSets[descSetCount++] = descriptorSet->vkSet;
|
||||
|
||||
if (!cached) {
|
||||
for (auto const& sampler: mSamplers) {
|
||||
descriptorSet->resources.acquire(sampler.texture);
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < samplerCount; ++i) {
|
||||
auto const& binding = mSamplers[i].binding;
|
||||
VkWriteDescriptorSet& descriptorWrite = descriptorWrites[nwrites++];
|
||||
auto& writeInfo = samplerWrite[i];
|
||||
writeInfo = mSamplers[i].info;
|
||||
descriptorWrite = {
|
||||
.dstSet = descriptorSet->vkSet,
|
||||
.dstBinding = binding,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.pImageInfo = &writeInfo,
|
||||
};
|
||||
}
|
||||
}
|
||||
commands->acquire(descriptorSet);
|
||||
}
|
||||
if (nwrites) {
|
||||
vkUpdateDescriptorSets(mDevice, nwrites, descriptorWrites, 0, nullptr);
|
||||
}
|
||||
|
||||
VkPipelineLayout const pipelineLayout = getPipelineLayoutFn(layouts);
|
||||
VkCommandBuffer const cmdbuffer = commands->buffer();
|
||||
|
||||
BoundState state {
|
||||
.cmdbuf = cmdbuffer,
|
||||
.pipelineLayout = pipelineLayout,
|
||||
};
|
||||
|
||||
if (state == mPreviousBoundState) {
|
||||
return;
|
||||
}
|
||||
|
||||
vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0,
|
||||
descSetCount, descSets.data(), 0, nullptr);
|
||||
|
||||
mPreviousBoundState = state;
|
||||
|
||||
// Once bound, the resources are now ref'd in the descriptor set and the references in this
|
||||
// class can be released and the descriptor set is ref'd by the command buffer.
|
||||
mResources.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
struct BoundState {
|
||||
VkCommandBuffer cmdbuf;
|
||||
VkPipelineLayout pipelineLayout;
|
||||
|
||||
bool operator==(BoundState const& b) const {
|
||||
return cmdbuf == b.cmdbuf && pipelineLayout == b.pipelineLayout;
|
||||
}
|
||||
};
|
||||
|
||||
VkDevice mDevice;
|
||||
|
||||
UBOSetLayoutCache mUBOLayoutCache;
|
||||
UBOSetCache mUBOCache;
|
||||
|
||||
SamplerSetLayoutCache mSamplerLayoutCache;
|
||||
SamplerSetCache mSamplerCache;
|
||||
|
||||
std::vector<std::tuple<uint32_t, VulkanBufferObject*, VkDeviceSize, VkDeviceSize>> mUbos;
|
||||
SamplerArray mSamplers;
|
||||
VulkanAcquireOnlyResourceManager mResources;
|
||||
|
||||
BoundState mPreviousBoundState;
|
||||
};
|
||||
|
||||
VulkanDescriptorSetManager::VulkanDescriptorSetManager(VkDevice device,
|
||||
VulkanResourceAllocator* resourceAllocator)
|
||||
: mImpl(new Impl(device, resourceAllocator)) {}
|
||||
|
||||
void VulkanDescriptorSetManager::terminate() noexcept {
|
||||
assert_invariant(mImpl);
|
||||
delete mImpl;
|
||||
mImpl = nullptr;
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetManager::gc() noexcept { mImpl->gc(); }
|
||||
|
||||
void VulkanDescriptorSetManager::bind(VulkanCommandBuffer* commands,
|
||||
GetPipelineLayoutFunction& getPipelineLayoutFn) {
|
||||
mImpl->bind(commands, getPipelineLayoutFn);
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetManager::setUniformBufferObject(uint32_t bindingIndex,
|
||||
VulkanBufferObject* bufferObject, VkDeviceSize offset, VkDeviceSize size) noexcept {
|
||||
mImpl->setUniformBufferObject(bindingIndex, bufferObject, offset, size);
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetManager::setSamplers(SamplerArray&& samplers) {
|
||||
mImpl->setSamplers(std::move(samplers));
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSET_H
|
||||
#define TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSET_H
|
||||
|
||||
#include <vulkan/VulkanResourceAllocator.h>
|
||||
#include <vulkan/VulkanTexture.h>
|
||||
#include <vulkan/VulkanUtility.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Program.h>
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
|
||||
#include <utils/bitset.h>
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
// We need to make this class public to enable allocation on the HandleAllocator.
|
||||
struct VulkanDescriptorSet : public VulkanResourceBase {
|
||||
public:
|
||||
// Because we need to recycle descriptor set not used, we allow for a callback that the "Pool"
|
||||
// can use to repackage the vk handle.
|
||||
using OnRecycle = std::function<void(VulkanDescriptorSet*)>;
|
||||
|
||||
VulkanDescriptorSet(VulkanResourceAllocator* allocator, VkDescriptorSet rawSet,
|
||||
VkDescriptorSetLayout layout, OnRecycle&& onRecycleFn);
|
||||
|
||||
~VulkanDescriptorSet();
|
||||
|
||||
static VulkanDescriptorSet* create(VulkanResourceAllocator* allocator, VkDescriptorSet rawSet,
|
||||
VkDescriptorSetLayout layout, OnRecycle&& onRecycleFn);
|
||||
|
||||
// TODO: maybe change to fixed size for performance.
|
||||
VulkanAcquireOnlyResourceManager resources;
|
||||
|
||||
VkDescriptorSet const vkSet;
|
||||
VkDescriptorSetLayout const layout;
|
||||
|
||||
private:
|
||||
OnRecycle mOnRecycleFn;
|
||||
};
|
||||
|
||||
// Abstraction over the pool and the layout cache.
|
||||
class VulkanDescriptorSetManager {
|
||||
public:
|
||||
using StageBitMask = uint32_t;
|
||||
using UniformBufferBitmask = StageBitMask;
|
||||
using SamplerBitmask = StageBitMask;
|
||||
|
||||
static constexpr uint8_t CONCURRENT_DESCRIPTOR_SET_COUNT = 2;// UBO and samplers
|
||||
static constexpr uint8_t MAX_SUPPORTED_SHADER_STAGE = 2; // Vertex and fragment.
|
||||
|
||||
static_assert(sizeof(UniformBufferBitmask) * 8 >=
|
||||
(Program::UNIFORM_BINDING_COUNT) *MAX_SUPPORTED_SHADER_STAGE);
|
||||
static_assert(sizeof(SamplerBitmask) * 8 >=
|
||||
Program::SAMPLER_BINDING_COUNT * MAX_SUPPORTED_SHADER_STAGE);
|
||||
|
||||
static constexpr StageBitMask VERTEX_STAGE = 0x1;
|
||||
static constexpr StageBitMask FRAGMENT_STAGE = (0x1 << (sizeof(StageBitMask) / 4));
|
||||
static constexpr uint8_t UBO_SET_INDEX = 0;
|
||||
static constexpr uint8_t SAMPLER_SET_INDEX = 1;
|
||||
|
||||
using LayoutArray = std::array<VkDescriptorSetLayout, CONCURRENT_DESCRIPTOR_SET_COUNT>;
|
||||
struct SamplerBundle {
|
||||
VkDescriptorImageInfo info = {};
|
||||
VulkanTexture* texture = nullptr;
|
||||
uint8_t binding = 0;
|
||||
SamplerBitmask stage = 0;
|
||||
};
|
||||
using SamplerArray = CappedArray<SamplerBundle, Program::SAMPLER_BINDING_COUNT>;
|
||||
using GetPipelineLayoutFunction = std::function<VkPipelineLayout(LayoutArray)>;
|
||||
|
||||
VulkanDescriptorSetManager(VkDevice device, VulkanResourceAllocator* resourceAllocator);
|
||||
|
||||
void terminate() noexcept;
|
||||
|
||||
void gc() noexcept;
|
||||
|
||||
// This will write/update all of the descriptor set.
|
||||
void bind(VulkanCommandBuffer* commands, GetPipelineLayoutFunction& getPipelineFn);
|
||||
|
||||
void setUniformBufferObject(uint32_t bindingIndex, VulkanBufferObject* bufferObject,
|
||||
VkDeviceSize offset, VkDeviceSize size) noexcept;
|
||||
|
||||
void setSamplers(SamplerArray&& samplers);
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
Impl* mImpl;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif// TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSET_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
|
||||
#define TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
|
||||
|
||||
#include <vulkan/VulkanResourceAllocator.h>
|
||||
#include <vulkan/VulkanTexture.h>
|
||||
#include <vulkan/VulkanUtility.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Program.h>
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
|
||||
#include <utils/bitset.h>
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
using namespace descset;
|
||||
|
||||
// [GDSR]: Great-Descriptor-Set-Refactor: As of 03/20/24, the Filament frontend is planning to
|
||||
// introduce descriptor set. This PR will arrive before that change is complete. As such, some of
|
||||
// the methods introduced here will be obsolete, and certain logic will be generalized.
|
||||
|
||||
// Abstraction over the pool and the layout cache.
|
||||
class VulkanDescriptorSetManager {
|
||||
public:
|
||||
static constexpr uint8_t UNIQUE_DESCRIPTOR_SET_COUNT =
|
||||
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT;
|
||||
using GetPipelineLayoutFunction =
|
||||
std::function<VkPipelineLayout(VulkanDescriptorSetLayoutList const&)>;
|
||||
|
||||
VulkanDescriptorSetManager(VkDevice device, VulkanResourceAllocator* resourceAllocator);
|
||||
|
||||
void terminate() noexcept;
|
||||
|
||||
void gc();
|
||||
|
||||
// TODO: Obsolete after [GDSR].
|
||||
// This will write/update/bind all of the descriptor set. After [GDSR], the binding for
|
||||
// descriptor sets will not depend on the layout described in the program.
|
||||
VkPipelineLayout bind(VulkanCommandBuffer* commands, VulkanProgram* program,
|
||||
GetPipelineLayoutFunction& getPipelineLayoutFn);
|
||||
|
||||
// TODO: Obsolete after [GDSR].
|
||||
// This is to "dynamically" bind UBOs that might have offsets changed between pipeline binding
|
||||
// and the draw call. We do this because UBOs for primitives that are part of the same
|
||||
// renderable can be stored within one buffer. This can be a no-op if there were no range
|
||||
// changes between the pipeline bind and the draw call. We will re-use applicable states
|
||||
// provided within the bind() call, including the UBO descriptor set layout. TODO: make it a
|
||||
// proper dynamic binding when Filament-side descriptor changes are completed.
|
||||
void dynamicBind(VulkanCommandBuffer* commands, Handle<VulkanDescriptorSetLayout> uboLayout);
|
||||
|
||||
Handle<VulkanDescriptorSetLayout> createLayout(descset::DescriptorSetLayout const& layout);
|
||||
|
||||
void destroyLayout(Handle<VulkanDescriptorSetLayout> layout);
|
||||
|
||||
void updateBuffer(Handle<VulkanDescriptorSet> set, uint8_t binding,
|
||||
VulkanBufferObject* bufferObject, VkDeviceSize offset, VkDeviceSize size) noexcept;
|
||||
|
||||
void updateSampler(Handle<VulkanDescriptorSet> set, uint8_t binding,
|
||||
VulkanTexture* texture, VkSampler sampler) noexcept;
|
||||
|
||||
void updateInputAttachment(Handle<VulkanDescriptorSet> set, VulkanAttachment attachment) noexcept;
|
||||
|
||||
void clearBuffer(uint32_t bindingIndex);
|
||||
|
||||
void setPlaceHolders(VkSampler sampler, VulkanTexture* texture,
|
||||
VulkanBufferObject* bufferObject) noexcept;
|
||||
|
||||
void clearState() noexcept;
|
||||
|
||||
// TODO: To be completed after [GDSR]
|
||||
Handle<VulkanDescriptorSet> createSet(Handle<VulkanDescriptorSetLayout> layout) {
|
||||
return Handle<VulkanDescriptorSet>();
|
||||
}
|
||||
|
||||
// TODO: To be completed after [GDSR]
|
||||
void destroySet(Handle<VulkanDescriptorSet> set) {}
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
Impl* mImpl;
|
||||
};
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
#endif// TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "VulkanPipelineLayoutCache.h"
|
||||
|
||||
#include <vulkan/VulkanResourceAllocator.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
|
||||
VulkanDescriptorSetLayoutList const& descriptorSetLayouts) {
|
||||
PipelineLayoutKey key = {VK_NULL_HANDLE};
|
||||
uint8_t descSetLayoutCount = 0;
|
||||
for (auto layoutHandle : descriptorSetLayouts) {
|
||||
if (layoutHandle) {
|
||||
auto layout = mAllocator->handle_cast<VulkanDescriptorSetLayout*>(layoutHandle);
|
||||
key[descSetLayoutCount++] = layout->vklayout;
|
||||
}
|
||||
}
|
||||
|
||||
auto iter = mPipelineLayouts.find(key);
|
||||
if (iter != mPipelineLayouts.end()) {
|
||||
PipelineLayoutCacheEntry& entry = mPipelineLayouts[key];
|
||||
entry.lastUsed = mTimestamp++;
|
||||
return entry.handle;
|
||||
}
|
||||
|
||||
VkPipelineLayoutCreateInfo info{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.setLayoutCount = (uint32_t) descSetLayoutCount,
|
||||
.pSetLayouts = key.data(),
|
||||
.pushConstantRangeCount = 0,
|
||||
};
|
||||
VkPipelineLayout layout;
|
||||
vkCreatePipelineLayout(mDevice, &info, VKALLOC, &layout);
|
||||
|
||||
mPipelineLayouts[key] = {
|
||||
.handle = layout,
|
||||
.lastUsed = mTimestamp++,
|
||||
};
|
||||
return layout;
|
||||
}
|
||||
|
||||
}// namespace filament::backend
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANPIPELINELAYOUTCACHE_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANPIPELINELAYOUTCACHE_H
|
||||
|
||||
#include <vulkan/VulkanHandles.h>
|
||||
#include <bluevk/BlueVK.h>
|
||||
|
||||
#include <utils/Hash.h>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class VulkanPipelineLayoutCache {
|
||||
public:
|
||||
VulkanPipelineLayoutCache(VkDevice device, VulkanResourceAllocator* allocator)
|
||||
: mDevice(device),
|
||||
mAllocator(allocator),
|
||||
mTimestamp(0) {}
|
||||
|
||||
using PipelineLayoutKey = std::array<VkDescriptorSetLayout,
|
||||
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
|
||||
|
||||
VulkanPipelineLayoutCache(VulkanPipelineLayoutCache const&) = delete;
|
||||
VulkanPipelineLayoutCache& operator=(VulkanPipelineLayoutCache const&) = delete;
|
||||
|
||||
VkPipelineLayout getLayout(VulkanDescriptorSetLayoutList const& descriptorSetLayouts);
|
||||
|
||||
private:
|
||||
using Timestamp = uint64_t;
|
||||
struct PipelineLayoutCacheEntry {
|
||||
VkPipelineLayout handle;
|
||||
Timestamp lastUsed;
|
||||
};
|
||||
|
||||
using PipelineLayoutKeyHashFn = utils::hash::MurmurHashFn<PipelineLayoutKey>;
|
||||
struct PipelineLayoutKeyEqual {
|
||||
bool operator()(PipelineLayoutKey const& k1, PipelineLayoutKey const& k2) const {
|
||||
return 0 == memcmp((const void*) &k1, (const void*) &k2, sizeof(PipelineLayoutKey));
|
||||
}
|
||||
};
|
||||
|
||||
using PipelineLayoutMap = tsl::robin_map<PipelineLayoutKey, PipelineLayoutCacheEntry,
|
||||
PipelineLayoutKeyHashFn, PipelineLayoutKeyEqual>;
|
||||
|
||||
VkDevice mDevice;
|
||||
VulkanResourceAllocator* mAllocator;
|
||||
Timestamp mTimestamp;
|
||||
PipelineLayoutMap mPipelineLayouts;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_VULKANPIPELINECACHE_H
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -72,7 +73,7 @@ void workaroundSpecConstant(Program::ShaderBlob const& blob,
|
||||
std::vector<uint32_t>& output) {
|
||||
using WordMap = std::unordered_map<uint32_t, uint32_t>;
|
||||
using SpecValueMap = std::unordered_map<uint32_t, SpecConstantValue>;
|
||||
constexpr size_t const HEADER_SIZE = 5;
|
||||
constexpr size_t HEADER_SIZE = 5;
|
||||
|
||||
WordMap varToIdMap;
|
||||
SpecValueMap idToValue;
|
||||
@@ -145,4 +146,61 @@ void workaroundSpecConstant(Program::ShaderBlob const& blob,
|
||||
output.resize(outputCursor);
|
||||
}
|
||||
|
||||
std::tuple<uint32_t,uint32_t, uint32_t> getProgramBindings(Program::ShaderBlob const& blob) {
|
||||
std::unordered_map<uint32_t, uint32_t> targetToSet, targetToBinding;
|
||||
|
||||
constexpr size_t HEADER_SIZE = 5;
|
||||
|
||||
size_t const dataSize = blob.size() / 4;
|
||||
uint32_t const* data = (uint32_t*) blob.data();
|
||||
|
||||
for (uint32_t cursor = HEADER_SIZE, cursorEnd = dataSize; cursor < cursorEnd;) {
|
||||
uint32_t const firstWord = data[cursor];
|
||||
uint32_t const wordCount = firstWord >> 16;
|
||||
uint32_t const op = firstWord & 0x0000FFFF;
|
||||
|
||||
switch(op) {
|
||||
case spv::Op::OpDecorate: {
|
||||
if (data[cursor + 2] == spv::Decoration::DecorationDescriptorSet) {
|
||||
uint32_t const targetVar = data[cursor + 1];
|
||||
uint32_t const setId = data[cursor + 3];
|
||||
targetToSet[targetVar] = setId;
|
||||
} else if (data[cursor + 2] == spv::Decoration::DecorationBinding) {
|
||||
uint32_t const targetVar = data[cursor + 1];
|
||||
uint32_t const binding = data[cursor + 3];
|
||||
targetToBinding[targetVar] = binding;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
cursor += wordCount;
|
||||
}
|
||||
|
||||
constexpr uint32_t UBO = 0;
|
||||
constexpr uint32_t SAMPLER = 1;
|
||||
constexpr uint32_t IATTACHMENT = 2;
|
||||
uint32_t ubo = 0, sampler = 0, inputAttachment = 0;
|
||||
|
||||
for (auto const [target, setId]: targetToSet) {
|
||||
uint32_t const binding = targetToBinding[target];
|
||||
assert_invariant(binding < 32);
|
||||
switch(setId) {
|
||||
case UBO:
|
||||
ubo |= (1 << binding);
|
||||
break;
|
||||
case SAMPLER:
|
||||
sampler |= (1 << binding);
|
||||
break;
|
||||
case IATTACHMENT:
|
||||
inputAttachment |= (1 << binding);
|
||||
break;
|
||||
default:
|
||||
PANIC_POSTCONDITION("unexpected %d", (int) setId);
|
||||
}
|
||||
}
|
||||
return {ubo, sampler, inputAttachment};
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -17,10 +17,11 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANSPIRVUTILS_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANSPIRVUTILS_H
|
||||
|
||||
#include "backend/Program.h"
|
||||
#include <backend/Program.h>
|
||||
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
namespace filament::backend {
|
||||
@@ -42,6 +43,9 @@ void workaroundSpecConstant(Program::ShaderBlob const& blob,
|
||||
utils::FixedCapacityVector<Program::SpecializationConstant> const& specConstants,
|
||||
std::vector<uint32_t>& output);
|
||||
|
||||
// bindings for UBO, samplers, input attachment
|
||||
std::tuple<uint32_t, uint32_t, uint32_t> getProgramBindings(Program::ShaderBlob const& blob);
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_VULKANSPIRVUTILS_H
|
||||
|
||||
@@ -277,6 +277,16 @@ bool MaterialParser::getBlendingMode(BlendingMode* value) const noexcept {
|
||||
return mImpl.getFromSimpleChunk(ChunkType::MaterialBlendingMode, reinterpret_cast<uint8_t*>(value));
|
||||
}
|
||||
|
||||
bool MaterialParser::getCustomBlendFunction(std::array<BlendFunction, 4>* value) const noexcept {
|
||||
uint32_t blendFunctions = 0;
|
||||
bool const result = mImpl.getFromSimpleChunk(ChunkType::MaterialBlendFunction, &blendFunctions);
|
||||
(*value)[0] = BlendFunction((blendFunctions >> 24) & 0xFF);
|
||||
(*value)[1] = BlendFunction((blendFunctions >> 16) & 0xFF);
|
||||
(*value)[2] = BlendFunction((blendFunctions >> 8) & 0xFF);
|
||||
(*value)[3] = BlendFunction((blendFunctions >> 0) & 0xFF);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MaterialParser::getMaskThreshold(float* value) const noexcept {
|
||||
return mImpl.getFromSimpleChunk(ChunkType::MaterialMaskThreshold, value);
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@ public:
|
||||
|
||||
bool getShading(Shading*) const noexcept;
|
||||
bool getBlendingMode(BlendingMode*) const noexcept;
|
||||
bool getCustomBlendFunction(std::array<backend::BlendFunction, 4>*) const noexcept;
|
||||
bool getMaskThreshold(float*) const noexcept;
|
||||
bool getAlphaToCoverageSet(bool*) const noexcept;
|
||||
bool getAlphaToCoverage(bool*) const noexcept;
|
||||
|
||||
@@ -450,11 +450,8 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
|
||||
.levels = uint8_t(levelCount),
|
||||
.format = isES2 ? TextureFormat::DEPTH24 : TextureFormat::DEPTH32F });
|
||||
|
||||
// workaround: since we have levels, this implies SAMPLEABLE (because of the gl
|
||||
// backend, which implements non-sampleables with renderbuffers, which don't have levels).
|
||||
// (should the gl driver revert to textures, in that case?)
|
||||
data.depth = builder.write(data.depth,
|
||||
FrameGraphTexture::Usage::DEPTH_ATTACHMENT | FrameGraphTexture::Usage::SAMPLEABLE);
|
||||
FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
|
||||
|
||||
if (config.picking) {
|
||||
data.picking = builder.createTexture("Picking Buffer", {
|
||||
|
||||
@@ -16,17 +16,35 @@
|
||||
|
||||
#include "RendererUtils.h"
|
||||
|
||||
#include "PostProcessManager.h"
|
||||
|
||||
#include "details/Engine.h"
|
||||
#include "details/View.h"
|
||||
|
||||
#include "fg/FrameGraph.h"
|
||||
#include "fg/FrameGraphId.h"
|
||||
#include "fg/FrameGraphResources.h"
|
||||
#include "fg/FrameGraphTexture.h"
|
||||
|
||||
#include <filament/Options.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/Viewport.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
#include <backend/PixelBufferDescriptor.h>
|
||||
|
||||
#include <utils/BitmaskEnum.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
using namespace backend;
|
||||
@@ -153,16 +171,9 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
|
||||
data.depth = builder.read(data.depth, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
|
||||
|
||||
data.color = builder.write(data.color, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
|
||||
data.depth = builder.write(data.depth, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
|
||||
if (engine.getConfig().stereoscopicType == StereoscopicType::MULTIVIEW) {
|
||||
// Add sampleable usage flag for depth in multiview rendering, othewise it's
|
||||
// treated as renderbuffer in the backend and crashed.
|
||||
data.depth = builder.write(data.depth,
|
||||
FrameGraphTexture::Usage::DEPTH_ATTACHMENT |
|
||||
FrameGraphTexture::Usage::SAMPLEABLE);
|
||||
layerCount = engine.getConfig().stereoscopicEyeCount;
|
||||
} else {
|
||||
data.depth = builder.write(data.depth,
|
||||
FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+127
-101
@@ -63,8 +63,7 @@ namespace filament {
|
||||
using namespace backend;
|
||||
using namespace math;
|
||||
|
||||
ShadowMapManager::ShadowMapManager(FEngine& engine)
|
||||
: mEngine(engine) {
|
||||
ShadowMapManager::ShadowMapManager(FEngine& engine) {
|
||||
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
|
||||
debugRegistry.registerProperty("d.shadowmap.visualize_cascades",
|
||||
&engine.debug.shadowmap.visualize_cascades);
|
||||
@@ -273,9 +272,26 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
|
||||
if (!spotShadowCastersRange.empty()) {
|
||||
for (auto& shadowMap : getSpotShadowMaps()) {
|
||||
assert_invariant(!shadowMap.isDirectionalShadow());
|
||||
passList.push_back({
|
||||
{}, &shadowMap, spotShadowCastersRange,
|
||||
VISIBLE_DYN_SHADOW_RENDERABLE });
|
||||
|
||||
switch (shadowMap.getShadowType()) {
|
||||
case ShadowType::DIRECTIONAL:
|
||||
// we should never be here
|
||||
break;
|
||||
case ShadowType::SPOT:
|
||||
prepareSpotShadowMap(shadowMap, engine, view, mainCameraInfo,
|
||||
scene->getLightData(), mSceneInfo);
|
||||
break;
|
||||
case ShadowType::POINT:
|
||||
preparePointShadowMap(shadowMap, engine, view, mainCameraInfo,
|
||||
scene->getLightData());
|
||||
break;
|
||||
}
|
||||
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
passList.push_back({
|
||||
{}, &shadowMap, spotShadowCastersRange,
|
||||
VISIBLE_DYN_SHADOW_RENDERABLE });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,6 +305,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
|
||||
scene, mainCameraInfo, userTime, passBuilder = passBuilder](
|
||||
FrameGraphResources const&, auto const& data, DriverApi& driver) mutable {
|
||||
|
||||
|
||||
// Note: we could almost parallel_for the loop below, the problem currently is
|
||||
// that updatePrimitivesLod() updates temporary global state.
|
||||
// prepareSpotShadowMap() also update the visibility of renderable. These two
|
||||
@@ -297,53 +314,54 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
|
||||
|
||||
// Generate a RenderPass for each shadow map
|
||||
for (auto const& entry : data.passList) {
|
||||
ShadowMap& shadowMap = *entry.shadowMap;
|
||||
ShadowMap const& shadowMap = *entry.shadowMap;
|
||||
assert_invariant(shadowMap.hasVisibleShadows());
|
||||
|
||||
// Note: this loop can generate a lot of commands that come out of the
|
||||
// "per frame command arena". The allocation persists until the
|
||||
// end of the frame.
|
||||
// One way to possibly mitigate this, would be to always use the
|
||||
// same command buffer for all shadow map, but then we'd generate
|
||||
// a lot of unneeded draw calls.
|
||||
// To do this efficiently, we'd need a way to cull draw calls already
|
||||
// recorded in the command buffer, per shadow map.
|
||||
|
||||
// Note: the output of culling below is stored in scene->getRenderableData()
|
||||
|
||||
// for spot shadow map, we need to do the culling
|
||||
switch (shadowMap.getShadowType()) {
|
||||
case ShadowType::DIRECTIONAL:
|
||||
// we should never be here
|
||||
break;
|
||||
case ShadowType::SPOT:
|
||||
prepareSpotShadowMap(shadowMap, engine, view, mainCameraInfo,
|
||||
ShadowMapManager::cullSpotShadowMap(shadowMap, engine, view,
|
||||
scene->getRenderableData(), entry.range,
|
||||
scene->getLightData(), mSceneInfo);
|
||||
scene->getLightData());
|
||||
break;
|
||||
case ShadowType::POINT:
|
||||
preparePointShadowMap(shadowMap, engine, view, mainCameraInfo,
|
||||
ShadowMapManager::cullPointShadowMap(shadowMap, view,
|
||||
scene->getRenderableData(), entry.range,
|
||||
scene->getLightData(), mSceneInfo);
|
||||
scene->getLightData());
|
||||
break;
|
||||
}
|
||||
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
// Note: this loop can generate a lot of commands that come out of the
|
||||
// "per frame command arena". The allocation persists until the
|
||||
// end of the frame.
|
||||
// One way to possibly mitigate this, would be to always use the
|
||||
// same command buffer for all shadow map, but then we'd generate
|
||||
// a lot of unneeded draw calls.
|
||||
// To do this efficiently, we'd need a way to cull draw calls already
|
||||
// recorded in the command buffer, per shadow map.
|
||||
// cameraInfo only valid after calling update
|
||||
const CameraInfo cameraInfo{ shadowMap.getCamera(), mainCameraInfo };
|
||||
|
||||
auto transaction = ShadowMap::open(driver);
|
||||
ShadowMap::prepareCamera(transaction, engine, cameraInfo);
|
||||
ShadowMap::prepareViewport(transaction, shadowMap.getViewport());
|
||||
ShadowMap::prepareTime(transaction, engine, userTime);
|
||||
ShadowMap::prepareShadowMapping(transaction,
|
||||
vsmShadowOptions.highPrecision);
|
||||
shadowMap.commit(transaction, driver);
|
||||
|
||||
// cameraInfo only valid after calling update
|
||||
const CameraInfo cameraInfo{ shadowMap.getCamera(), mainCameraInfo };
|
||||
// updatePrimitivesLod must be run before RenderPass::appendCommands.
|
||||
view.updatePrimitivesLod(engine,
|
||||
cameraInfo, scene->getRenderableData(), entry.range);
|
||||
|
||||
auto transaction = ShadowMap::open(driver);
|
||||
ShadowMap::prepareCamera(transaction, engine, cameraInfo);
|
||||
ShadowMap::prepareViewport(transaction, shadowMap.getViewport());
|
||||
ShadowMap::prepareTime(transaction, engine, userTime);
|
||||
ShadowMap::prepareShadowMapping(transaction,
|
||||
vsmShadowOptions.highPrecision);
|
||||
shadowMap.commit(transaction, driver);
|
||||
// generate and sort the commands for rendering the shadow map
|
||||
|
||||
// updatePrimitivesLod must be run before RenderPass::appendCommands.
|
||||
view.updatePrimitivesLod(engine,
|
||||
cameraInfo, scene->getRenderableData(), entry.range);
|
||||
|
||||
// generate and sort the commands for rendering the shadow map
|
||||
|
||||
RenderPass const pass = passBuilder
|
||||
RenderPass const pass = passBuilder
|
||||
.camera(cameraInfo)
|
||||
.visibilityMask(entry.visibilityMask)
|
||||
.geometry(scene->getRenderableData(),
|
||||
@@ -351,16 +369,15 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
|
||||
.commandTypeFlags(RenderPass::CommandTypeFlags::SHADOW)
|
||||
.build(engine);
|
||||
|
||||
entry.executor = pass.getExecutor();
|
||||
entry.executor = pass.getExecutor();
|
||||
|
||||
if (!view.hasVSM()) {
|
||||
auto const* options = shadowMap.getShadowOptions();
|
||||
PolygonOffset const polygonOffset = { // handle reversed Z
|
||||
.slope = -options->polygonOffsetSlope,
|
||||
.constant = -options->polygonOffsetConstant
|
||||
};
|
||||
entry.executor.overridePolygonOffset(&polygonOffset);
|
||||
}
|
||||
if (!view.hasVSM()) {
|
||||
auto const* options = shadowMap.getShadowOptions();
|
||||
PolygonOffset const polygonOffset = { // handle reversed Z
|
||||
.slope = -options->polygonOffsetSlope,
|
||||
.constant = -options->polygonOffsetConstant
|
||||
};
|
||||
entry.executor.overridePolygonOffset(&polygonOffset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,10 +402,8 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
|
||||
|
||||
auto const& passList = prepareShadowPass.getData().passList;
|
||||
for (auto const& entry: passList) {
|
||||
if (!entry.shadowMap->hasVisibleShadows()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
assert_invariant(entry.shadowMap->hasVisibleShadows());
|
||||
|
||||
const uint8_t layer = entry.shadowMap->getLayer();
|
||||
const auto* options = entry.shadowMap->getShadowOptions();
|
||||
const auto msaaSamples = textureRequirements.msaaSamples;
|
||||
@@ -615,7 +630,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
// Compute scene-dependent values shared across all cascades
|
||||
ShadowMap::updateSceneInfoDirectional(MvAtOrigin, *scene, sceneInfo);
|
||||
|
||||
shadowMap.updateDirectional(mEngine,
|
||||
shadowMap.updateDirectional(engine,
|
||||
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo);
|
||||
|
||||
hasVisibleShadows = shadowMap.hasVisibleShadows();
|
||||
@@ -681,7 +696,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
|
||||
sceneInfo.csNearFar = { csSplitPosition[i], csSplitPosition[i + 1] };
|
||||
|
||||
auto shaderParameters = shadowMap.updateDirectional(mEngine,
|
||||
auto shaderParameters = shadowMap.updateDirectional(engine,
|
||||
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo);
|
||||
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
@@ -757,15 +772,58 @@ void ShadowMapManager::updateSpotVisibilityMasks(
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap,
|
||||
FEngine& engine, FView& view, CameraInfo const& mainCameraInfo,
|
||||
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> range,
|
||||
void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap, FEngine& engine, FView& view,
|
||||
CameraInfo const& mainCameraInfo,
|
||||
FScene::LightSoa& lightData, ShadowMap::SceneInfo const& sceneInfo) noexcept {
|
||||
|
||||
const size_t lightIndex = shadowMap.getLightIndex();
|
||||
FLightManager::ShadowOptions const* const options = shadowMap.getShadowOptions();
|
||||
|
||||
// update the shadow map frustum/camera
|
||||
const ShadowMap::ShadowMapInfo shadowMapInfo{
|
||||
.atlasDimension = mTextureAtlasRequirements.size,
|
||||
.textureDimension = uint16_t(options->mapSize),
|
||||
.shadowDimension = uint16_t(options->mapSize - 2u),
|
||||
.textureSpaceFlipped = engine.getBackend() == Backend::METAL ||
|
||||
engine.getBackend() == Backend::VULKAN,
|
||||
.vsm = view.hasVSM()
|
||||
};
|
||||
|
||||
auto shaderParameters = shadowMap.updateSpot(engine,
|
||||
lightData, lightIndex, mainCameraInfo, shadowMapInfo, *view.getScene(), sceneInfo);
|
||||
|
||||
// and if we need to generate it, update all the UBO data
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
const size_t shadowIndex = shadowMap.getShadowIndex();
|
||||
const float wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
|
||||
// note: normalBias is set to zero for VSM
|
||||
const float normalBias = shadowMapInfo.vsm ? 0.0f : options->normalBias;
|
||||
|
||||
auto& s = mShadowUb.edit();
|
||||
const double n = shadowMap.getCamera().getNear();
|
||||
const double f = shadowMap.getCamera().getCullingFar();
|
||||
s.shadows[shadowIndex].layer = shadowMap.getLayer();
|
||||
s.shadows[shadowIndex].lightFromWorldMatrix = shaderParameters.lightSpace;
|
||||
s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized;
|
||||
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter;
|
||||
s.shadows[shadowIndex].lightFromWorldZ = shaderParameters.lightFromWorldZ;
|
||||
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSizeAtOneMeter;
|
||||
s.shadows[shadowIndex].nearOverFarMinusNear = float(n / (f - n));
|
||||
s.shadows[shadowIndex].elvsm = options->vsm.elvsm;
|
||||
s.shadows[shadowIndex].bulbRadiusLs =
|
||||
mSoftShadowOptions.penumbraScale * options->shadowBulbRadius
|
||||
/ wsTexelSizeAtOneMeter;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowMapManager::cullSpotShadowMap(ShadowMap const& shadowMap, FEngine& engine, FView& view,
|
||||
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> range,
|
||||
FScene::LightSoa& lightData) noexcept {
|
||||
auto& lcm = engine.getLightManager();
|
||||
|
||||
const size_t lightIndex = shadowMap.getLightIndex();
|
||||
const FLightManager::Instance li = lightData.elementAt<FScene::LIGHT_INSTANCE>(lightIndex);
|
||||
FLightManager::ShadowOptions const* const options = shadowMap.getShadowOptions();
|
||||
|
||||
// compute the frustum for this light
|
||||
// for spotlights, we cull shadow casters first because we already know the frustum,
|
||||
@@ -802,19 +860,28 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap,
|
||||
visibility + range.first,
|
||||
visibleArray + range.first,
|
||||
range.size());
|
||||
}
|
||||
|
||||
void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
|
||||
FEngine& engine, FView& view, CameraInfo const& mainCameraInfo,
|
||||
FScene::LightSoa& lightData) noexcept {
|
||||
|
||||
const uint8_t face = shadowMap.getFace();
|
||||
const size_t lightIndex = shadowMap.getLightIndex();
|
||||
FLightManager::ShadowOptions const* const options = shadowMap.getShadowOptions();
|
||||
|
||||
// update the shadow map frustum/camera
|
||||
const ShadowMap::ShadowMapInfo shadowMapInfo{
|
||||
.atlasDimension = mTextureAtlasRequirements.size,
|
||||
.textureDimension = uint16_t(options->mapSize),
|
||||
.shadowDimension = uint16_t(options->mapSize - 2u),
|
||||
.shadowDimension = uint16_t(options->mapSize), // point-lights don't have a border
|
||||
.textureSpaceFlipped = engine.getBackend() == Backend::METAL ||
|
||||
engine.getBackend() == Backend::VULKAN,
|
||||
.vsm = view.hasVSM()
|
||||
};
|
||||
|
||||
auto shaderParameters = shadowMap.updateSpot(mEngine,
|
||||
lightData, lightIndex, mainCameraInfo, shadowMapInfo, *view.getScene(), sceneInfo);
|
||||
auto shaderParameters = shadowMap.updatePoint(engine, lightData, lightIndex,
|
||||
mainCameraInfo, shadowMapInfo, *view.getScene(), face);
|
||||
|
||||
// and if we need to generate it, update all the UBO data
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
@@ -837,19 +904,15 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap,
|
||||
s.shadows[shadowIndex].bulbRadiusLs =
|
||||
mSoftShadowOptions.penumbraScale * options->shadowBulbRadius
|
||||
/ wsTexelSizeAtOneMeter;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
|
||||
FEngine& engine, FView& view, CameraInfo const& mainCameraInfo,
|
||||
void ShadowMapManager::cullPointShadowMap(ShadowMap const& shadowMap, FView& view,
|
||||
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> range,
|
||||
FScene::LightSoa& lightData,
|
||||
ShadowMap::SceneInfo const&) noexcept {
|
||||
FScene::LightSoa& lightData) noexcept {
|
||||
|
||||
const uint8_t face = shadowMap.getFace();
|
||||
const size_t lightIndex = shadowMap.getLightIndex();
|
||||
FLightManager::ShadowOptions const* const options = shadowMap.getShadowOptions();
|
||||
|
||||
// compute the frustum for this light
|
||||
// for spotlights, we cull shadow casters first because we already know the frustum,
|
||||
@@ -883,43 +946,6 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
|
||||
visibility + range.first,
|
||||
visibleArray + range.first,
|
||||
range.size());
|
||||
|
||||
// update the shadow map frustum/camera
|
||||
const ShadowMap::ShadowMapInfo shadowMapInfo{
|
||||
.atlasDimension = mTextureAtlasRequirements.size,
|
||||
.textureDimension = uint16_t(options->mapSize),
|
||||
.shadowDimension = uint16_t(options->mapSize), // point-lights don't have a border
|
||||
.textureSpaceFlipped = engine.getBackend() == Backend::METAL ||
|
||||
engine.getBackend() == Backend::VULKAN,
|
||||
.vsm = view.hasVSM()
|
||||
};
|
||||
|
||||
auto shaderParameters = shadowMap.updatePoint(mEngine, lightData, lightIndex,
|
||||
mainCameraInfo, shadowMapInfo, *view.getScene(), face);
|
||||
|
||||
|
||||
// and if we need to generate it, update all the UBO data
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
const size_t shadowIndex = shadowMap.getShadowIndex();
|
||||
const float wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
|
||||
// note: normalBias is set to zero for VSM
|
||||
const float normalBias = shadowMapInfo.vsm ? 0.0f : options->normalBias;
|
||||
|
||||
auto& s = mShadowUb.edit();
|
||||
const double n = shadowMap.getCamera().getNear();
|
||||
const double f = shadowMap.getCamera().getCullingFar();
|
||||
s.shadows[shadowIndex].layer = shadowMap.getLayer();
|
||||
s.shadows[shadowIndex].lightFromWorldMatrix = shaderParameters.lightSpace;
|
||||
s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized;
|
||||
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter;
|
||||
s.shadows[shadowIndex].lightFromWorldZ = shaderParameters.lightFromWorldZ;
|
||||
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSizeAtOneMeter;
|
||||
s.shadows[shadowIndex].nearOverFarMinusNear = float(n / (f - n));
|
||||
s.shadows[shadowIndex].elvsm = options->vsm.elvsm;
|
||||
s.shadows[shadowIndex].bulbRadiusLs =
|
||||
mSoftShadowOptions.penumbraScale * options->shadowBulbRadius
|
||||
/ wsTexelSizeAtOneMeter;
|
||||
}
|
||||
}
|
||||
|
||||
ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine& engine,
|
||||
|
||||
@@ -160,14 +160,20 @@ private:
|
||||
|
||||
void prepareSpotShadowMap(ShadowMap& shadowMap,
|
||||
FEngine& engine, FView& view, CameraInfo const& mainCameraInfo,
|
||||
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> range,
|
||||
FScene::LightSoa& lightData, ShadowMap::SceneInfo const& sceneInfo) noexcept;
|
||||
|
||||
static void cullSpotShadowMap(ShadowMap const& map,
|
||||
FEngine& engine, FView& view,
|
||||
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> range,
|
||||
FScene::LightSoa& lightData) noexcept;
|
||||
|
||||
void preparePointShadowMap(ShadowMap& map,
|
||||
FEngine& engine, FView& view, CameraInfo const& mainCameraInfo,
|
||||
FScene::LightSoa& lightData) noexcept;
|
||||
|
||||
static void cullPointShadowMap(ShadowMap const& shadowMap, FView& view,
|
||||
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> range,
|
||||
FScene::LightSoa& lightData,
|
||||
ShadowMap::SceneInfo const& sceneInfo) noexcept;
|
||||
FScene::LightSoa& lightData) noexcept;
|
||||
|
||||
static void updateSpotVisibilityMasks(
|
||||
uint8_t visibleLayers,
|
||||
@@ -203,8 +209,6 @@ private:
|
||||
size_t mSplitCount;
|
||||
};
|
||||
|
||||
FEngine& mEngine;
|
||||
|
||||
// Atlas requirements, updated in ShadowMapManager::update(),
|
||||
// consumed in ShadowMapManager::render()
|
||||
struct TextureAtlasRequirements {
|
||||
|
||||
@@ -726,9 +726,10 @@ const FMaterial* FEngine::getSkyboxMaterial() const noexcept {
|
||||
* Object created from a Builder
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
inline T* FEngine::create(ResourceList<T>& list, typename T::Builder const& builder) noexcept {
|
||||
T* p = mHeapAllocator.make<T>(*this, builder);
|
||||
template<typename T, typename ... ARGS>
|
||||
inline T* FEngine::create(ResourceList<T>& list,
|
||||
typename T::Builder const& builder, ARGS&& ... args) noexcept {
|
||||
T* p = mHeapAllocator.make<T>(*this, builder, std::forward<ARGS>(args)...);
|
||||
if (UTILS_UNLIKELY(p)) { // this should never happen
|
||||
list.insert(p);
|
||||
}
|
||||
@@ -767,8 +768,9 @@ FIndirectLight* FEngine::createIndirectLight(const IndirectLight::Builder& build
|
||||
return create(mIndirectLights, builder);
|
||||
}
|
||||
|
||||
FMaterial* FEngine::createMaterial(const Material::Builder& builder) noexcept {
|
||||
return create(mMaterials, builder);
|
||||
FMaterial* FEngine::createMaterial(const Material::Builder& builder,
|
||||
std::unique_ptr<MaterialParser> materialParser) noexcept {
|
||||
return create(mMaterials, builder, std::move(materialParser));
|
||||
}
|
||||
|
||||
FSkybox* FEngine::createSkybox(const Skybox::Builder& builder) noexcept {
|
||||
|
||||
@@ -263,8 +263,8 @@ public:
|
||||
return mDefaultRenderTarget;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* create(ResourceList<T>& list, typename T::Builder const& builder) noexcept;
|
||||
template <typename T, typename ... ARGS>
|
||||
T* create(ResourceList<T>& list, typename T::Builder const& builder, ARGS&& ... args) noexcept;
|
||||
|
||||
FBufferObject* createBufferObject(const BufferObject::Builder& builder) noexcept;
|
||||
FVertexBuffer* createVertexBuffer(const VertexBuffer::Builder& builder) noexcept;
|
||||
@@ -273,7 +273,7 @@ public:
|
||||
FMorphTargetBuffer* createMorphTargetBuffer(const MorphTargetBuffer::Builder& builder) noexcept;
|
||||
FInstanceBuffer* createInstanceBuffer(const InstanceBuffer::Builder& builder) noexcept;
|
||||
FIndirectLight* createIndirectLight(const IndirectLight::Builder& builder) noexcept;
|
||||
FMaterial* createMaterial(const Material::Builder& builder) noexcept;
|
||||
FMaterial* createMaterial(const Material::Builder& builder, std::unique_ptr<MaterialParser> materialParser) noexcept;
|
||||
FTexture* createTexture(const Texture::Builder& builder) noexcept;
|
||||
FSkybox* createSkybox(const Skybox::Builder& builder) noexcept;
|
||||
FColorGrading* createColorGrading(const ColorGrading::Builder& builder) noexcept;
|
||||
|
||||
@@ -15,29 +15,58 @@
|
||||
*/
|
||||
|
||||
#include "details/Material.h"
|
||||
#include "details/Engine.h"
|
||||
|
||||
#include "Froxelizer.h"
|
||||
#include "MaterialParser.h"
|
||||
|
||||
#include "details/Engine.h"
|
||||
|
||||
#include "FilamentAPI-impl.h"
|
||||
|
||||
#include <private/filament/Variant.h>
|
||||
|
||||
#include <private/filament/EngineEnums.h>
|
||||
#include <private/filament/SamplerInterfaceBlock.h>
|
||||
#include <private/filament/BufferInterfaceBlock.h>
|
||||
#include <private/filament/Variant.h>
|
||||
|
||||
#include <filament/Material.h>
|
||||
#include <filament/MaterialEnums.h>
|
||||
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
#include <matdbg/DebugServer.h>
|
||||
#endif
|
||||
|
||||
#include <filaflat/ChunkContainer.h>
|
||||
#include <filaflat/MaterialChunk.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/CallbackHandler.h>
|
||||
#include <backend/Program.h>
|
||||
|
||||
#include <utils/BitmaskEnum.h>
|
||||
#include <utils/bitset.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Invocable.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Hash.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
@@ -45,15 +74,15 @@ using namespace backend;
|
||||
using namespace filaflat;
|
||||
using namespace utils;
|
||||
|
||||
static MaterialParser* createParser(Backend backend, ShaderLanguage language,
|
||||
const void* data, size_t size) {
|
||||
static std::unique_ptr<MaterialParser> createParser(
|
||||
Backend backend, ShaderLanguage language, const void* data, size_t size) {
|
||||
// unique_ptr so we don't leak MaterialParser on failures below
|
||||
auto materialParser = std::make_unique<MaterialParser>(language, data, size);
|
||||
|
||||
MaterialParser::ParseResult const materialResult = materialParser->parse();
|
||||
|
||||
if (backend == Backend::NOOP) {
|
||||
return materialParser.release();
|
||||
return materialParser;
|
||||
}
|
||||
|
||||
ASSERT_PRECONDITION(materialResult != MaterialParser::ParseResult::ERROR_MISSING_BACKEND,
|
||||
@@ -71,13 +100,12 @@ static MaterialParser* createParser(Backend backend, ShaderLanguage language,
|
||||
|
||||
assert_invariant(backend != Backend::DEFAULT && "Default backend has not been resolved.");
|
||||
|
||||
return materialParser.release();
|
||||
return materialParser;
|
||||
}
|
||||
|
||||
struct Material::BuilderDetails {
|
||||
const void* mPayload = nullptr;
|
||||
size_t mSize = 0;
|
||||
MaterialParser* mMaterialParser = nullptr;
|
||||
bool mDefaultMaterial = false;
|
||||
std::unordered_map<
|
||||
utils::CString,
|
||||
@@ -115,11 +143,11 @@ template Material::Builder& Material::Builder::constant<float>(const char*, size
|
||||
template Material::Builder& Material::Builder::constant<bool>(const char*, size_t, bool);
|
||||
|
||||
Material* Material::Builder::build(Engine& engine) {
|
||||
std::unique_ptr<MaterialParser> materialParser{ createParser(
|
||||
std::unique_ptr<MaterialParser> materialParser = createParser(
|
||||
downcast(engine).getBackend(), downcast(engine).getShaderLanguage(),
|
||||
mImpl->mPayload, mImpl->mSize) };
|
||||
mImpl->mPayload, mImpl->mSize);
|
||||
|
||||
if (materialParser == nullptr) {
|
||||
if (!materialParser) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -146,17 +174,16 @@ Material* Material::Builder::build(Engine& engine) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mImpl->mMaterialParser = materialParser.release();
|
||||
|
||||
return downcast(engine).createMaterial(*this);
|
||||
return downcast(engine).createMaterial(*this, std::move(materialParser));
|
||||
}
|
||||
|
||||
FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
|
||||
FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder,
|
||||
std::unique_ptr<MaterialParser> materialParser)
|
||||
: mEngine(engine),
|
||||
mMaterialId(engine.getMaterialId())
|
||||
mMaterialId(engine.getMaterialId()),
|
||||
mMaterialParser(std::move(materialParser))
|
||||
{
|
||||
MaterialParser* parser = builder->mMaterialParser;
|
||||
mMaterialParser = parser;
|
||||
MaterialParser* const parser = mMaterialParser.get();
|
||||
|
||||
UTILS_UNUSED_IN_RELEASE bool const nameOk = parser->getName(&mName);
|
||||
assert_invariant(nameOk);
|
||||
@@ -236,7 +263,7 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
|
||||
int const maxInstanceCount = (engine.getActiveFeatureLevel() == FeatureLevel::FEATURE_LEVEL_0)
|
||||
? 1 : CONFIG_MAX_INSTANCES;
|
||||
|
||||
int const maxFroxelBufferHeight = std::min(
|
||||
int const maxFroxelBufferHeight = (int)std::min(
|
||||
FROXEL_BUFFER_MAX_ENTRY_COUNT / 4,
|
||||
engine.getDriverApi().getMaxUniformBufferSize() / 16u);
|
||||
|
||||
@@ -323,12 +350,8 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
|
||||
parser->getMaskThreshold(&mMaskThreshold);
|
||||
}
|
||||
|
||||
// The fade blending mode only affects shading. For proper sorting we need to
|
||||
// treat this blending mode as a regular transparent blending operation.
|
||||
if (UTILS_UNLIKELY(mBlendingMode == BlendingMode::FADE)) {
|
||||
mRenderBlendingMode = BlendingMode::TRANSPARENT;
|
||||
} else {
|
||||
mRenderBlendingMode = mBlendingMode;
|
||||
if (mBlendingMode == BlendingMode::CUSTOM) {
|
||||
parser->getCustomBlendFunction(&mCustomBlendFunctions);
|
||||
}
|
||||
|
||||
if (mShading == Shading::UNLIT) {
|
||||
@@ -381,6 +404,12 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
|
||||
mRasterState.blendFunctionDstAlpha = BlendFunction::ONE_MINUS_SRC_COLOR;
|
||||
mRasterState.depthWrite = false;
|
||||
break;
|
||||
case BlendingMode::CUSTOM:
|
||||
mRasterState.blendFunctionSrcRGB = mCustomBlendFunctions[0];
|
||||
mRasterState.blendFunctionSrcAlpha = mCustomBlendFunctions[1];
|
||||
mRasterState.blendFunctionDstRGB = mCustomBlendFunctions[2];
|
||||
mRasterState.blendFunctionDstAlpha = mCustomBlendFunctions[3];
|
||||
mRasterState.depthWrite = false;
|
||||
}
|
||||
|
||||
bool depthWriteSet = false;
|
||||
@@ -463,9 +492,7 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
|
||||
mDefaultInstance.initDefaultInstance(engine, this);
|
||||
}
|
||||
|
||||
FMaterial::~FMaterial() noexcept {
|
||||
delete mMaterialParser;
|
||||
}
|
||||
FMaterial::~FMaterial() noexcept = default;
|
||||
|
||||
void FMaterial::invalidate(Variant::type_t variantMask, Variant::type_t variantValue) noexcept {
|
||||
if (mMaterialDomain == MaterialDomain::SURFACE) {
|
||||
@@ -788,9 +815,7 @@ void FMaterial::applyPendingEdits() noexcept {
|
||||
const char* name = mName.c_str();
|
||||
slog.d << "Applying edits to " << (name ? name : "(untitled)") << io::endl;
|
||||
destroyPrograms(mEngine); // FIXME: this will not destroy the shared variants
|
||||
delete mMaterialParser;
|
||||
mMaterialParser = mPendingEdits;
|
||||
mPendingEdits = nullptr;
|
||||
latchPendingEdits();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -807,8 +832,9 @@ void FMaterial::onEditCallback(void* userdata, const utils::CString&, const void
|
||||
|
||||
// This is called on a web server thread, so we defer clearing the program cache
|
||||
// and swapping out the MaterialParser until the next getProgram call.
|
||||
material->mPendingEdits = createParser(engine.getBackend(), engine.getShaderLanguage(),
|
||||
packageData, packageSize);
|
||||
std::unique_ptr<MaterialParser> pending = createParser(
|
||||
engine.getBackend(), engine.getShaderLanguage(), packageData, packageSize);
|
||||
material->setPendingEdits(std::move(pending));
|
||||
}
|
||||
|
||||
void FMaterial::onQueryCallback(void* userdata, VariantList* pVariants) {
|
||||
|
||||
@@ -22,18 +22,39 @@
|
||||
#include "details/MaterialInstance.h"
|
||||
|
||||
#include <filament/Material.h>
|
||||
#include <filament/MaterialEnums.h>
|
||||
|
||||
#include <private/filament/EngineEnums.h>
|
||||
#include <private/filament/BufferInterfaceBlock.h>
|
||||
#include <private/filament/SamplerBindingsInfo.h>
|
||||
#include <private/filament/SamplerInterfaceBlock.h>
|
||||
#include <private/filament/SubpassInfo.h>
|
||||
#include <private/filament/Variant.h>
|
||||
#include <private/filament/ConstantInfo.h>
|
||||
|
||||
#include <backend/CallbackHandler.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
#include <backend/Program.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Invocable.h>
|
||||
#include <utils/Mutex.h>
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
@@ -43,7 +64,8 @@ class FEngine;
|
||||
|
||||
class FMaterial : public Material {
|
||||
public:
|
||||
FMaterial(FEngine& engine, const Material::Builder& builder);
|
||||
FMaterial(FEngine& engine, const Material::Builder& builder,
|
||||
std::unique_ptr<MaterialParser> materialParser);
|
||||
~FMaterial() noexcept;
|
||||
|
||||
class DefaultMaterialBuilder : public Material::Builder {
|
||||
@@ -127,7 +149,6 @@ public:
|
||||
Shading getShading() const noexcept { return mShading; }
|
||||
Interpolation getInterpolation() const noexcept { return mInterpolation; }
|
||||
BlendingMode getBlendingMode() const noexcept { return mBlendingMode; }
|
||||
BlendingMode getRenderBlendingMode() const noexcept { return mRenderBlendingMode; }
|
||||
VertexDomain getVertexDomain() const noexcept { return mVertexDomain; }
|
||||
MaterialDomain getMaterialDomain() const noexcept { return mMaterialDomain; }
|
||||
CullingMode getCullingMode() const noexcept { return mCullingMode; }
|
||||
@@ -198,7 +219,7 @@ public:
|
||||
static void onQueryCallback(void* userdata, VariantList* pActiveVariants);
|
||||
|
||||
void checkProgramEdits() noexcept {
|
||||
if (UTILS_UNLIKELY(mPendingEdits.load())) {
|
||||
if (UTILS_UNLIKELY(hasPendingEdits())) {
|
||||
applyPendingEdits();
|
||||
}
|
||||
}
|
||||
@@ -223,13 +244,13 @@ private:
|
||||
mutable std::array<backend::Handle<backend::HwProgram>, VARIANT_COUNT> mCachedPrograms;
|
||||
|
||||
backend::RasterState mRasterState;
|
||||
BlendingMode mRenderBlendingMode = BlendingMode::OPAQUE;
|
||||
TransparencyMode mTransparencyMode = TransparencyMode::DEFAULT;
|
||||
bool mIsVariantLit = false;
|
||||
backend::FeatureLevel mFeatureLevel = backend::FeatureLevel::FEATURE_LEVEL_1;
|
||||
Shading mShading = Shading::UNLIT;
|
||||
|
||||
BlendingMode mBlendingMode = BlendingMode::OPAQUE;
|
||||
std::array<backend::BlendFunction, 4> mCustomBlendFunctions = {};
|
||||
Interpolation mInterpolation = Interpolation::SMOOTH;
|
||||
VertexDomain mVertexDomain = VertexDomain::OBJECT;
|
||||
MaterialDomain mMaterialDomain = MaterialDomain::SURFACE;
|
||||
@@ -281,7 +302,21 @@ private:
|
||||
matdbg::MaterialKey mDebuggerId;
|
||||
mutable utils::Mutex mActiveProgramsLock;
|
||||
mutable VariantList mActivePrograms;
|
||||
std::atomic<MaterialParser*> mPendingEdits = {};
|
||||
mutable utils::Mutex mPendingEditsLock;
|
||||
std::unique_ptr<MaterialParser> mPendingEdits;
|
||||
void setPendingEdits(std::unique_ptr<MaterialParser> pendingEdits) noexcept {
|
||||
std::lock_guard lock(mPendingEditsLock);
|
||||
std::swap(pendingEdits, mPendingEdits);
|
||||
}
|
||||
bool hasPendingEdits() noexcept {
|
||||
std::lock_guard lock(mPendingEditsLock);
|
||||
return (bool)mPendingEdits;
|
||||
}
|
||||
void latchPendingEdits() noexcept {
|
||||
std::lock_guard lock(mPendingEditsLock);
|
||||
std::swap(mPendingEdits, mMaterialParser);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
utils::CString mName;
|
||||
@@ -289,7 +324,7 @@ private:
|
||||
const uint32_t mMaterialId;
|
||||
uint64_t mCacheId = 0;
|
||||
mutable uint32_t mMaterialInstanceId = 0;
|
||||
MaterialParser* mMaterialParser = nullptr;
|
||||
std::unique_ptr<MaterialParser> mMaterialParser;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "Filament"
|
||||
spec.version = "1.51.0"
|
||||
spec.version = "1.51.4"
|
||||
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
|
||||
spec.homepage = "https://google.github.io/filament"
|
||||
spec.authors = "Google LLC."
|
||||
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
|
||||
spec.platform = :ios, "11.0"
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.51.0/filament-v1.51.0-ios.tgz" }
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.51.4/filament-v1.51.4-ios.tgz" }
|
||||
|
||||
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
|
||||
spec.pod_target_xcconfig = {
|
||||
|
||||
@@ -59,6 +59,7 @@ enum UTILS_PUBLIC ChunkType : uint64_t {
|
||||
MaterialFeatureLevel = charTo64bitNum("MAT_FEAT"),
|
||||
MaterialShading = charTo64bitNum("MAT_SHAD"),
|
||||
MaterialBlendingMode = charTo64bitNum("MAT_BLEN"),
|
||||
MaterialBlendFunction = charTo64bitNum("MAT_BLFN"),
|
||||
MaterialTransparencyMode = charTo64bitNum("MAT_TRMD"),
|
||||
MaterialMaskThreshold = charTo64bitNum("MAT_THRS"),
|
||||
MaterialShadowMultiplier = charTo64bitNum("MAT_SHML"),
|
||||
|
||||
@@ -80,6 +80,8 @@ enum class BlendingMode : uint8_t {
|
||||
MULTIPLY,
|
||||
//! material brightens what's behind it
|
||||
SCREEN,
|
||||
//! custom blending function
|
||||
CUSTOM,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -227,6 +227,7 @@ public:
|
||||
|
||||
using ShaderQuality = filament::ShaderQuality;
|
||||
using BlendingMode = filament::BlendingMode;
|
||||
using BlendFunction = filament::backend::BlendFunction;
|
||||
using Shading = filament::Shading;
|
||||
using Interpolation = filament::Interpolation;
|
||||
using VertexDomain = filament::VertexDomain;
|
||||
@@ -407,6 +408,15 @@ public:
|
||||
*/
|
||||
MaterialBuilder& blending(BlendingMode blending) noexcept;
|
||||
|
||||
/**
|
||||
* Set the blend function for this material. blending must be et to CUSTOM.
|
||||
*/
|
||||
MaterialBuilder& customBlendFunctions(
|
||||
BlendFunction srcRGB,
|
||||
BlendFunction srcA,
|
||||
BlendFunction dstRGB,
|
||||
BlendFunction dstA) noexcept;
|
||||
|
||||
/**
|
||||
* Set the blending mode of the post-lighting color for this material.
|
||||
* Only OPAQUE, TRANSPARENT and ADD are supported, the default is TRANSPARENT.
|
||||
@@ -828,6 +838,7 @@ private:
|
||||
FeatureLevel mFeatureLevel = FeatureLevel::FEATURE_LEVEL_1;
|
||||
BlendingMode mBlendingMode = BlendingMode::OPAQUE;
|
||||
BlendingMode mPostLightingBlendingMode = BlendingMode::TRANSPARENT;
|
||||
std::array<BlendFunction, 4> mCustomBlendFunctions = {};
|
||||
CullingMode mCullingMode = CullingMode::BACK;
|
||||
Shading mShading = Shading::LIT;
|
||||
MaterialDomain mMaterialDomain = MaterialDomain::SURFACE;
|
||||
|
||||
@@ -388,6 +388,16 @@ MaterialBuilder& MaterialBuilder::blending(BlendingMode blending) noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
MaterialBuilder& MaterialBuilder::customBlendFunctions(
|
||||
BlendFunction srcRGB, BlendFunction srcA,
|
||||
BlendFunction dstRGB, BlendFunction dstA) noexcept {
|
||||
mCustomBlendFunctions[0] = srcRGB;
|
||||
mCustomBlendFunctions[1] = srcA;
|
||||
mCustomBlendFunctions[2] = dstRGB;
|
||||
mCustomBlendFunctions[3] = dstA;
|
||||
return *this;
|
||||
}
|
||||
|
||||
MaterialBuilder& MaterialBuilder::postLightingBlending(BlendingMode blending) noexcept {
|
||||
mPostLightingBlendingMode = blending;
|
||||
return *this;
|
||||
@@ -1510,6 +1520,16 @@ void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo&
|
||||
container.emplace<bool>(ChunkType::MaterialDoubleSided, mDoubleSided);
|
||||
container.emplace<uint8_t>(ChunkType::MaterialBlendingMode,
|
||||
static_cast<uint8_t>(mBlendingMode));
|
||||
|
||||
if (mBlendingMode == BlendingMode::CUSTOM) {
|
||||
uint32_t const blendFunctions =
|
||||
(uint32_t(mCustomBlendFunctions[0]) << 24) |
|
||||
(uint32_t(mCustomBlendFunctions[1]) << 16) |
|
||||
(uint32_t(mCustomBlendFunctions[2]) << 8) |
|
||||
(uint32_t(mCustomBlendFunctions[3]) << 0);
|
||||
container.emplace< uint32_t >(ChunkType::MaterialBlendFunction, blendFunctions);
|
||||
}
|
||||
|
||||
container.emplace<uint8_t>(ChunkType::MaterialTransparencyMode,
|
||||
static_cast<uint8_t>(mTransparencyMode));
|
||||
container.emplace<uint8_t>(ChunkType::MaterialReflectionMode,
|
||||
|
||||
@@ -157,6 +157,9 @@ void ShaderGenerator::generateSurfaceMaterialVariantDefines(utils::io::sstream&
|
||||
case BlendingMode::SCREEN:
|
||||
CodeGenerator::generateDefine(out, "BLEND_MODE_SCREEN", true);
|
||||
break;
|
||||
case BlendingMode::CUSTOM:
|
||||
CodeGenerator::generateDefine(out, "BLEND_MODE_CUSTOM", true);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (material.postLightingBlendingMode) {
|
||||
@@ -175,6 +178,9 @@ void ShaderGenerator::generateSurfaceMaterialVariantDefines(utils::io::sstream&
|
||||
case BlendingMode::SCREEN:
|
||||
CodeGenerator::generateDefine(out, "POST_LIGHTING_BLEND_MODE_SCREEN", true);
|
||||
break;
|
||||
case BlendingMode::CUSTOM:
|
||||
CodeGenerator::generateDefine(out, "POST_LIGHTING_BLEND_MODE_CUSTOM", true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -338,6 +338,7 @@ const char* toString(BlendingMode blendingMode) noexcept {
|
||||
case BlendingMode::FADE: return "fade";
|
||||
case BlendingMode::MULTIPLY: return "multiply";
|
||||
case BlendingMode::SCREEN: return "screen";
|
||||
case BlendingMode::CUSTOM: return "custom";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ const char* toString(BlendingMode blendingMode) noexcept {
|
||||
case BlendingMode::FADE: return "fade";
|
||||
case BlendingMode::MULTIPLY: return "multiply";
|
||||
case BlendingMode::SCREEN: return "screen";
|
||||
case BlendingMode::CUSTOM: return "custom";
|
||||
}
|
||||
return "--";
|
||||
}
|
||||
|
||||
@@ -76,6 +76,8 @@ static string_view readBlendingMode(string_view cursor, BlendingMode* blending)
|
||||
*blending = BlendingMode::MULTIPLY;
|
||||
} else if (sz = readPrefix("screen"sv, cursor); sz > 0) {
|
||||
*blending = BlendingMode::SCREEN;
|
||||
} else if (sz = readPrefix("custom"sv, cursor); sz > 0) {
|
||||
*blending = BlendingMode::CUSTOM;
|
||||
}
|
||||
return { cursor.data(), sz };
|
||||
}
|
||||
|
||||
@@ -87,7 +87,6 @@ if (LINUX OR ANDROID)
|
||||
list(APPEND SRCS src/linux/Path.cpp)
|
||||
endif()
|
||||
if (APPLE)
|
||||
list(APPEND SRCS src/darwin/Exception.mm)
|
||||
list(APPEND SRCS src/darwin/Path.mm)
|
||||
list(APPEND SRCS src/darwin/Systrace.cpp)
|
||||
endif()
|
||||
@@ -123,10 +122,6 @@ if (LINUX)
|
||||
target_link_libraries(${TARGET} PRIVATE dl)
|
||||
endif()
|
||||
|
||||
if (APPLE AND NOT IOS)
|
||||
target_link_libraries(${TARGET} PRIVATE "-framework Cocoa")
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
# Installation
|
||||
# ==================================================================================================
|
||||
|
||||
@@ -250,54 +250,71 @@ namespace utils {
|
||||
*/
|
||||
class UTILS_PUBLIC Panic {
|
||||
public:
|
||||
|
||||
using PanicHandlerCallback = void(*)(void* user, Panic const& panic);
|
||||
|
||||
/**
|
||||
* Sets a user-defined handler for the Panic. If exceptions are enabled, the concrete Panic
|
||||
* object will be thrown upon return; moreover it is acceptable to throw from the provided
|
||||
* callback, but it is unsafe to throw the Panic object itself, since it's just an interface.
|
||||
* It is also acceptable to abort from the callback. If exceptions are not enabled, std::abort()
|
||||
* will be automatically called upon return.
|
||||
*
|
||||
* The PanicHandlerCallback can be called from any thread.
|
||||
*
|
||||
* Caveat: this API can misbehave if is used as a static library in multiple translation units,
|
||||
* some of these translation units might not see the callback.
|
||||
*
|
||||
* @param handler pointer to the user defined handler for the Panic
|
||||
* @param user user pointer given back to the callback
|
||||
*/
|
||||
static void setPanicHandler(PanicHandlerCallback handler, void* user) noexcept;
|
||||
|
||||
|
||||
virtual ~Panic() noexcept;
|
||||
|
||||
/**
|
||||
* @return a detailed description of the error
|
||||
* @return a formatted and detailed description of the error including all available
|
||||
* information.
|
||||
* @see std::exception
|
||||
*/
|
||||
virtual const char* what() const noexcept = 0;
|
||||
|
||||
/**
|
||||
* Get the reason for the panic.
|
||||
* @return a C string containing the reason for the panic.
|
||||
* Get the reason string for the panic
|
||||
* @return a C string containing the reason for the panic
|
||||
*/
|
||||
virtual const char* getReason() const noexcept = 0;
|
||||
|
||||
/**
|
||||
* Get the type of the panic. When compiled without RTTI, this returns a generic type name.
|
||||
* @return a C string containing the type of the panic.
|
||||
*/
|
||||
virtual const char* getType() const noexcept = 0;
|
||||
|
||||
/**
|
||||
* Get the function name where the panic was detected
|
||||
* Get the function name where the panic was detected. On debug build the fully qualified
|
||||
* function name is returned; on release builds only the function name is.
|
||||
* @return a C string containing the function name where the panic was detected
|
||||
*/
|
||||
virtual const char* getFunction() const noexcept = 0;
|
||||
|
||||
/**
|
||||
* Get the file name where the panic was detected
|
||||
* Get the file name where the panic was detected. Only available on debug builds.
|
||||
* @return a C string containing the file name where the panic was detected
|
||||
*/
|
||||
virtual const char* getFile() const noexcept = 0;
|
||||
|
||||
/**
|
||||
* Get the line number in the file where the panic was detected
|
||||
* Get the line number in the file where the panic was detected. Only available on debug builds.
|
||||
* @return an integer containing the line number in the file where the panic was detected
|
||||
*/
|
||||
virtual int getLine() const noexcept = 0;
|
||||
|
||||
/**
|
||||
* Logs this exception to the system-log
|
||||
*/
|
||||
virtual void log() const noexcept = 0;
|
||||
|
||||
/**
|
||||
* Get the CallStack when the panic was detected
|
||||
* Get the CallStack when the panic was detected if available.
|
||||
* @return the CallStack when the panic was detected
|
||||
*/
|
||||
virtual const CallStack& getCallStack() const noexcept = 0;
|
||||
|
||||
/**
|
||||
* Logs this exception to the system-log
|
||||
*/
|
||||
virtual void log() const noexcept = 0;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
@@ -318,7 +335,6 @@ public:
|
||||
|
||||
// Panic interface
|
||||
const char* getReason() const noexcept override;
|
||||
const char* getType() const noexcept override;
|
||||
const char* getFunction() const noexcept override;
|
||||
const char* getFile() const noexcept override;
|
||||
int getLine() const noexcept override;
|
||||
@@ -383,8 +399,7 @@ private:
|
||||
char const* const m_function = nullptr;
|
||||
char const* const m_file = nullptr;
|
||||
const int m_line = -1;
|
||||
std::string m_msg;
|
||||
std::string m_type;
|
||||
mutable std::string m_msg;
|
||||
};
|
||||
|
||||
namespace details {
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_DARWIN_EXCEPTION_H
|
||||
#define TNT_UTILS_DARWIN_EXCEPTION_H
|
||||
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/compiler.h>
|
||||
|
||||
namespace utils::details {
|
||||
|
||||
void throwDarwinException(const Panic& exception) UTILS_NORETURN;
|
||||
|
||||
}
|
||||
|
||||
#endif // TNT_UTILS_DARWIN_EXCEPTION_H
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <utils/compiler.h>
|
||||
|
||||
namespace utils {
|
||||
UTILS_PUBLIC
|
||||
void panic(const char *func, const char * file, int line, const char *assertion) noexcept;
|
||||
} // namespace filament
|
||||
|
||||
|
||||
+79
-26
@@ -14,21 +14,64 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <utils/darwin/Exception.h>
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdarg>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace utils {
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
class UserPanicHandler {
|
||||
struct CallBack {
|
||||
Panic::PanicHandlerCallback handler = nullptr;
|
||||
void* user = nullptr;
|
||||
void call(Panic const& panic) const noexcept {
|
||||
if (UTILS_UNLIKELY(handler)) {
|
||||
handler(user, panic);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
mutable std::mutex mLock{};
|
||||
CallBack mCallBack{};
|
||||
|
||||
CallBack getCallback() const noexcept {
|
||||
std::lock_guard const lock(mLock);
|
||||
return mCallBack;
|
||||
}
|
||||
|
||||
public:
|
||||
static UserPanicHandler& get() noexcept {
|
||||
static UserPanicHandler data;
|
||||
return data;
|
||||
}
|
||||
|
||||
void call(Panic const& panic) const noexcept {
|
||||
getCallback().call(panic);
|
||||
}
|
||||
|
||||
void set(Panic::PanicHandlerCallback handler, void* user) noexcept {
|
||||
std::lock_guard const lock(mLock);
|
||||
mCallBack = { handler, user };
|
||||
}
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
static std::string formatString(const char* format, va_list args) noexcept {
|
||||
std::string reason;
|
||||
|
||||
@@ -39,10 +82,12 @@ static std::string formatString(const char* format, va_list args) noexcept {
|
||||
|
||||
if (n >= 0) {
|
||||
++n; // for the nul-terminating char
|
||||
char* buf = new char[n];
|
||||
vsnprintf(buf, size_t(n), format, args);
|
||||
reason.assign(buf);
|
||||
delete [] buf;
|
||||
char* const buf = new(std::nothrow) char[n];
|
||||
if (buf) {
|
||||
vsnprintf(buf, size_t(n), format, args);
|
||||
reason.assign(buf);
|
||||
delete [] buf;
|
||||
}
|
||||
}
|
||||
return reason;
|
||||
}
|
||||
@@ -67,8 +112,16 @@ static std::string panicString(
|
||||
#endif
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
Panic::~Panic() noexcept = default;
|
||||
|
||||
void Panic::setPanicHandler(PanicHandlerCallback handler, void* user) noexcept {
|
||||
UserPanicHandler::get().set(handler, user);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename T>
|
||||
TPanic<T>::TPanic(std::string reason) :
|
||||
m_reason(std::move(reason)) {
|
||||
@@ -97,11 +150,6 @@ const char* TPanic<T>::getReason() const noexcept {
|
||||
return m_reason.c_str();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const char* TPanic<T>::getType() const noexcept {
|
||||
return m_type.c_str();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const char* TPanic<T>::getFunction() const noexcept {
|
||||
return m_function;
|
||||
@@ -130,12 +178,13 @@ void TPanic<T>::log() const noexcept {
|
||||
|
||||
template<typename T>
|
||||
void TPanic<T>::buildMessage() {
|
||||
std::string type;
|
||||
#if UTILS_HAS_RTTI
|
||||
m_type = CallStack::demangleTypeName(typeid(T).name()).c_str();
|
||||
type = CallStack::demangleTypeName(typeid(T).name()).c_str();
|
||||
#else
|
||||
m_type = "Panic";
|
||||
type = "Panic";
|
||||
#endif
|
||||
m_msg = panicString(m_type, m_function, m_line, m_file, m_reason.c_str());
|
||||
m_msg = panicString(type, m_function, m_line, m_file, m_reason.c_str());
|
||||
}
|
||||
|
||||
UTILS_ALWAYS_INLINE
|
||||
@@ -148,18 +197,22 @@ template<typename T>
|
||||
void TPanic<T>::panic(char const* function, char const* file, int line, const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
std::string reason(formatString(format, args));
|
||||
std::string const reason(formatString(format, args));
|
||||
va_end(args);
|
||||
T e(function, formatFile(file), line, reason);
|
||||
|
||||
// always log the Panic at the point it is detected
|
||||
e.log();
|
||||
|
||||
#if defined(__APPLE__)
|
||||
details::throwDarwinException(e);
|
||||
// Call the user provided handler
|
||||
UserPanicHandler::get().call(e);
|
||||
|
||||
// if exceptions are enabled, throw now.
|
||||
#ifdef __EXCEPTIONS
|
||||
throw e;
|
||||
#endif
|
||||
|
||||
#ifdef __EXCEPTIONS
|
||||
throw e;
|
||||
#endif
|
||||
// and finally abort if we somehow get here
|
||||
std::abort();
|
||||
}
|
||||
|
||||
@@ -170,7 +223,7 @@ namespace details {
|
||||
void panicLog(char const* function, char const* file, int line, const char* format, ...) noexcept {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
std::string reason(formatString(format, args));
|
||||
std::string const reason(formatString(format, args));
|
||||
va_end(args);
|
||||
|
||||
const std::string msg = panicString("" /* no extra message */,
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
namespace utils::details {
|
||||
|
||||
void throwDarwinException(const Panic& exception) {
|
||||
// Turn this exception into an NSException and raise it.
|
||||
// This is allowed even if we're built without Objective-C exceptions (-fno-objc-exceptions).
|
||||
[[NSException exceptionWithName:@(exception.getType())
|
||||
reason:@(exception.getReason())
|
||||
userInfo:@{
|
||||
@"file" : @(exception.getFile()),
|
||||
@"line" : @(exception.getLine()),
|
||||
@"function" : @(exception.getFunction())
|
||||
}] raise];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -671,6 +671,7 @@ static bool processBlending(MaterialBuilder& builder, const JsonishValue& value)
|
||||
{ "fade", MaterialBuilder::BlendingMode::FADE },
|
||||
{ "multiply", MaterialBuilder::BlendingMode::MULTIPLY },
|
||||
{ "screen", MaterialBuilder::BlendingMode::SCREEN },
|
||||
{ "custom", MaterialBuilder::BlendingMode::CUSTOM },
|
||||
};
|
||||
auto jsonString = value.toJsonString();
|
||||
if (!isStringValidEnum(strToEnum, jsonString->getString())) {
|
||||
@@ -681,6 +682,52 @@ static bool processBlending(MaterialBuilder& builder, const JsonishValue& value)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool processBlendFunction(MaterialBuilder& builder, const JsonishValue& value) {
|
||||
static const std::unordered_map<std::string, MaterialBuilder::BlendFunction> strToEnum{
|
||||
{ "zero", MaterialBuilder::BlendFunction::ZERO },
|
||||
{ "one", MaterialBuilder::BlendFunction::ONE },
|
||||
{ "srcColor", MaterialBuilder::BlendFunction::SRC_COLOR },
|
||||
{ "oneMinusSrcColor", MaterialBuilder::BlendFunction::ONE_MINUS_SRC_COLOR },
|
||||
{ "dstColor", MaterialBuilder::BlendFunction::DST_COLOR },
|
||||
{ "oneMinusDstColor", MaterialBuilder::BlendFunction::ONE_MINUS_DST_COLOR },
|
||||
{ "srcAlpha", MaterialBuilder::BlendFunction::SRC_ALPHA },
|
||||
{ "oneMinusSrcAlpha", MaterialBuilder::BlendFunction::ONE_MINUS_SRC_ALPHA },
|
||||
{ "dstAlpha", MaterialBuilder::BlendFunction::DST_ALPHA },
|
||||
{ "oneMinusDstAlpha", MaterialBuilder::BlendFunction::ONE_MINUS_DST_ALPHA },
|
||||
{ "srcAlphaSaturate", MaterialBuilder::BlendFunction::SRC_ALPHA_SATURATE },
|
||||
};
|
||||
|
||||
if (value.getType() != JsonishValue::Type::OBJECT) {
|
||||
std::cerr << "blendFunction must be an OBJECT." << std::endl;
|
||||
}
|
||||
|
||||
JsonishObject const* const jsonObject = value.toJsonObject();
|
||||
|
||||
MaterialBuilder::BlendFunction srcRGB, srcA, dstRGB, dstA;
|
||||
std::pair<const char*, MaterialBuilder::BlendFunction*> functions[] = {
|
||||
{ "srcRGB", &srcRGB },
|
||||
{ "srcA", &srcA },
|
||||
{ "dstRGB", &dstRGB },
|
||||
{ "dstA", &dstA },
|
||||
};
|
||||
|
||||
for (auto&& entry : functions) {
|
||||
const char* key = entry.first;
|
||||
const JsonishValue* v = jsonObject->getValue(key);
|
||||
if (!v) {
|
||||
std::cerr << "blendFunction: entry without '" << key << "' key." << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (v->getType() != JsonishValue::STRING) {
|
||||
std::cerr << "blendFunction: '" << key << "' value must be STRING." << std::endl;
|
||||
return false;
|
||||
}
|
||||
*entry.second = stringToEnum(strToEnum, v->toJsonString()->getString());
|
||||
}
|
||||
builder.customBlendFunctions(srcRGB, srcA, dstRGB, dstA);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool processPostLightingBlending(MaterialBuilder& builder, const JsonishValue& value) {
|
||||
static const std::unordered_map<std::string, MaterialBuilder::BlendingMode> strToEnum {
|
||||
{ "add", MaterialBuilder::BlendingMode::ADD },
|
||||
@@ -1194,6 +1241,7 @@ ParametersProcessor::ParametersProcessor() {
|
||||
mParameters["variables"] = { &processVariables, Type::ARRAY };
|
||||
mParameters["requires"] = { &processRequires, Type::ARRAY };
|
||||
mParameters["blending"] = { &processBlending, Type::STRING };
|
||||
mParameters["blendFunction"] = { &processBlendFunction, Type::OBJECT };
|
||||
mParameters["postLightingBlending"] = { &processPostLightingBlending, Type::STRING };
|
||||
mParameters["vertexDomain"] = { &processVertexDomain, Type::STRING };
|
||||
mParameters["culling"] = { &processCulling, Type::STRING };
|
||||
|
||||
@@ -369,8 +369,8 @@ using EntityVector = std::vector<utils::Entity>;
|
||||
|
||||
register_vector<std::string>("RegistryKeys");
|
||||
register_vector<utils::Entity>("EntityVector");
|
||||
register_vector<FilamentInstance*>("AssetInstanceVector");
|
||||
register_vector<MaterialInstance*>("MaterialInstanceVector");
|
||||
register_vector<allow_raw_pointer<FilamentInstance*>>("AssetInstanceVector");
|
||||
register_vector<allow_raw_pointer<MaterialInstance*>>("MaterialInstanceVector");
|
||||
|
||||
// CORE FILAMENT CLASSES
|
||||
// ---------------------
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "filament",
|
||||
"version": "1.51.0",
|
||||
"version": "1.51.4",
|
||||
"description": "Real-time physically based rendering engine",
|
||||
"main": "filament.js",
|
||||
"module": "filament.js",
|
||||
|
||||
Reference in New Issue
Block a user