Compare commits
776
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3f1a4c94d | ||
|
|
bef004e1b0 | ||
|
|
936d0a7b1d | ||
|
|
2b651d4946 | ||
|
|
d6ab9f1c0b | ||
|
|
786b7ec7ae | ||
|
|
55173efc2c | ||
|
|
7fc8e339e7 | ||
|
|
0395df3689 | ||
|
|
64f03b3832 | ||
|
|
982b159b3e | ||
|
|
bbd4177dd0 | ||
|
|
6e3cccf30c | ||
|
|
5d5f53e6e3 | ||
|
|
ec44c4a157 | ||
|
|
74751a0971 | ||
|
|
28069e43dc | ||
|
|
3603202cc5 | ||
|
|
a8596ae9c9 | ||
|
|
3fb9521c10 | ||
|
|
17f32d198a | ||
|
|
11ecaa2fbf | ||
|
|
d56f769d4d | ||
|
|
a46ca78f41 | ||
|
|
7ba437b2c6 | ||
|
|
b4c33d2ab2 | ||
|
|
455025349d | ||
|
|
3fa4aab02a | ||
|
|
5485ef238f | ||
|
|
a5541de84d | ||
|
|
2d184f5077 | ||
|
|
c2e3a97705 | ||
|
|
0d22805342 | ||
|
|
aeb0c14ce1 | ||
|
|
144d99df57 | ||
|
|
268e204a9f | ||
|
|
a13aa728bf | ||
|
|
c0ee1e2874 | ||
|
|
1b9d2c6fa6 | ||
|
|
7489c55532 | ||
|
|
2d157e8fe1 | ||
|
|
3ba082da13 | ||
|
|
7ae2773222 | ||
|
|
b7eb12bd0c | ||
|
|
305bfb36d8 | ||
|
|
880b454702 | ||
|
|
996e2a206e | ||
|
|
b57fbfb128 | ||
|
|
2f36df8d93 | ||
|
|
76a8f18700 | ||
|
|
53af1fd052 | ||
|
|
cb88e7555f | ||
|
|
35fa79ec23 | ||
|
|
d9cba80bcf | ||
|
|
ed4154ee0e | ||
|
|
65f2df7776 | ||
|
|
1b1c03814a | ||
|
|
b89a0173ef | ||
|
|
1fec588fb1 | ||
|
|
de1edbdf25 | ||
|
|
d468303bc9 | ||
|
|
81658541a1 | ||
|
|
d0eb56ff20 | ||
|
|
85589a7d16 | ||
|
|
d476c7fa1b | ||
|
|
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
|
||||
|
||||
- Add a `name` API to Filament objects for debugging handle use-after-free assertions
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.54.0'
|
||||
implementation 'com.google.android.filament:filament-android:1.53.2'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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.54.0'
|
||||
pod 'Filament', '~> 1.53.2'
|
||||
```
|
||||
|
||||
### Snapshots
|
||||
|
||||
@@ -7,26 +7,6 @@ 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.54.1
|
||||
|
||||
|
||||
## v1.54.0
|
||||
|
||||
- materials: add a new `stereoscopicType` material parameter. [⚠️ **New Material Version**]
|
||||
- Fix a crash when compiling shaders on IMG devices
|
||||
|
||||
## v1.53.5
|
||||
|
||||
- engine: Fix bug causing certain sampler parameters to not be applied correctly in GLES 2.0 and on
|
||||
certain GLES 3.0 drivers.
|
||||
|
||||
## v1.53.4
|
||||
|
||||
|
||||
## v1.53.3
|
||||
|
||||
- Add drag and drop support for IBL files for desktop gltf_viewer.
|
||||
|
||||
## v1.53.2
|
||||
|
||||
|
||||
|
||||
@@ -28,14 +28,6 @@
|
||||
using namespace filament;
|
||||
using namespace backend;
|
||||
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Renderer_nSkipFrame(JNIEnv *, jclass, jlong nativeRenderer,
|
||||
jlong vsyncSteadyClockTimeNano) {
|
||||
Renderer *renderer = (Renderer *) nativeRenderer;
|
||||
renderer->skipFrame(uint64_t(vsyncSteadyClockTimeNano));
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_Renderer_nBeginFrame(JNIEnv *, jclass, jlong nativeRenderer,
|
||||
jlong nativeSwapChain, jlong frameTimeNanos) {
|
||||
|
||||
@@ -531,12 +531,3 @@ Java_com_google_android_filament_View_nGetFogEntity(JNIEnv *env, jclass clazz,
|
||||
View *view = (View *) nativeView;
|
||||
return (jint)view->getFogEntity().getId();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_View_nClearFrameHistory(JNIEnv *env, jclass clazz,
|
||||
jlong nativeView, jlong nativeEngine) {
|
||||
View *view = (View *) nativeView;
|
||||
Engine *engine = (Engine *) nativeEngine;
|
||||
view->clearFrameHistory(*engine);
|
||||
}
|
||||
|
||||
@@ -425,12 +425,9 @@ public class Engine {
|
||||
public long resourceAllocatorCacheSizeMB = 64;
|
||||
|
||||
/*
|
||||
* This value determines how many frames texture entries are kept for in the cache. This
|
||||
* is a soft limit, meaning some texture older than this are allowed to stay in the cache.
|
||||
* Typically only one texture is evicted per frame.
|
||||
* The default is 1.
|
||||
* This value determines for how many frames are texture entries kept in the cache.
|
||||
*/
|
||||
public long resourceAllocatorCacheMaxAge = 1;
|
||||
public long resourceAllocatorCacheMaxAge = 2;
|
||||
|
||||
/*
|
||||
* Disable backend handles use-after-free checks.
|
||||
|
||||
@@ -297,20 +297,6 @@ public class Renderer {
|
||||
nSetVsyncTime(getNativeObject(), steadyClockTimeNano);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call skipFrame when momentarily skipping frames, for instance if the content of the
|
||||
* scene doesn't change.
|
||||
*
|
||||
* @param vsyncSteadyClockTimeNano The time in nanoseconds when the frame started being rendered,
|
||||
* in the {@link System#nanoTime()} timebase. Divide this value by 1000000 to
|
||||
* convert it to the {@link android.os.SystemClock#uptimeMillis()}
|
||||
* time base. This typically comes from
|
||||
* {@link android.view.Choreographer.FrameCallback}.
|
||||
*/
|
||||
public void skipFrame(long vsyncSteadyClockTimeNano) {
|
||||
nSkipFrame(getNativeObject(), vsyncSteadyClockTimeNano);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a frame for this <code>Renderer</code>.
|
||||
* <p><code>beginFrame</code> manages frame pacing, and returns whether or not a frame should be
|
||||
@@ -730,7 +716,6 @@ public class Renderer {
|
||||
|
||||
private static native void nSetPresentationTime(long nativeObject, long monotonicClockNanos);
|
||||
private static native void nSetVsyncTime(long nativeObject, long steadyClockTimeNano);
|
||||
private static native void nSkipFrame(long nativeObject, long vsyncSteadyClockTimeNano);
|
||||
private static native boolean nBeginFrame(long nativeRenderer, long nativeSwapChain, long frameTimeNanos);
|
||||
private static native void nEndFrame(long nativeRenderer);
|
||||
private static native void nRender(long nativeRenderer, long nativeView);
|
||||
|
||||
@@ -1233,18 +1233,6 @@ public class View {
|
||||
return nGetFogEntity(getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* When certain temporal features are used (e.g.: TAA or Screen-space reflections), the view
|
||||
* keeps a history of previous frame renders associated with the Renderer the view was last
|
||||
* used with. When switching Renderer, it may be necessary to clear that history by calling
|
||||
* this method. Similarly, if the whole content of the screen change, like when a cut-scene
|
||||
* starts, clearing the history might be needed to avoid artifacts due to the previous frame
|
||||
* being very different.
|
||||
*/
|
||||
public void clearFrameHistory(Engine engine) {
|
||||
nClearFrameHistory(getNativeObject(), engine.getNativeObject());
|
||||
}
|
||||
|
||||
public long getNativeObject() {
|
||||
if (mNativeObject == 0) {
|
||||
throw new IllegalStateException("Calling method on destroyed View");
|
||||
@@ -1306,7 +1294,7 @@ public class View {
|
||||
private static native void nSetMaterialGlobal(long nativeView, int index, float x, float y, float z, float w);
|
||||
private static native void nGetMaterialGlobal(long nativeView, int index, float[] out);
|
||||
private static native int nGetFogEntity(long nativeView);
|
||||
private static native void nClearFrameHistory(long nativeView, long nativeEngine);
|
||||
|
||||
|
||||
/**
|
||||
* List of available ambient occlusion techniques.
|
||||
|
||||
@@ -125,11 +125,6 @@ extern "C" JNIEXPORT void Java_com_google_android_filament_utils_Manipulator_nBu
|
||||
builder->groundPlane(a, b, c, d);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void Java_com_google_android_filament_utils_Manipulator_nBuilderPanning(JNIEnv*, jclass, jlong nativeBuilder, jboolean enabled) {
|
||||
Builder* builder = (Builder*) nativeBuilder;
|
||||
builder->panning(enabled);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT long Java_com_google_android_filament_utils_Manipulator_nBuilderBuild(JNIEnv*, jclass, jlong nativeBuilder, jint mode) {
|
||||
Builder* builder = (Builder*) nativeBuilder;
|
||||
return (jlong) builder->build((Mode) mode);
|
||||
|
||||
-12
@@ -274,17 +274,6 @@ public class Manipulator {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether panning is enabled in the manipulator.
|
||||
*
|
||||
* @return this <code>Builder</code> object for chaining calls
|
||||
*/
|
||||
@NonNull
|
||||
public Builder panning(Boolean enabled) {
|
||||
nBuilderPanning(mNativeBuilder, enabled);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns the <code>Manipulator</code> object.
|
||||
*
|
||||
@@ -494,7 +483,6 @@ public class Manipulator {
|
||||
private static native void nBuilderFlightPanSpeed(long nativeBuilder, float x, float y);
|
||||
private static native void nBuilderFlightMoveDamping(long nativeBuilder, float damping);
|
||||
private static native void nBuilderGroundPlane(long nativeBuilder, float a, float b, float c, float d);
|
||||
private static native void nBuilderPanning(long nativeBuilder, Boolean enabled);
|
||||
private static native long nBuilderBuild(long nativeBuilder, int mode);
|
||||
|
||||
private static native void nDestroyManipulator(long nativeManip);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.54.0
|
||||
VERSION_NAME=1.53.2
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
+2
-11
@@ -1308,12 +1308,7 @@ Description
|
||||
declare a variable called `eyeDirection` you can access it in the fragment shader using
|
||||
`variable_eyeDirection`. In the vertex shader, the interpolant name is simply a member of
|
||||
the `MaterialVertexInputs` structure (`material.eyeDirection` in your example). Each
|
||||
interpolant is of type `float4` (`vec4`) in the shaders. By default the precision of the
|
||||
interpolant is `highp` in *both* the vertex and fragment shaders.
|
||||
An alternate syntax can be used to specify both the name and precision of the interpolant.
|
||||
In this case the specified precision is used as-is in both fragment and vertex stages, in
|
||||
particular if `default` is specified the default precision is used is the fragment shader
|
||||
(`mediump`) and in the vertex shader (`highp`).
|
||||
interpolant is of type `float4` (`vec4`) in the shaders.
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
|
||||
material {
|
||||
@@ -1325,11 +1320,7 @@ material {
|
||||
}
|
||||
],
|
||||
variables : [
|
||||
eyeDirection,
|
||||
{
|
||||
name : eyeColor,
|
||||
precision : medium
|
||||
}
|
||||
eyeDirection
|
||||
],
|
||||
vertexDomain : device,
|
||||
depthWrite : false,
|
||||
|
||||
@@ -61,7 +61,6 @@ set(SRCS
|
||||
src/Engine.cpp
|
||||
src/Exposure.cpp
|
||||
src/Fence.cpp
|
||||
src/FilamentBuilder.cpp
|
||||
src/FrameInfo.cpp
|
||||
src/FrameSkipper.cpp
|
||||
src/Froxelizer.cpp
|
||||
|
||||
@@ -1244,7 +1244,7 @@ enum class Workaround : uint16_t {
|
||||
ADRENO_UNIFORM_ARRAY_CRASH,
|
||||
// Workaround a Metal pipeline compilation error with the message:
|
||||
// "Could not statically determine the target of a texture". See light_indirect.fs
|
||||
METAL_STATIC_TEXTURE_TARGET_ERROR,
|
||||
A8X_STATIC_TEXTURE_TARGET_ERROR,
|
||||
// Adreno drivers sometimes aren't able to blit into a layer of a texture array.
|
||||
DISABLE_BLIT_INTO_TEXTURE_ARRAY,
|
||||
// Multiple workarounds needed for PowerVR GPUs
|
||||
|
||||
@@ -162,10 +162,6 @@ DECL_DRIVER_API_0(finish)
|
||||
// reset state tracking, if the driver does any state tracking (e.g. GL)
|
||||
DECL_DRIVER_API_0(resetState)
|
||||
|
||||
DECL_DRIVER_API_N(setDebugTag,
|
||||
backend::HandleBase::HandleId, handleId,
|
||||
utils::CString, tag)
|
||||
|
||||
/*
|
||||
* Creating driver objects
|
||||
* -----------------------
|
||||
|
||||
@@ -20,12 +20,11 @@
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include <utils/Allocator.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
@@ -40,7 +39,7 @@
|
||||
|
||||
#define HandleAllocatorGL HandleAllocator<32, 64, 136> // ~4520 / pool / MiB
|
||||
#define HandleAllocatorVK HandleAllocator<64, 160, 312> // ~1820 / pool / MiB
|
||||
#define HandleAllocatorMTL HandleAllocator<32, 64, 552> // ~1660 / pool / MiB
|
||||
#define HandleAllocatorMTL HandleAllocator<32, 48, 552> // ~1660 / pool / MiB
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -174,10 +173,8 @@ public:
|
||||
uint8_t const age = (tag & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT;
|
||||
auto const pNode = static_cast<typename Allocator::Node*>(p);
|
||||
uint8_t const expectedAge = pNode[-1].age;
|
||||
// getHandleTag() is only called if the check fails.
|
||||
FILAMENT_CHECK_POSTCONDITION(expectedAge == age)
|
||||
<< "use-after-free of Handle with id=" << handle.getId()
|
||||
<< ", tag=" << getHandleTag(handle.getId()).c_str_safe();
|
||||
FILAMENT_CHECK_POSTCONDITION(expectedAge == age) <<
|
||||
"use-after-free of Handle with id=" << handle.getId();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,34 +201,6 @@ public:
|
||||
return handle_cast<Dp>(const_cast<Handle<B>&>(handle));
|
||||
}
|
||||
|
||||
void associateTagToHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept {
|
||||
// TODO: for now, only pool handles check for use-after-free, so we only keep tags for
|
||||
// those
|
||||
if (isPoolHandle(id)) {
|
||||
// Truncate the tag's age to N bits.
|
||||
constexpr uint8_t N = 2; // support a history of 4 tags
|
||||
constexpr uint8_t mask = (1 << N) - 1;
|
||||
|
||||
uint8_t const age = (id & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT;
|
||||
uint8_t const newAge = age & mask;
|
||||
uint32_t const key = (id & ~HANDLE_AGE_MASK) | (newAge << HANDLE_AGE_SHIFT);
|
||||
|
||||
// This line is the costly part. In the future, we could potentially use a custom
|
||||
// allocator.
|
||||
mDebugTags[key] = std::move(tag);
|
||||
}
|
||||
}
|
||||
|
||||
utils::CString getHandleTag(HandleBase::HandleId id) const noexcept {
|
||||
if (!isPoolHandle(id)) {
|
||||
return "(no tag)";
|
||||
}
|
||||
if (auto pos = mDebugTags.find(id); pos != mDebugTags.end()) {
|
||||
return pos->second;
|
||||
}
|
||||
return "(no tag)";
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
template<typename D>
|
||||
@@ -394,7 +363,6 @@ private:
|
||||
// Below is only used when running out of space in the HandleArena
|
||||
mutable utils::Mutex mLock;
|
||||
tsl::robin_map<HandleBase::HandleId, void*> mOverflowMap;
|
||||
tsl::robin_map<HandleBase::HandleId, utils::CString> mDebugTags;
|
||||
HandleBase::HandleId mId = 0;
|
||||
bool mUseAfterFreeCheckDisabled = false;
|
||||
};
|
||||
|
||||
@@ -32,11 +32,10 @@
|
||||
# define HAS_MMAP 0
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace utils;
|
||||
|
||||
@@ -82,9 +81,6 @@ void* CircularBuffer::alloc(size_t size) noexcept {
|
||||
// map the circular buffer once...
|
||||
vaddr = mmap(reserve_vaddr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
|
||||
if (vaddr != MAP_FAILED) {
|
||||
// populate the address space with pages (because this is a circular buffer,
|
||||
// all the pages will be allocated eventually, might as well do it now)
|
||||
memset(vaddr, 0, size);
|
||||
// and map the circular buffer again, behind the previous copy...
|
||||
vaddr_shadow = mmap((char*)vaddr + size, size,
|
||||
PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
|
||||
@@ -105,7 +101,7 @@ void* CircularBuffer::alloc(size_t size) noexcept {
|
||||
if (UTILS_UNLIKELY(mAshmemFd < 0)) {
|
||||
// ashmem failed
|
||||
if (vaddr_guard != MAP_FAILED) {
|
||||
munmap(vaddr_guard, BLOCK_SIZE);
|
||||
munmap(vaddr_guard, size);
|
||||
}
|
||||
|
||||
if (vaddr_shadow != MAP_FAILED) {
|
||||
@@ -123,11 +119,12 @@ void* CircularBuffer::alloc(size_t size) noexcept {
|
||||
data = mmap(nullptr, size * 2 + BLOCK_SIZE,
|
||||
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(data != MAP_FAILED) <<
|
||||
FILAMENT_CHECK_POSTCONDITION(data) <<
|
||||
"couldn't allocate " << (size * 2 / 1024) <<
|
||||
" KiB of virtual address space for the command buffer";
|
||||
|
||||
slog.w << "Using 'soft' CircularBuffer (" << (size * 2 / 1024) << " KiB)" << io::endl;
|
||||
slog.d << "WARNING: Using soft CircularBuffer (" << (size * 2 / 1024) << " KiB)"
|
||||
<< io::endl;
|
||||
|
||||
// guard page at the end
|
||||
void* guard = (void*)(uintptr_t(data) + size * 2);
|
||||
|
||||
@@ -101,8 +101,9 @@ void CommandBufferQueue::flush() noexcept {
|
||||
size_t const used = std::distance(
|
||||
static_cast<char const*>(begin), static_cast<char const*>(end));
|
||||
|
||||
|
||||
std::unique_lock<utils::Mutex> lock(mLock);
|
||||
mCommandBuffersToExecute.push_back({ begin, end });
|
||||
mCondition.notify_one();
|
||||
|
||||
// circular buffer is too small, we corrupted the stream
|
||||
FILAMENT_CHECK_POSTCONDITION(used <= mFreeSpace) <<
|
||||
@@ -111,13 +112,11 @@ void CommandBufferQueue::flush() noexcept {
|
||||
"Space used at this time: " << used <<
|
||||
" bytes, overflow: " << used - mFreeSpace << " bytes";
|
||||
|
||||
mFreeSpace -= used;
|
||||
mCommandBuffersToExecute.push_back({ begin, end });
|
||||
mCondition.notify_one();
|
||||
|
||||
// wait until there is enough space in the buffer
|
||||
mFreeSpace -= used;
|
||||
if (UTILS_UNLIKELY(mFreeSpace < requiredSize)) {
|
||||
|
||||
|
||||
#ifndef NDEBUG
|
||||
size_t const totalUsed = circularBuffer.size() - mFreeSpace;
|
||||
slog.d << "CommandStream used too much space (will block): "
|
||||
@@ -154,10 +153,8 @@ std::vector<CommandBufferQueue::Range> CommandBufferQueue::waitForCommands() con
|
||||
}
|
||||
|
||||
void CommandBufferQueue::releaseBuffer(CommandBufferQueue::Range const& buffer) {
|
||||
size_t const used = std::distance(
|
||||
static_cast<char const*>(buffer.begin), static_cast<char const*>(buffer.end));
|
||||
std::lock_guard<utils::Mutex> const lock(mLock);
|
||||
mFreeSpace += used;
|
||||
mFreeSpace += uintptr_t(buffer.end) - uintptr_t(buffer.begin);
|
||||
mCondition.notify_one();
|
||||
}
|
||||
|
||||
|
||||
@@ -80,9 +80,6 @@ HandleAllocator<P0, P1, P2>::HandleAllocator(const char* name, size_t size,
|
||||
bool disableUseAfterFreeCheck) noexcept
|
||||
: mHandleArena(name, size, disableUseAfterFreeCheck),
|
||||
mUseAfterFreeCheckDisabled(disableUseAfterFreeCheck) {
|
||||
// Reserve initial space for debug tags. This prevents excessive calls to malloc when the first
|
||||
// few tags are set.
|
||||
mDebugTags.reserve(512);
|
||||
}
|
||||
|
||||
template <size_t P0, size_t P1, size_t P2>
|
||||
|
||||
@@ -93,7 +93,7 @@ struct MetalContext {
|
||||
} highestSupportedGpuFamily;
|
||||
|
||||
struct {
|
||||
bool staticTextureTargetError;
|
||||
bool a8xStaticTextureTargetError;
|
||||
} bugs;
|
||||
|
||||
// sampleCountLookup[requestedSamples] gives a <= sample count supported by the device.
|
||||
|
||||
@@ -53,14 +53,14 @@ Driver* MetalDriverFactory::create(MetalPlatform* const platform, const Platform
|
||||
// MetalRenderPrimitive : 24 many
|
||||
// MetalVertexBuffer : 32 moderate
|
||||
// -- less than or equal 32 bytes
|
||||
// MetalIndexBuffer : 40 moderate
|
||||
// MetalFence : 48 few
|
||||
// MetalIndexBuffer : 56 moderate
|
||||
// MetalBufferObject : 64 many
|
||||
// -- less than or equal 64 bytes
|
||||
// MetalBufferObject : 48 many
|
||||
// -- less than or equal 48 bytes
|
||||
// MetalSamplerGroup : 112 few
|
||||
// MetalProgram : 152 moderate
|
||||
// MetalTexture : 152 moderate
|
||||
// MetalSwapChain : 208 few
|
||||
// MetalSwapChain : 184 few
|
||||
// MetalRenderTarget : 272 few
|
||||
// MetalVertexBufferInfo : 552 moderate
|
||||
// -- less than or equal to 552 bytes
|
||||
@@ -162,10 +162,8 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
|
||||
sc[s] = [mContext->device supportsTextureSampleCount:s] ? s : sc[s - 1];
|
||||
}
|
||||
|
||||
mContext->bugs.staticTextureTargetError =
|
||||
[mContext->device.name containsString:@"Apple A8X GPU"] ||
|
||||
[mContext->device.name containsString:@"Apple A8 GPU"] ||
|
||||
[mContext->device.name containsString:@"Apple A7 GPU"];
|
||||
mContext->bugs.a8xStaticTextureTargetError =
|
||||
[mContext->device.name containsString:@"Apple A8X GPU"];
|
||||
|
||||
mContext->commandQueue = mPlatform.createCommandQueue(mContext->device);
|
||||
mContext->pipelineStateCache.setDevice(mContext->device);
|
||||
@@ -842,8 +840,8 @@ bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
|
||||
return true;
|
||||
case Workaround::ADRENO_UNIFORM_ARRAY_CRASH:
|
||||
return false;
|
||||
case Workaround::METAL_STATIC_TEXTURE_TARGET_ERROR:
|
||||
return mContext->bugs.staticTextureTargetError;
|
||||
case Workaround::A8X_STATIC_TEXTURE_TARGET_ERROR:
|
||||
return mContext->bugs.a8xStaticTextureTargetError;
|
||||
case Workaround::DISABLE_BLIT_INTO_TEXTURE_ARRAY:
|
||||
return false;
|
||||
default:
|
||||
@@ -2061,10 +2059,6 @@ void MetalDriver::enumerateBoundBuffers(BufferObjectBinding bindingType,
|
||||
void MetalDriver::resetState(int) {
|
||||
}
|
||||
|
||||
void MetalDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
|
||||
mHandleAllocator.associateTagToHandle(handleId, std::move(tag));
|
||||
}
|
||||
|
||||
void MetalDriver::runAtNextTick(const std::function<void()>& fn) noexcept {
|
||||
std::lock_guard<std::mutex> const lock(mTickOpsLock);
|
||||
mTickOps.push_back(fn);
|
||||
|
||||
@@ -556,6 +556,8 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le
|
||||
descriptor.usage = getMetalTextureUsage(usage);
|
||||
descriptor.storageMode = MTLStorageModePrivate;
|
||||
texture = [context.device newTextureWithDescriptor:descriptor];
|
||||
FILAMENT_CHECK_POSTCONDITION(texture != nil)
|
||||
<< "Could not create Metal texture. Out of memory?";
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
@@ -570,6 +572,8 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le
|
||||
descriptor.usage = getMetalTextureUsage(usage);
|
||||
descriptor.storageMode = MTLStorageModePrivate;
|
||||
texture = [context.device newTextureWithDescriptor:descriptor];
|
||||
FILAMENT_CHECK_POSTCONDITION(texture != nil)
|
||||
<< "Could not create Metal texture. Out of memory?";
|
||||
break;
|
||||
case SamplerType::SAMPLER_3D:
|
||||
descriptor = [MTLTextureDescriptor new];
|
||||
@@ -582,6 +586,8 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le
|
||||
descriptor.usage = getMetalTextureUsage(usage);
|
||||
descriptor.storageMode = MTLStorageModePrivate;
|
||||
texture = [context.device newTextureWithDescriptor:descriptor];
|
||||
FILAMENT_CHECK_POSTCONDITION(texture != nil)
|
||||
<< "Could not create Metal texture. Out of memory?";
|
||||
break;
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
// If we're using external textures (CVPixelBufferRefs), we don't need to make any
|
||||
@@ -590,12 +596,6 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le
|
||||
break;
|
||||
}
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(target == SamplerType::SAMPLER_EXTERNAL || texture != nil)
|
||||
<< "Could not create Metal texture (SamplerType = " << int(target)
|
||||
<< ", levels = " << int(levels) << ", MTLPixelFormat = " << int(devicePixelFormat)
|
||||
<< ", width = " << width << ", height = " << height << ", depth = " << depth
|
||||
<< "). Out of memory?";
|
||||
|
||||
// If swizzling is set, set up a swizzled texture view that we'll use when sampling this texture.
|
||||
const bool isDefaultSwizzle =
|
||||
r == TextureSwizzle::CHANNEL_0 &&
|
||||
|
||||
@@ -388,7 +388,4 @@ void NoopDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
void NoopDriver::resetState(int) {
|
||||
}
|
||||
|
||||
void NoopDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -1044,11 +1044,6 @@ void OpenGLContext::resetState() noexcept {
|
||||
);
|
||||
glDepthMask(state.raster.depthMask);
|
||||
glDepthFunc(state.raster.depthFunc);
|
||||
|
||||
utils::slog.e << "stencil front=" <<
|
||||
state.stencil.front.func.func << " " <<
|
||||
state.stencil.front.func.ref << " " <<
|
||||
state.stencil.front.func.mask << " " << utils::io::endl;
|
||||
|
||||
// state.stencil
|
||||
glStencilFuncSeparate(
|
||||
|
||||
@@ -83,20 +83,12 @@
|
||||
#define HAS_MAPBUFFERS 1
|
||||
#endif
|
||||
|
||||
#define DEBUG_GROUP_MARKER_NONE 0x00 // no debug marker
|
||||
#define DEBUG_GROUP_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
|
||||
#define DEBUG_GROUP_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
|
||||
#define DEBUG_GROUP_MARKER_ALL 0x03 // all markers
|
||||
#define DEBUG_MARKER_NONE 0x00 // no debug marker
|
||||
#define DEBUG_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
|
||||
#define DEBUG_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
|
||||
#define DEBUG_MARKER_ALL 0x03 // all markers
|
||||
|
||||
#define DEBUG_MARKER_NONE 0x00 // no debug marker
|
||||
#define DEBUG_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
|
||||
#define DEBUG_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
|
||||
#define DEBUG_MARKER_ALL 0x03 // all markers
|
||||
|
||||
// set to the desired debug marker level (for user markers [default: All])
|
||||
#define DEBUG_GROUP_MARKER_LEVEL DEBUG_GROUP_MARKER_ALL
|
||||
|
||||
// set to the desired debug level (for internal debugging [Default: None])
|
||||
// set to the desired debug marker level
|
||||
#define DEBUG_MARKER_LEVEL DEBUG_MARKER_NONE
|
||||
|
||||
#if DEBUG_MARKER_LEVEL > DEBUG_MARKER_NONE
|
||||
@@ -204,37 +196,11 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
|
||||
|
||||
OpenGLDriver::DebugMarker::DebugMarker(OpenGLDriver& driver, const char* string) noexcept
|
||||
: driver(driver) {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#ifdef GL_EXT_debug_marker
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
|
||||
if (UTILS_LIKELY(driver.getContext().ext.EXT_debug_marker)) {
|
||||
glPushGroupMarkerEXT(GLsizei(strlen(string)), string);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_NAME_BEGIN(string);
|
||||
#endif
|
||||
#endif
|
||||
driver.pushGroupMarker(string, strlen(string));
|
||||
}
|
||||
|
||||
OpenGLDriver::DebugMarker::~DebugMarker() noexcept {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#ifdef GL_EXT_debug_marker
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
|
||||
if (UTILS_LIKELY(driver.getContext().ext.EXT_debug_marker)) {
|
||||
glPopGroupMarkerEXT();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_NAME_END();
|
||||
#endif
|
||||
#endif
|
||||
driver.popGroupMarker();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
@@ -460,20 +426,20 @@ void OpenGLDriver::setStencilState(StencilState ss) noexcept {
|
||||
|
||||
// stencil test / operation
|
||||
// GL_STENCIL_TEST must be enabled if we're testing OR writing to the stencil buffer.
|
||||
// if (UTILS_LIKELY(
|
||||
// ss.front.stencilFunc == StencilState::StencilFunction::A &&
|
||||
// ss.back.stencilFunc == StencilState::StencilFunction::A &&
|
||||
// ss.front.stencilOpDepthFail == StencilOperation::KEEP &&
|
||||
// ss.back.stencilOpDepthFail == StencilOperation::KEEP &&
|
||||
// ss.front.stencilOpStencilFail == StencilOperation::KEEP &&
|
||||
// ss.back.stencilOpStencilFail == StencilOperation::KEEP &&
|
||||
// ss.front.stencilOpDepthStencilPass == StencilOperation::KEEP &&
|
||||
// ss.back.stencilOpDepthStencilPass == StencilOperation::KEEP) && false) {
|
||||
// // that's equivalent to having the stencil test disabled
|
||||
// gl.disable(GL_STENCIL_TEST);
|
||||
// } else {
|
||||
if (UTILS_LIKELY(
|
||||
ss.front.stencilFunc == StencilState::StencilFunction::A &&
|
||||
ss.back.stencilFunc == StencilState::StencilFunction::A &&
|
||||
ss.front.stencilOpDepthFail == StencilOperation::KEEP &&
|
||||
ss.back.stencilOpDepthFail == StencilOperation::KEEP &&
|
||||
ss.front.stencilOpStencilFail == StencilOperation::KEEP &&
|
||||
ss.back.stencilOpStencilFail == StencilOperation::KEEP &&
|
||||
ss.front.stencilOpDepthStencilPass == StencilOperation::KEEP &&
|
||||
ss.back.stencilOpDepthStencilPass == StencilOperation::KEEP)) {
|
||||
// that's equivalent to having the stencil test disabled
|
||||
gl.disable(GL_STENCIL_TEST);
|
||||
} else {
|
||||
gl.enable(GL_STENCIL_TEST);
|
||||
// }
|
||||
}
|
||||
|
||||
// glStencilFuncSeparate() also sets the reference value, which may be used depending
|
||||
// on the stencilOp, so we always need to call glStencilFuncSeparate().
|
||||
@@ -481,9 +447,9 @@ void OpenGLDriver::setStencilState(StencilState ss) noexcept {
|
||||
getStencilFunc(ss.front.stencilFunc), ss.front.ref, ss.front.readMask,
|
||||
getStencilFunc(ss.back.stencilFunc), ss.back.ref, ss.back.readMask);
|
||||
|
||||
// if (UTILS_LIKELY(!ss.stencilWrite)) {
|
||||
// gl.stencilMaskSeparate(0x00, 0x00);
|
||||
// } else {
|
||||
if (UTILS_LIKELY(!ss.stencilWrite)) {
|
||||
gl.stencilMaskSeparate(0x00, 0x00);
|
||||
} else {
|
||||
// Stencil ops are only relevant when stencil write is enabled
|
||||
gl.stencilOpSeparate(
|
||||
getStencilOp(ss.front.stencilOpStencilFail),
|
||||
@@ -493,7 +459,7 @@ void OpenGLDriver::setStencilState(StencilState ss) noexcept {
|
||||
getStencilOp(ss.back.stencilOpDepthFail),
|
||||
getStencilOp(ss.back.stencilOpDepthStencilPass));
|
||||
gl.stencilMaskSeparate(ss.front.writeMask, ss.back.writeMask);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
@@ -2210,10 +2176,6 @@ void OpenGLDriver::makeCurrent(Handle<HwSwapChain> schDraw, Handle<HwSwapChain>
|
||||
// Updating driver objects
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void OpenGLDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
|
||||
mHandleAllocator.associateTagToHandle(handleId, std::move(tag));
|
||||
}
|
||||
|
||||
void OpenGLDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh,
|
||||
uint32_t index, Handle<HwBufferObject> boh) {
|
||||
DEBUG_MARKER()
|
||||
@@ -2379,15 +2341,6 @@ void OpenGLDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
|
||||
GLTexture const* const t = handle_cast<const GLTexture*>(th);
|
||||
assert_invariant(t);
|
||||
|
||||
if (UTILS_UNLIKELY(es2)
|
||||
#if defined(GL_EXT_texture_filter_anisotropic)
|
||||
|| UTILS_UNLIKELY(anisotropyWorkaround)
|
||||
#endif
|
||||
) {
|
||||
// We must set texture parameters on the texture itself.
|
||||
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
|
||||
}
|
||||
|
||||
SamplerParams params = pSamplers[i].s;
|
||||
if (UTILS_UNLIKELY(t->target == SamplerType::SAMPLER_EXTERNAL)) {
|
||||
// From OES_EGL_image_external spec:
|
||||
@@ -3198,14 +3151,14 @@ void OpenGLDriver::insertEventMarker(char const* string, uint32_t len) {
|
||||
void OpenGLDriver::pushGroupMarker(char const* string, uint32_t len) {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#ifdef GL_EXT_debug_marker
|
||||
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_OPENGL
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
|
||||
if (UTILS_LIKELY(mContext.ext.EXT_debug_marker)) {
|
||||
glPushGroupMarkerEXT(GLsizei(len ? len : strlen(string)), string);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_BACKEND
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_NAME_BEGIN(string);
|
||||
#endif
|
||||
@@ -3215,14 +3168,14 @@ void OpenGLDriver::pushGroupMarker(char const* string, uint32_t len) {
|
||||
void OpenGLDriver::popGroupMarker(int) {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#ifdef GL_EXT_debug_marker
|
||||
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_OPENGL
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
|
||||
if (UTILS_LIKELY(mContext.ext.EXT_debug_marker)) {
|
||||
glPopGroupMarkerEXT();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_BACKEND
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_NAME_END();
|
||||
#endif
|
||||
@@ -3928,7 +3881,6 @@ void OpenGLDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
}
|
||||
|
||||
void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
||||
DEBUG_MARKER()
|
||||
GLRenderPrimitive const* const rp = mBoundRenderPrimitive;
|
||||
if (UTILS_UNLIKELY(!rp || !mValidProgram)) {
|
||||
return;
|
||||
@@ -3950,7 +3902,6 @@ void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins
|
||||
}
|
||||
|
||||
void OpenGLDriver::draw2GLES2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
||||
DEBUG_MARKER()
|
||||
GLRenderPrimitive const* const rp = mBoundRenderPrimitive;
|
||||
if (UTILS_UNLIKELY(!rp || !mValidProgram)) {
|
||||
return;
|
||||
@@ -3971,7 +3922,6 @@ void OpenGLDriver::draw2GLES2(uint32_t indexOffset, uint32_t indexCount, uint32_
|
||||
}
|
||||
|
||||
void OpenGLDriver::scissor(Viewport scissor) {
|
||||
DEBUG_MARKER()
|
||||
setScissor(scissor);
|
||||
}
|
||||
|
||||
@@ -3991,7 +3941,6 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
|
||||
}
|
||||
|
||||
void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
|
||||
DEBUG_MARKER()
|
||||
getShaderCompilerService().tick();
|
||||
|
||||
OpenGLProgram* const p = handle_cast<OpenGLProgram*>(program);
|
||||
|
||||
@@ -81,7 +81,7 @@ public:
|
||||
const Platform::DriverConfig& driverConfig) noexcept;
|
||||
|
||||
class DebugMarker {
|
||||
UTILS_UNUSED OpenGLDriver& driver;
|
||||
OpenGLDriver& driver;
|
||||
public:
|
||||
DebugMarker(OpenGLDriver& driver, const char* string) noexcept;
|
||||
~DebugMarker() noexcept;
|
||||
|
||||
@@ -143,25 +143,21 @@ void TimerQueryNativeFactory::endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQ
|
||||
|
||||
driver.runEveryNowAndThen([&context = mContext, weak]() -> bool {
|
||||
auto state = weak.lock();
|
||||
if (!state) {
|
||||
// The timer query state has been destroyed on the way, very likely due to the IBL
|
||||
// prefilter context destruction. We still return true to get this element removed from
|
||||
// the query list.
|
||||
return true;
|
||||
if (state) {
|
||||
GLuint available = 0;
|
||||
context.procs.getQueryObjectuiv(state->gl.query, GL_QUERY_RESULT_AVAILABLE, &available);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
if (!available) {
|
||||
// we need to try this one again later
|
||||
return false;
|
||||
}
|
||||
GLuint64 elapsedTime = 0;
|
||||
// we won't end-up here if we're on ES and don't have GL_EXT_disjoint_timer_query
|
||||
context.procs.getQueryObjectui64v(state->gl.query, GL_QUERY_RESULT, &elapsedTime);
|
||||
state->elapsed.store((int64_t)elapsedTime, std::memory_order_relaxed);
|
||||
} else {
|
||||
state->elapsed.store(int64_t(TimerQueryResult::ERROR), std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
GLuint available = 0;
|
||||
context.procs.getQueryObjectuiv(state->gl.query, GL_QUERY_RESULT_AVAILABLE, &available);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
if (!available) {
|
||||
// we need to try this one again later
|
||||
return false;
|
||||
}
|
||||
GLuint64 elapsedTime = 0;
|
||||
// we won't end-up here if we're on ES and don't have GL_EXT_disjoint_timer_query
|
||||
context.procs.getQueryObjectui64v(state->gl.query, GL_QUERY_RESULT, &elapsedTime);
|
||||
state->elapsed.store((int64_t)elapsedTime, std::memory_order_relaxed);
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -26,28 +26,16 @@
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/JobSystem.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
using namespace utils;
|
||||
@@ -488,12 +476,6 @@ GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept {
|
||||
// check status of program linking and shader compilation, logs error and free all resources
|
||||
// in case of error.
|
||||
bool const success = checkProgramStatus(token);
|
||||
|
||||
// Unless we have matdbg, we panic if a program is invalid. Otherwise, we'd get a UB.
|
||||
// The compilation error has been logged to log.e by this point.
|
||||
FILAMENT_CHECK_POSTCONDITION(FILAMENT_ENABLE_MATDBG || success)
|
||||
<< "OpenGL program " << token->name.c_str_safe() << " failed to link or compile";
|
||||
|
||||
if (UTILS_LIKELY(success)) {
|
||||
program = token->gl.program;
|
||||
// no need to keep the shaders around
|
||||
@@ -597,31 +579,24 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
|
||||
version = "#version 310 es\n";
|
||||
}
|
||||
|
||||
std::array<std::string_view, 5> sources = {
|
||||
version,
|
||||
prolog,
|
||||
specializationConstantString,
|
||||
packingFunctions,
|
||||
{ body.data(), body.size() - 1 } // null-terminated
|
||||
const std::array<const char*, 5> sources = {
|
||||
version.data(),
|
||||
prolog.data(),
|
||||
specializationConstantString.c_str(),
|
||||
packingFunctions.data(),
|
||||
body.data()
|
||||
};
|
||||
|
||||
// Some of the sources may be zero-length. Remove them as to avoid passing lengths of
|
||||
// zero to glShaderSource(). glShaderSource should work with lengths of zero, but some
|
||||
// drivers instead interpret zero as a sentinel for a null-terminated string.
|
||||
auto partitionPoint = std::stable_partition(
|
||||
sources.begin(), sources.end(), [](std::string_view s) { return !s.empty(); });
|
||||
size_t count = std::distance(sources.begin(), partitionPoint);
|
||||
|
||||
std::array<const char*, 5> shaderStrings;
|
||||
std::array<GLint, 5> lengths;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
shaderStrings[i] = sources[i].data();
|
||||
lengths[i] = sources[i].size();
|
||||
}
|
||||
const std::array<GLint, 5> lengths = {
|
||||
(GLint)version.length(),
|
||||
(GLint)prolog.length(),
|
||||
(GLint)specializationConstantString.length(),
|
||||
(GLint)packingFunctions.length(),
|
||||
(GLint)body.length() - 1 // null terminated
|
||||
};
|
||||
|
||||
GLuint const shaderId = glCreateShader(glShaderType);
|
||||
glShaderSource(shaderId, count, shaderStrings.data(), lengths.data());
|
||||
|
||||
glShaderSource(shaderId, sources.size(), sources.data(), lengths.data());
|
||||
glCompileShader(shaderId);
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
@@ -28,7 +28,6 @@ VulkanBuffer::VulkanBuffer(VmaAllocator allocator, VulkanStagePool& stagePool,
|
||||
: mAllocator(allocator),
|
||||
mStagePool(stagePool),
|
||||
mUsage(usage),
|
||||
mUpdatedOffset(0),
|
||||
mUpdatedBytes(0) {
|
||||
// for now make sure that only 1 bit is set in usage
|
||||
// (because loadFromCpu() assumes that somewhat)
|
||||
@@ -81,7 +80,6 @@ void VulkanBuffer::loadFromCpu(VkCommandBuffer cmdbuf, const void* cpuData, uint
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.buffer = mGpuBuffer,
|
||||
.offset = mUpdatedOffset,
|
||||
.size = mUpdatedBytes,
|
||||
};
|
||||
vkCmdPipelineBarrier(cmdbuf, srcStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 1,
|
||||
@@ -95,7 +93,6 @@ void VulkanBuffer::loadFromCpu(VkCommandBuffer cmdbuf, const void* cpuData, uint
|
||||
};
|
||||
vkCmdCopyBuffer(cmdbuf, stage->buffer, mGpuBuffer, 1, ®ion);
|
||||
|
||||
mUpdatedOffset = byteOffset;
|
||||
mUpdatedBytes = numBytes;
|
||||
|
||||
// Firstly, ensure that the copy finishes before the next draw call.
|
||||
|
||||
@@ -42,7 +42,6 @@ private:
|
||||
VmaAllocation mGpuMemory = VK_NULL_HANDLE;
|
||||
VkBuffer mGpuBuffer = VK_NULL_HANDLE;
|
||||
VkBufferUsageFlags mUsage = {};
|
||||
uint32_t mUpdatedOffset = 0;
|
||||
uint32_t mUpdatedBytes = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -59,11 +59,7 @@ VkExtent2D VulkanAttachment::getExtent2D() const {
|
||||
|
||||
VkImageView VulkanAttachment::getImageView() {
|
||||
assert_invariant(texture);
|
||||
VkImageSubresourceRange range = getSubresourceRange();
|
||||
if (range.layerCount > 1) {
|
||||
return texture->getMultiviewAttachmentView(range);
|
||||
}
|
||||
return texture->getAttachmentView(range);
|
||||
return texture->getAttachmentView(getSubresourceRange());
|
||||
}
|
||||
|
||||
bool VulkanAttachment::isDepth() const {
|
||||
@@ -77,7 +73,7 @@ VkImageSubresourceRange VulkanAttachment::getSubresourceRange() const {
|
||||
.baseMipLevel = uint32_t(level),
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = uint32_t(layer),
|
||||
.layerCount = layerCount,
|
||||
.layerCount = 1,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -43,8 +43,6 @@ struct VulkanCommandBuffer;
|
||||
struct VulkanAttachment {
|
||||
VulkanTexture* texture = nullptr;
|
||||
uint8_t level = 0;
|
||||
uint8_t baseViewIndex = 0;
|
||||
uint8_t layerCount = 1;
|
||||
uint16_t layer = 0;
|
||||
|
||||
bool isDepth() const;
|
||||
|
||||
@@ -595,8 +595,6 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
colorTargets[i] = {
|
||||
.texture = mResourceAllocator.handle_cast<VulkanTexture*>(color[i].handle),
|
||||
.level = color[i].level,
|
||||
.baseViewIndex = color[i].baseViewIndex,
|
||||
.layerCount = layerCount,
|
||||
.layer = color[i].layer,
|
||||
};
|
||||
UTILS_UNUSED_IN_RELEASE VkExtent2D extent = colorTargets[i].getExtent2D();
|
||||
@@ -611,8 +609,6 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
depthStencil[0] = {
|
||||
.texture = mResourceAllocator.handle_cast<VulkanTexture*>(depth.handle),
|
||||
.level = depth.level,
|
||||
.baseViewIndex = depth.baseViewIndex,
|
||||
.layerCount = layerCount,
|
||||
.layer = depth.layer,
|
||||
};
|
||||
UTILS_UNUSED_IN_RELEASE VkExtent2D extent = depthStencil[0].getExtent2D();
|
||||
@@ -625,8 +621,6 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
depthStencil[1] = {
|
||||
.texture = mResourceAllocator.handle_cast<VulkanTexture*>(stencil.handle),
|
||||
.level = stencil.level,
|
||||
.baseViewIndex = stencil.baseViewIndex,
|
||||
.layerCount = layerCount,
|
||||
.layer = stencil.layer,
|
||||
};
|
||||
UTILS_UNUSED_IN_RELEASE VkExtent2D extent = depthStencil[1].getExtent2D();
|
||||
@@ -643,7 +637,7 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
|
||||
auto renderTarget = mResourceAllocator.construct<VulkanRenderTarget>(rth, mPlatform->getDevice(),
|
||||
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, width, height,
|
||||
samples, colorTargets, depthStencil, mStagePool, layerCount);
|
||||
samples, colorTargets, depthStencil, mStagePool);
|
||||
mResourceManager.acquire(renderTarget);
|
||||
}
|
||||
|
||||
@@ -992,15 +986,15 @@ FeatureLevel VulkanDriver::getFeatureLevel() {
|
||||
|
||||
// If the max sampler counts do not meet FL2 standards, then this is an FL1 device.
|
||||
const auto& fl2 = FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_2];
|
||||
if (limits.maxPerStageDescriptorSamplers < fl2.MAX_VERTEX_SAMPLER_COUNT ||
|
||||
limits.maxPerStageDescriptorSamplers < fl2.MAX_FRAGMENT_SAMPLER_COUNT) {
|
||||
if (fl2.MAX_VERTEX_SAMPLER_COUNT < limits.maxPerStageDescriptorSamplers ||
|
||||
fl2.MAX_FRAGMENT_SAMPLER_COUNT < limits.maxPerStageDescriptorSamplers) {
|
||||
return FeatureLevel::FEATURE_LEVEL_1;
|
||||
}
|
||||
|
||||
// If the max sampler counts do not meet FL3 standards, then this is an FL2 device.
|
||||
const auto& fl3 = FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_3];
|
||||
if (limits.maxPerStageDescriptorSamplers < fl3.MAX_VERTEX_SAMPLER_COUNT||
|
||||
limits.maxPerStageDescriptorSamplers < fl3.MAX_FRAGMENT_SAMPLER_COUNT) {
|
||||
if (fl3.MAX_VERTEX_SAMPLER_COUNT < limits.maxPerStageDescriptorSamplers ||
|
||||
fl3.MAX_FRAGMENT_SAMPLER_COUNT < limits.maxPerStageDescriptorSamplers) {
|
||||
return FeatureLevel::FEATURE_LEVEL_2;
|
||||
}
|
||||
|
||||
@@ -1273,8 +1267,6 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t const renderTargetLayerCount = rt->getLayerCount();
|
||||
|
||||
// Create the VkRenderPass or fetch it from cache.
|
||||
VulkanFboCache::RenderPassKey rpkey = {
|
||||
.initialColorLayoutMask = 0,
|
||||
@@ -1285,12 +1277,10 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
.discardEnd = discardEndVal,
|
||||
.samples = rt->getSamples(),
|
||||
.subpassMask = uint8_t(params.subpassMask),
|
||||
.viewCount = renderTargetLayerCount,
|
||||
};
|
||||
for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
|
||||
const VulkanAttachment& info = rt->getColor(i);
|
||||
if (info.texture) {
|
||||
assert_invariant(info.layerCount == renderTargetLayerCount);
|
||||
rpkey.initialColorLayoutMask |= 1 << i;
|
||||
rpkey.colorFormat[i] = info.getFormat();
|
||||
if (rpkey.samples > 1 && info.texture->samples == 1) {
|
||||
@@ -2032,10 +2022,6 @@ void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, cons
|
||||
void VulkanDriver::resetState(int) {
|
||||
}
|
||||
|
||||
void VulkanDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
|
||||
mResourceAllocator.associateHandle(handleId, std::move(tag));
|
||||
}
|
||||
|
||||
// explicit instantiation of the Dispatcher
|
||||
template class ConcreteDispatcher<VulkanDriver>;
|
||||
|
||||
|
||||
@@ -43,7 +43,6 @@ bool VulkanFboCache::RenderPassEq::operator()(const RenderPassKey& k1,
|
||||
if (k1.samples != k2.samples) return false;
|
||||
if (k1.needsResolveMask != k2.needsResolveMask) return false;
|
||||
if (k1.subpassMask != k2.subpassMask) return false;
|
||||
if (k1.viewCount != k2.viewCount) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -187,25 +186,6 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
|
||||
.pDependencies = dependencies
|
||||
};
|
||||
|
||||
VkRenderPassMultiviewCreateInfo multiviewCreateInfo = {};
|
||||
uint32_t const subpassViewMask = (1 << config.viewCount) - 1;
|
||||
// Prepare a view mask array for the maximum number of subpasses. All subpasses have all views
|
||||
// activated.
|
||||
uint32_t const viewMasks[2] = {subpassViewMask, subpassViewMask};
|
||||
if (config.viewCount > 1) {
|
||||
// Fill the multiview create info.
|
||||
multiviewCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO;
|
||||
multiviewCreateInfo.pNext = nullptr;
|
||||
multiviewCreateInfo.subpassCount = hasSubpasses ? 2u : 1u;
|
||||
multiviewCreateInfo.pViewMasks = viewMasks;
|
||||
multiviewCreateInfo.dependencyCount = 0;
|
||||
multiviewCreateInfo.pViewOffsets = nullptr;
|
||||
multiviewCreateInfo.correlationMaskCount = 1;
|
||||
multiviewCreateInfo.pCorrelationMasks = &subpassViewMask;
|
||||
|
||||
renderPassInfo.pNext = &multiviewCreateInfo;
|
||||
}
|
||||
|
||||
int attachmentIndex = 0;
|
||||
|
||||
// Populate the Color Attachments.
|
||||
|
||||
@@ -64,7 +64,7 @@ public:
|
||||
uint8_t samples; // 1 byte
|
||||
uint8_t needsResolveMask; // 1 byte
|
||||
uint8_t subpassMask; // 1 byte
|
||||
uint8_t viewCount; // 1 byte
|
||||
uint8_t padding2; // 1 byte
|
||||
};
|
||||
struct RenderPassVal {
|
||||
VkRenderPass handle;
|
||||
|
||||
@@ -323,12 +323,11 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
|
||||
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
|
||||
uint32_t width, uint32_t height, uint8_t samples,
|
||||
VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
|
||||
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool, uint8_t layerCount)
|
||||
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool)
|
||||
: HwRenderTarget(width, height),
|
||||
VulkanResource(VulkanResourceType::RENDER_TARGET),
|
||||
mOffscreen(true),
|
||||
mSamples(samples),
|
||||
mLayerCount(layerCount) {
|
||||
mSamples(samples) {
|
||||
for (int index = 0; index < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; index++) {
|
||||
mColor[index] = color[index];
|
||||
}
|
||||
|
||||
@@ -304,7 +304,7 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource {
|
||||
VulkanContext const& context, VmaAllocator allocator,
|
||||
VulkanCommands* commands, uint32_t width, uint32_t height,
|
||||
uint8_t samples, VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
|
||||
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool, uint8_t layerCount);
|
||||
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool);
|
||||
|
||||
// Creates a special "default" render target (i.e. associated with the swap chain)
|
||||
explicit VulkanRenderTarget();
|
||||
@@ -319,7 +319,6 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource {
|
||||
VulkanAttachment& getMsaaDepth();
|
||||
uint8_t getColorTargetCount(const VulkanRenderPass& pass) const;
|
||||
uint8_t getSamples() const { return mSamples; }
|
||||
uint8_t getLayerCount() const { return mLayerCount; }
|
||||
bool hasDepth() const { return mDepth.texture; }
|
||||
bool isSwapChain() const { return !mOffscreen; }
|
||||
void bindToSwapChain(VulkanSwapChain& surf);
|
||||
@@ -331,7 +330,6 @@ private:
|
||||
VulkanAttachment mMsaaDepthAttachment = {};
|
||||
const bool mOffscreen : 1;
|
||||
uint8_t mSamples : 7;
|
||||
uint8_t mLayerCount = 1;
|
||||
};
|
||||
|
||||
struct VulkanBufferObject;
|
||||
|
||||
@@ -105,10 +105,6 @@ public:
|
||||
mHandleAllocatorImpl.deallocate(handle, obj);
|
||||
}
|
||||
|
||||
inline void associateHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept {
|
||||
mHandleAllocatorImpl.associateTagToHandle(id, std::move(tag));
|
||||
}
|
||||
|
||||
private:
|
||||
AllocatorImpl mHandleAllocatorImpl;
|
||||
|
||||
|
||||
@@ -387,15 +387,12 @@ void VulkanTexture::setPrimaryRange(uint32_t minMiplevel, uint32_t maxMiplevel)
|
||||
}
|
||||
|
||||
VkImageView VulkanTexture::getAttachmentView(VkImageSubresourceRange range) {
|
||||
// Attachments should only have one mipmap level and one layer.
|
||||
range.levelCount = 1;
|
||||
range.layerCount = 1;
|
||||
return getImageView(range, VK_IMAGE_VIEW_TYPE_2D, {});
|
||||
}
|
||||
|
||||
VkImageView VulkanTexture::getMultiviewAttachmentView(VkImageSubresourceRange range) {
|
||||
return getImageView(range, VK_IMAGE_VIEW_TYPE_2D_ARRAY, {});
|
||||
}
|
||||
|
||||
VkImageView VulkanTexture::getViewForType(VkImageSubresourceRange const& range, VkImageViewType type) {
|
||||
return getImageView(range, type, mSwizzle);
|
||||
}
|
||||
|
||||
@@ -73,10 +73,6 @@ struct VulkanTexture : public HwTexture, VulkanResource {
|
||||
// and the identity swizzle.
|
||||
VkImageView getAttachmentView(VkImageSubresourceRange range);
|
||||
|
||||
// Gets or creates a cached VkImageView for a single subresource that can be used as a render
|
||||
// target attachment when rendering with multiview.
|
||||
VkImageView getMultiviewAttachmentView(VkImageSubresourceRange range);
|
||||
|
||||
// This is a workaround for the first few frames where we're waiting for the texture to actually
|
||||
// be uploaded. In that case, we bind the sampler to an empty texture, but the corresponding
|
||||
// imageView needs to be of the right type. Hence, we provide an option to indicate the
|
||||
|
||||
@@ -172,7 +172,7 @@ public:
|
||||
UTILS_UNUSED VkResult result = vkAllocateDescriptorSets(mDevice, &allocInfo, &vkSet);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Failed to allocate descriptor set code=" << result << " size=" << mSize
|
||||
<< " capacity=" << mCapacity << " count=" << mUnusedCount;
|
||||
<< " capacity=" << mCapacity << " count=" << "%s";
|
||||
mSize++;
|
||||
return createSet(layout->bitmask, vkSet);
|
||||
}
|
||||
|
||||
@@ -33,11 +33,11 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
|
||||
|
||||
// build the push constant layout key
|
||||
uint32_t pushConstantRangeCount = program->getPushConstantRangeCount();
|
||||
auto const& pushConstantRanges = program->getPushConstantRanges();
|
||||
auto const& pushCostantRanges = program->getPushConstantRanges();
|
||||
if (pushConstantRangeCount > 0) {
|
||||
assert_invariant(pushConstantRangeCount <= Program::SHADER_TYPE_COUNT);
|
||||
for (uint8_t i = 0; i < pushConstantRangeCount; ++i) {
|
||||
auto const& range = pushConstantRanges[i];
|
||||
auto const& range = pushCostantRanges[i];
|
||||
auto& pushConstant = key.pushConstant[i];
|
||||
if (range.stageFlags & VK_SHADER_STAGE_VERTEX_BIT) {
|
||||
pushConstant.stage = static_cast<uint8_t>(ShaderStage::VERTEX);
|
||||
@@ -52,8 +52,9 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
|
||||
}
|
||||
}
|
||||
|
||||
if (PipelineLayoutMap::iterator iter = mPipelineLayouts.find(key); iter != mPipelineLayouts.end()) {
|
||||
PipelineLayoutCacheEntry& entry = iter.value();
|
||||
auto iter = mPipelineLayouts.find(key);
|
||||
if (iter != mPipelineLayouts.end()) {
|
||||
PipelineLayoutCacheEntry& entry = mPipelineLayouts[key];
|
||||
entry.lastUsed = mTimestamp++;
|
||||
return entry.handle;
|
||||
}
|
||||
@@ -64,7 +65,7 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
|
||||
.setLayoutCount = (uint32_t) descSetLayoutCount,
|
||||
.pSetLayouts = key.descSetLayouts.data(),
|
||||
.pushConstantRangeCount = pushConstantRangeCount,
|
||||
.pPushConstantRanges = pushConstantRanges,
|
||||
.pPushConstantRanges = pushCostantRanges,
|
||||
};
|
||||
|
||||
VkPipelineLayout layout;
|
||||
|
||||
@@ -64,15 +64,14 @@ First determine the Filament material and variant that causes the problem.
|
||||
What we want is the "raw" GLSL version of the shader, before any optimizations / cross-compilation
|
||||
happens.
|
||||
|
||||
We can use the `--save-raw-variants` debug flag in matc to export each GLSL
|
||||
shader to a file. For example:
|
||||
The easiest way I've found to do this is by adding a `std::cout` statement inside `MaterialBuilder`,
|
||||
right after `sg.createFragmentProgram` or `sg.createVertexProgram` is called. For example,
|
||||
|
||||
```
|
||||
matc --save-raw-variants --optimize-size --variant-filter fog,ssr,vsm,stereo \
|
||||
-a all -p all -o mymaterial.filamat mymaterial.mat
|
||||
if (v.variant.key == 8u) { std::cout << shader << std::endl; }
|
||||
```
|
||||
|
||||
Files will be named like `mymaterial_0x05.frag` or `mymaterial_0x05.vert`.
|
||||
Then, I invoke `matc` and redirect stdout to a .frag or .vert file.
|
||||
|
||||
Note that gltfio material "templates" first go through a build step. After building gltfio, the
|
||||
gltfio Filament materials are output to:
|
||||
|
||||
@@ -54,7 +54,7 @@ public:
|
||||
using BufferDescriptor = backend::BufferDescriptor;
|
||||
using BindingType = backend::BufferObjectBinding;
|
||||
|
||||
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
|
||||
class Builder : public BuilderBase<BuilderDetails> {
|
||||
friend struct BuilderDetails;
|
||||
public:
|
||||
Builder() noexcept;
|
||||
@@ -78,21 +78,6 @@ public:
|
||||
*/
|
||||
Builder& bindingType(BindingType bindingType) noexcept;
|
||||
|
||||
/**
|
||||
* Associate an optional name with this BufferObject for debugging purposes.
|
||||
*
|
||||
* name will show in error messages and should be kept as short as possible. The name is
|
||||
* truncated to a maximum of 128 characters.
|
||||
*
|
||||
* The name string is copied during this method so clients may free its memory after
|
||||
* the function returns.
|
||||
*
|
||||
* @param name A string to identify this BufferObject
|
||||
* @param len Length of name, should be less than or equal to 128
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
|
||||
|
||||
/**
|
||||
* Creates the BufferObject and returns a pointer to it. After creation, the buffer
|
||||
* object is uninitialized. Use BufferObject::setBuffer() to initialize it.
|
||||
|
||||
@@ -350,12 +350,9 @@ public:
|
||||
uint32_t resourceAllocatorCacheSizeMB = 64;
|
||||
|
||||
/*
|
||||
* This value determines how many frames texture entries are kept for in the cache. This
|
||||
* is a soft limit, meaning some texture older than this are allowed to stay in the cache.
|
||||
* Typically only one texture is evicted per frame.
|
||||
* The default is 1.
|
||||
* This value determines for how many frames are texture entries kept in the cache.
|
||||
*/
|
||||
uint32_t resourceAllocatorCacheMaxAge = 1;
|
||||
uint32_t resourceAllocatorCacheMaxAge = 2;
|
||||
|
||||
/*
|
||||
* Disable backend handles use-after-free checks.
|
||||
@@ -896,29 +893,6 @@ public:
|
||||
/** Tells whether an InstanceBuffer object is valid */
|
||||
bool isValid(const InstanceBuffer* UTILS_NULLABLE p) const;
|
||||
|
||||
/**
|
||||
* Retrieve the count of each resource tracked by Engine.
|
||||
* This is intended for debugging.
|
||||
* @{
|
||||
*/
|
||||
size_t getBufferObjectCount() const noexcept;
|
||||
size_t getViewCount() const noexcept;
|
||||
size_t getSceneCount() const noexcept;
|
||||
size_t getSwapChainCount() const noexcept;
|
||||
size_t getStreamCount() const noexcept;
|
||||
size_t getIndexBufferCount() const noexcept;
|
||||
size_t getSkinningBufferCount() const noexcept;
|
||||
size_t getMorphTargetBufferCount() const noexcept;
|
||||
size_t getInstanceBufferCount() const noexcept;
|
||||
size_t getVertexBufferCount() const noexcept;
|
||||
size_t getIndirectLightCount() const noexcept;
|
||||
size_t getMaterialCount() const noexcept;
|
||||
size_t getTextureCount() const noexcept;
|
||||
size_t getSkyboxeCount() const noexcept;
|
||||
size_t getColorGradingCount() const noexcept;
|
||||
size_t getRenderTargetCount() const noexcept;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* Kicks the hardware thread (e.g. the OpenGL, Vulkan or Metal thread) and blocks until
|
||||
* all commands to this point are executed. Note that does guarantee that the
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/PrivateImplementation.h>
|
||||
#include <utils/CString.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -55,22 +54,6 @@ public:
|
||||
template<typename T>
|
||||
using BuilderBase = utils::PrivateImplementation<T>;
|
||||
|
||||
void builderMakeName(utils::CString& outName, const char* name, size_t len) noexcept;
|
||||
|
||||
template <typename Builder>
|
||||
class UTILS_PUBLIC BuilderNameMixin {
|
||||
public:
|
||||
Builder& name(const char* name, size_t len) noexcept {
|
||||
builderMakeName(mName, name, len);
|
||||
return static_cast<Builder&>(*this);
|
||||
}
|
||||
|
||||
utils::CString const& getName() const noexcept { return mName; }
|
||||
|
||||
private:
|
||||
utils::CString mName;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
#endif // TNT_FILAMENT_FILAMENTAPI_H
|
||||
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
UINT = uint8_t(backend::ElementType::UINT), //!< 32-bit indices
|
||||
};
|
||||
|
||||
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
|
||||
class Builder : public BuilderBase<BuilderDetails> {
|
||||
friend struct BuilderDetails;
|
||||
public:
|
||||
Builder() noexcept;
|
||||
@@ -83,21 +83,6 @@ public:
|
||||
*/
|
||||
Builder& bufferType(IndexType indexType) noexcept;
|
||||
|
||||
/**
|
||||
* Associate an optional name with this IndexBuffer for debugging purposes.
|
||||
*
|
||||
* name will show in error messages and should be kept as short as possible. The name is
|
||||
* truncated to a maximum of 128 characters.
|
||||
*
|
||||
* The name string is copied during this method so clients may free its memory after
|
||||
* the function returns.
|
||||
*
|
||||
* @param name A string to identify this IndexBuffer
|
||||
* @param len Length of name, should be less than or equal to 128
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
|
||||
|
||||
/**
|
||||
* Creates the IndexBuffer object and returns a pointer to it. After creation, the index
|
||||
* buffer is uninitialized. Use IndexBuffer::setBuffer() to initialize the IndexBuffer.
|
||||
|
||||
@@ -38,7 +38,7 @@ class UTILS_PUBLIC InstanceBuffer : public FilamentAPI {
|
||||
struct BuilderDetails;
|
||||
|
||||
public:
|
||||
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
|
||||
class Builder : public BuilderBase<BuilderDetails> {
|
||||
friend struct BuilderDetails;
|
||||
|
||||
public:
|
||||
@@ -70,21 +70,6 @@ public:
|
||||
*/
|
||||
Builder& localTransforms(math::mat4f const* UTILS_NULLABLE localTransforms) noexcept;
|
||||
|
||||
/**
|
||||
* Associate an optional name with this InstanceBuffer for debugging purposes.
|
||||
*
|
||||
* name will show in error messages and should be kept as short as possible. The name is
|
||||
* truncated to a maximum of 128 characters.
|
||||
*
|
||||
* The name string is copied during this method so clients may free its memory after
|
||||
* the function returns.
|
||||
*
|
||||
* @param name A string to identify this InstanceBuffer
|
||||
* @param len Length of name, should be less than or equal to 128
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
|
||||
|
||||
/**
|
||||
* Creates the InstanceBuffer object and returns a pointer to it.
|
||||
*/
|
||||
|
||||
@@ -39,7 +39,7 @@ class UTILS_PUBLIC MorphTargetBuffer : public FilamentAPI {
|
||||
struct BuilderDetails;
|
||||
|
||||
public:
|
||||
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
|
||||
class Builder : public BuilderBase<BuilderDetails> {
|
||||
friend struct BuilderDetails;
|
||||
public:
|
||||
Builder() noexcept;
|
||||
@@ -63,21 +63,6 @@ public:
|
||||
*/
|
||||
Builder& count(size_t count) noexcept;
|
||||
|
||||
/**
|
||||
* Associate an optional name with this MorphTargetBuffer for debugging purposes.
|
||||
*
|
||||
* name will show in error messages and should be kept as short as possible. The name is
|
||||
* truncated to a maximum of 128 characters.
|
||||
*
|
||||
* The name string is copied during this method so clients may free its memory after
|
||||
* the function returns.
|
||||
*
|
||||
* @param name A string to identify this MorphTargetBuffer
|
||||
* @param len Length of name, should be less than or equal to 128
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
|
||||
|
||||
/**
|
||||
* Creates the MorphTargetBuffer object and returns a pointer to it.
|
||||
*
|
||||
|
||||
@@ -22,11 +22,9 @@
|
||||
#include <filament/FilamentAPI.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
@@ -83,37 +81,6 @@ public:
|
||||
UTILS_DEPRECATED uint64_t vsyncOffsetNanos = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Timing information about a frame
|
||||
* @see getFrameInfoHistory()
|
||||
*/
|
||||
struct FrameInfo {
|
||||
using time_point_ns = int64_t;
|
||||
using duration_ns = int64_t;
|
||||
uint32_t frameId; //!< monotonically increasing frame identifier
|
||||
duration_ns frameTime; //!< frame duration on the GPU in nanosecond [ns]
|
||||
duration_ns denoisedFrameTime; //!< denoised frame duration on the GPU in [ns]
|
||||
time_point_ns beginFrame; //!< Renderer::beginFrame() time since epoch [ns]
|
||||
time_point_ns endFrame; //!< Renderer::endFrame() time since epoch [ns]
|
||||
time_point_ns backendBeginFrame; //!< Backend thread time of frame start since epoch [ns]
|
||||
time_point_ns backendEndFrame; //!< Backend thread time of frame end since epoch [ns]
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve an historic of frame timing information. The maximum frame history size is
|
||||
* given by getMaxFrameHistorySize().
|
||||
* @param historySize requested history size. The returned vector could be smaller.
|
||||
* @return A vector of FrameInfo.
|
||||
*/
|
||||
utils::FixedCapacityVector<Renderer::FrameInfo> getFrameInfoHistory(
|
||||
size_t historySize = 1) const noexcept;
|
||||
|
||||
/**
|
||||
* @return the maximum supported frame history size.
|
||||
* @see getFrameInfoHistory()
|
||||
*/
|
||||
size_t getMaxFrameHistorySize() const noexcept;
|
||||
|
||||
/**
|
||||
* Use FrameRateOptions to set the desired frame rate and control how quickly the system
|
||||
* reacts to GPU load changes.
|
||||
@@ -271,14 +238,6 @@ public:
|
||||
*/
|
||||
void setVsyncTime(uint64_t steadyClockTimeNano) noexcept;
|
||||
|
||||
/**
|
||||
* Call skipFrame when momentarily skipping frames, for instance if the content of the
|
||||
* scene doesn't change.
|
||||
*
|
||||
* @param vsyncSteadyClockTimeNano
|
||||
*/
|
||||
void skipFrame(uint64_t vsyncSteadyClockTimeNano = 0u);
|
||||
|
||||
/**
|
||||
* Set-up a frame for this Renderer.
|
||||
*
|
||||
|
||||
@@ -39,7 +39,7 @@ class UTILS_PUBLIC SkinningBuffer : public FilamentAPI {
|
||||
struct BuilderDetails;
|
||||
|
||||
public:
|
||||
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
|
||||
class Builder : public BuilderBase<BuilderDetails> {
|
||||
friend struct BuilderDetails;
|
||||
public:
|
||||
Builder() noexcept;
|
||||
@@ -69,21 +69,6 @@ public:
|
||||
*/
|
||||
Builder& initialize(bool initialize = true) noexcept;
|
||||
|
||||
/**
|
||||
* Associate an optional name with this SkinningBuffer for debugging purposes.
|
||||
*
|
||||
* name will show in error messages and should be kept as short as possible. The name is
|
||||
* truncated to a maximum of 128 characters.
|
||||
*
|
||||
* The name string is copied during this method so clients may free its memory after
|
||||
* the function returns.
|
||||
*
|
||||
* @param name A string to identify this SkinningBuffer
|
||||
* @param len Length of name, should be less than or equal to 128
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
|
||||
|
||||
/**
|
||||
* Creates the SkinningBuffer object and returns a pointer to it.
|
||||
*
|
||||
|
||||
@@ -94,7 +94,7 @@ public:
|
||||
*
|
||||
* To create a NATIVE stream, call the <pre>stream</pre> method on the builder.
|
||||
*/
|
||||
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
|
||||
class Builder : public BuilderBase<BuilderDetails> {
|
||||
friend struct BuilderDetails;
|
||||
public:
|
||||
Builder() noexcept;
|
||||
@@ -136,21 +136,6 @@ public:
|
||||
*/
|
||||
Builder& height(uint32_t height) noexcept;
|
||||
|
||||
/**
|
||||
* Associate an optional name with this Stream for debugging purposes.
|
||||
*
|
||||
* name will show in error messages and should be kept as short as possible. The name is
|
||||
* truncated to a maximum of 128 characters.
|
||||
*
|
||||
* The name string is copied during this method so clients may free its memory after
|
||||
* the function returns.
|
||||
*
|
||||
* @param name A string to identify this Stream
|
||||
* @param len Length of name, should be less than or equal to 128
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
|
||||
|
||||
/**
|
||||
* Creates the Stream object and returns a pointer to it.
|
||||
*
|
||||
|
||||
@@ -112,7 +112,7 @@ public:
|
||||
|
||||
|
||||
//! Use Builder to construct a Texture object instance
|
||||
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
|
||||
class Builder : public BuilderBase<BuilderDetails> {
|
||||
friend struct BuilderDetails;
|
||||
public:
|
||||
Builder() noexcept;
|
||||
@@ -202,21 +202,6 @@ public:
|
||||
*/
|
||||
Builder& swizzle(Swizzle r, Swizzle g, Swizzle b, Swizzle a) noexcept;
|
||||
|
||||
/**
|
||||
* Associate an optional name with this Texture for debugging purposes.
|
||||
*
|
||||
* name will show in error messages and should be kept as short as possible. The name is
|
||||
* truncated to a maximum of 128 characters.
|
||||
*
|
||||
* The name string is copied during this method so clients may free its memory after
|
||||
* the function returns.
|
||||
*
|
||||
* @param name A string to identify this Texture
|
||||
* @param len Length of name, should be less than or equal to 128
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
|
||||
|
||||
/**
|
||||
* Creates the Texture object and returns a pointer to it.
|
||||
*
|
||||
|
||||
@@ -61,7 +61,7 @@ public:
|
||||
using AttributeType = backend::ElementType;
|
||||
using BufferDescriptor = backend::BufferDescriptor;
|
||||
|
||||
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
|
||||
class Builder : public BuilderBase<BuilderDetails> {
|
||||
friend struct BuilderDetails;
|
||||
public:
|
||||
Builder() noexcept;
|
||||
@@ -158,21 +158,6 @@ public:
|
||||
*/
|
||||
Builder& advancedSkinning(bool enabled) noexcept;
|
||||
|
||||
/**
|
||||
* Associate an optional name with this VertexBuffer for debugging purposes.
|
||||
*
|
||||
* name will show in error messages and should be kept as short as possible. The name is
|
||||
* truncated to a maximum of 128 characters.
|
||||
*
|
||||
* The name string is copied during this method so clients may free its memory after
|
||||
* the function returns.
|
||||
*
|
||||
* @param name A string to identify this VertexBuffer
|
||||
* @param len Length of name, should be less than or equal to 128
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
|
||||
|
||||
/**
|
||||
* Creates the VertexBuffer object and returns a pointer to it.
|
||||
*
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Entity.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <math/mathfwd.h>
|
||||
|
||||
@@ -41,7 +40,6 @@ class CallbackHandler;
|
||||
|
||||
class Camera;
|
||||
class ColorGrading;
|
||||
class Engine;
|
||||
class MaterialInstance;
|
||||
class RenderTarget;
|
||||
class Scene;
|
||||
@@ -728,7 +726,7 @@ public:
|
||||
void setDebugCamera(Camera* UTILS_NULLABLE camera) noexcept;
|
||||
|
||||
//! debugging: returns a Camera from the point of view of *the* dominant directional light used for shadowing.
|
||||
utils::FixedCapacityVector<Camera const*> getDirectionalShadowCameras() const noexcept;
|
||||
Camera const* UTILS_NULLABLE getDirectionalShadowCamera() const noexcept;
|
||||
|
||||
|
||||
/** Result of a picking query */
|
||||
@@ -880,17 +878,6 @@ public:
|
||||
*/
|
||||
utils::Entity getFogEntity() const noexcept;
|
||||
|
||||
|
||||
/**
|
||||
* When certain temporal features are used (e.g.: TAA or Screen-space reflections), the view
|
||||
* keeps a history of previous frame renders associated with the Renderer the view was last
|
||||
* used with. When switching Renderer, it may be necessary to clear that history by calling
|
||||
* this method. Similarly, if the whole content of the screen change, like when a cut-scene
|
||||
* starts, clearing the history might be needed to avoid artifacts due to the previous frame
|
||||
* being very different.
|
||||
*/
|
||||
void clearFrameHistory(Engine& engine) noexcept;
|
||||
|
||||
/**
|
||||
* List of available ambient occlusion techniques
|
||||
* @deprecated use AmbientOcclusionOptions::enabled instead
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
#include "details/Engine.h"
|
||||
|
||||
#include "ResourceAllocator.h"
|
||||
|
||||
#include "details/BufferObject.h"
|
||||
#include "details/Camera.h"
|
||||
#include "details/Fence.h"
|
||||
@@ -268,71 +266,6 @@ bool Engine::isValid(const InstanceBuffer* p) const {
|
||||
return downcast(this)->isValid(downcast(p));
|
||||
}
|
||||
|
||||
size_t Engine::getBufferObjectCount() const noexcept {
|
||||
return downcast(this)->getBufferObjectCount();
|
||||
}
|
||||
|
||||
size_t Engine::getViewCount() const noexcept {
|
||||
return downcast(this)->getViewCount();
|
||||
}
|
||||
|
||||
size_t Engine::getSceneCount() const noexcept {
|
||||
return downcast(this)->getSceneCount();
|
||||
}
|
||||
|
||||
size_t Engine::getSwapChainCount() const noexcept {
|
||||
return downcast(this)->getSwapChainCount();
|
||||
}
|
||||
|
||||
size_t Engine::getStreamCount() const noexcept {
|
||||
return downcast(this)->getStreamCount();
|
||||
}
|
||||
|
||||
size_t Engine::getIndexBufferCount() const noexcept {
|
||||
return downcast(this)->getIndexBufferCount();
|
||||
}
|
||||
|
||||
size_t Engine::getSkinningBufferCount() const noexcept {
|
||||
return downcast(this)->getSkinningBufferCount();
|
||||
}
|
||||
|
||||
size_t Engine::getMorphTargetBufferCount() const noexcept {
|
||||
return downcast(this)->getMorphTargetBufferCount();
|
||||
}
|
||||
|
||||
size_t Engine::getInstanceBufferCount() const noexcept {
|
||||
return downcast(this)->getInstanceBufferCount();
|
||||
}
|
||||
|
||||
size_t Engine::getVertexBufferCount() const noexcept {
|
||||
return downcast(this)->getVertexBufferCount();
|
||||
}
|
||||
|
||||
size_t Engine::getIndirectLightCount() const noexcept {
|
||||
return downcast(this)->getIndirectLightCount();
|
||||
}
|
||||
|
||||
size_t Engine::getMaterialCount() const noexcept {
|
||||
return downcast(this)->getMaterialCount();
|
||||
}
|
||||
|
||||
size_t Engine::getTextureCount() const noexcept {
|
||||
return downcast(this)->getTextureCount();
|
||||
}
|
||||
|
||||
size_t Engine::getSkyboxeCount() const noexcept {
|
||||
return downcast(this)->getSkyboxeCount();
|
||||
}
|
||||
|
||||
size_t Engine::getColorGradingCount() const noexcept {
|
||||
return downcast(this)->getColorGradingCount();
|
||||
}
|
||||
|
||||
size_t Engine::getRenderTargetCount() const noexcept {
|
||||
return downcast(this)->getRenderTargetCount();
|
||||
}
|
||||
|
||||
|
||||
void Engine::flushAndWait() {
|
||||
downcast(this)->flushAndWait();
|
||||
}
|
||||
|
||||
@@ -1,31 +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 <filament/FilamentAPI.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace filament {
|
||||
|
||||
void builderMakeName(utils::CString& outName, const char* name, size_t len) noexcept {
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
size_t const length = std::min(len, size_t { 128u });
|
||||
outName = utils::CString(name, length);
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
@@ -21,9 +21,6 @@
|
||||
#include <fg/FrameGraphTexture.h>
|
||||
|
||||
#include <math/mat4.h>
|
||||
#include <math/vec2.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
|
||||
+55
-149
@@ -16,34 +16,30 @@
|
||||
|
||||
#include "FrameInfo.h"
|
||||
|
||||
#include <filament/Renderer.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <ratio>
|
||||
#include <math/scalar.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <cmath>
|
||||
|
||||
namespace filament {
|
||||
|
||||
using namespace utils;
|
||||
using namespace backend;
|
||||
|
||||
// this is to avoid a call to memmove
|
||||
template<class InputIterator, class OutputIterator>
|
||||
static inline
|
||||
void move_backward(InputIterator first, InputIterator last, OutputIterator result) {
|
||||
while (first != last) {
|
||||
*--result = *--last;
|
||||
}
|
||||
}
|
||||
|
||||
FrameInfoManager::FrameInfoManager(DriverApi& driver) noexcept {
|
||||
for (auto& query : mQueries) {
|
||||
query.handle = driver.createTimerQuery();
|
||||
query = driver.createTimerQuery();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,155 +47,65 @@ FrameInfoManager::~FrameInfoManager() noexcept = default;
|
||||
|
||||
void FrameInfoManager::terminate(DriverApi& driver) noexcept {
|
||||
for (auto& query : mQueries) {
|
||||
driver.destroyTimerQuery(query.handle);
|
||||
driver.destroyTimerQuery(query);
|
||||
}
|
||||
}
|
||||
|
||||
void FrameInfoManager::beginFrame(DriverApi& driver, Config const& config, uint32_t frameId) noexcept {
|
||||
auto& history = mFrameTimeHistory;
|
||||
// don't exceed the capacity, drop the oldest entry
|
||||
if (UTILS_LIKELY(history.size() == history.capacity())) {
|
||||
history.pop_back();
|
||||
}
|
||||
|
||||
// create a new entry
|
||||
auto& front = history.emplace_front(frameId);
|
||||
|
||||
// store the current time
|
||||
front.beginFrame = std::chrono::steady_clock::now();
|
||||
|
||||
// references are not invalidated by CircularQueue<>, so we can associate a reference to
|
||||
// the slot we created to the timer query used to find the frame time.
|
||||
mQueries[mIndex].pInfo = std::addressof(front);
|
||||
// issue the timer query
|
||||
driver.beginTimerQuery(mQueries[mIndex].handle);
|
||||
// issue the custom backend command to get the backend time
|
||||
driver.queueCommand([&front](){
|
||||
front.backendBeginFrame = std::chrono::steady_clock::now();
|
||||
});
|
||||
|
||||
// now is a good time to check the oldest active query
|
||||
while (mLast != mIndex) {
|
||||
uint64_t elapsed = 0;
|
||||
TimerQueryResult const result = driver.getTimerQueryValue(mQueries[mLast].handle, &elapsed);
|
||||
switch (result) {
|
||||
case TimerQueryResult::NOT_READY:
|
||||
// nothing to do
|
||||
break;
|
||||
case TimerQueryResult::ERROR:
|
||||
mLast = (mLast + 1) % POOL_COUNT;
|
||||
break;
|
||||
case TimerQueryResult::AVAILABLE: {
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_VALUE32("FrameInfo::elapsed", uint32_t(elapsed));
|
||||
// conversion to our duration happens here
|
||||
pFront = mQueries[mLast].pInfo;
|
||||
pFront->frameTime = std::chrono::duration<uint64_t, std::nano>(elapsed);
|
||||
mLast = (mLast + 1) % POOL_COUNT;
|
||||
denoiseFrameTime(history, config);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result != TimerQueryResult::AVAILABLE) {
|
||||
void FrameInfoManager::beginFrame(DriverApi& driver,Config const& config, uint32_t) noexcept {
|
||||
driver.beginTimerQuery(mQueries[mIndex]);
|
||||
uint64_t elapsed = 0;
|
||||
TimerQueryResult const result = driver.getTimerQueryValue(mQueries[mLast], &elapsed);
|
||||
switch (result) {
|
||||
case TimerQueryResult::NOT_READY:
|
||||
// nothing to do
|
||||
break;
|
||||
case TimerQueryResult::ERROR:
|
||||
mLast = (mLast + 1) % POOL_COUNT;
|
||||
break;
|
||||
case TimerQueryResult::AVAILABLE:
|
||||
mLast = (mLast + 1) % POOL_COUNT;
|
||||
// conversion to our duration happens here
|
||||
mFrameTime = std::chrono::duration<uint64_t, std::nano>(elapsed);
|
||||
break;
|
||||
}
|
||||
// read the pending timer queries until we find one that's not ready
|
||||
}
|
||||
|
||||
// keep this just for debugging
|
||||
if constexpr (false) {
|
||||
using namespace utils;
|
||||
auto h = getFrameInfoHistory(1);
|
||||
if (!h.empty()) {
|
||||
slog.d << frameId << ": "
|
||||
<< h[0].frameId << " (" << frameId - h[0].frameId << ")"
|
||||
<< ", Dm=" << h[0].endFrame - h[0].beginFrame
|
||||
<< ", L =" << h[0].backendBeginFrame - h[0].beginFrame
|
||||
<< ", Db=" << h[0].backendEndFrame - h[0].backendBeginFrame
|
||||
<< ", T =" << h[0].frameTime
|
||||
<< io::endl;
|
||||
}
|
||||
}
|
||||
update(config, mFrameTime);
|
||||
}
|
||||
|
||||
void FrameInfoManager::endFrame(DriverApi& driver) noexcept {
|
||||
auto& front = mFrameTimeHistory.front();
|
||||
// close the timer query
|
||||
driver.endTimerQuery(mQueries[mIndex].handle);
|
||||
// queue custom backend command to query the current time
|
||||
driver.queueCommand([&front](){
|
||||
// backend frame end-time
|
||||
front.backendEndFrame = std::chrono::steady_clock::now();
|
||||
// signal that the data is available
|
||||
front.ready.store(true, std::memory_order_release);
|
||||
});
|
||||
// and finally acquire the time on the main thread
|
||||
front.endFrame = std::chrono::steady_clock::now();
|
||||
driver.endTimerQuery(mQueries[mIndex]);
|
||||
mIndex = (mIndex + 1) % POOL_COUNT;
|
||||
}
|
||||
|
||||
void FrameInfoManager::denoiseFrameTime(FrameHistoryQueue& history, Config const& config) noexcept {
|
||||
assert_invariant(!history.empty());
|
||||
void FrameInfoManager::update(Config const& config,
|
||||
FrameInfoManager::duration lastFrameTime) noexcept {
|
||||
// keep an history of frame times
|
||||
auto& history = mFrameTimeHistory;
|
||||
|
||||
// find the first slot that has a valid frame duration
|
||||
size_t first = history.size();
|
||||
for (size_t i = 0, c = history.size(); i < c; ++i) {
|
||||
if (history[i].frameTime != duration(0)) {
|
||||
first = i;
|
||||
break;
|
||||
}
|
||||
// this is like doing { pop_back(); push_front(); }
|
||||
filament::move_backward(history.begin(), history.end() - 1, history.end());
|
||||
history[0].frameTime = lastFrameTime;
|
||||
|
||||
mFrameTimeHistorySize = std::min(++mFrameTimeHistorySize, uint32_t(MAX_FRAMETIME_HISTORY));
|
||||
if (UTILS_UNLIKELY(mFrameTimeHistorySize < 3)) {
|
||||
// not enough history to do anything useful
|
||||
history[0].valid = false;
|
||||
return;
|
||||
}
|
||||
assert_invariant(first != history.size());
|
||||
|
||||
// we need at least 3 valid frame time to calculate the median
|
||||
if (history.size() >= first + 3) {
|
||||
// apply a median filter to get a good representation of the frame time of the last
|
||||
// N frames.
|
||||
std::array<duration, MAX_FRAMETIME_HISTORY> median; // NOLINT -- it's initialized below
|
||||
size_t const size = std::min({
|
||||
history.size() - first,
|
||||
median.size(),
|
||||
size_t(config.historySize) });
|
||||
// apply a median filter to get a good representation of the frame time of the last
|
||||
// N frames.
|
||||
std::array<duration, MAX_FRAMETIME_HISTORY> median; // NOLINT -- it's initialized below
|
||||
size_t const size = std::min(mFrameTimeHistorySize,
|
||||
std::min(config.historySize, (uint32_t)median.size()));
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
median[i] = history[i].frameTime;
|
||||
}
|
||||
std::sort(median.begin(), median.begin() + size);
|
||||
duration const denoisedFrameTime = median[size / 2];
|
||||
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
median[i] = history[first + i].frameTime;
|
||||
}
|
||||
std::sort(median.begin(), median.begin() + size);
|
||||
duration const denoisedFrameTime = median[size / 2];
|
||||
|
||||
history[first].denoisedFrameTime = denoisedFrameTime;
|
||||
history[first].valid = true;
|
||||
}
|
||||
history[0].denoisedFrameTime = denoisedFrameTime;
|
||||
history[0].valid = true;
|
||||
}
|
||||
|
||||
utils::FixedCapacityVector<Renderer::FrameInfo> FrameInfoManager::getFrameInfoHistory(
|
||||
size_t historySize) const noexcept {
|
||||
auto result = utils::FixedCapacityVector<Renderer::FrameInfo>::with_capacity(MAX_FRAMETIME_HISTORY);
|
||||
auto const& history = mFrameTimeHistory;
|
||||
size_t i = 0;
|
||||
size_t const c = history.size();
|
||||
for (; i < c; ++i) {
|
||||
auto const& entry = history[i];
|
||||
if (entry.ready.load(std::memory_order_acquire) && entry.valid) {
|
||||
// once we found an entry ready,
|
||||
// we know by construction that all following ones are too
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (; i < c && historySize; ++i, --historySize) {
|
||||
auto const& entry = history[i];
|
||||
using namespace std::chrono;
|
||||
result.push_back({
|
||||
entry.frameId,
|
||||
duration_cast<nanoseconds>(entry.frameTime).count(),
|
||||
duration_cast<nanoseconds>(entry.denoisedFrameTime).count(),
|
||||
duration_cast<nanoseconds>(entry.beginFrame.time_since_epoch()).count(),
|
||||
duration_cast<nanoseconds>(entry.endFrame.time_since_epoch()).count(),
|
||||
duration_cast<nanoseconds>(entry.backendBeginFrame.time_since_epoch()).count(),
|
||||
duration_cast<nanoseconds>(entry.backendEndFrame.time_since_epoch()).count()
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
+17
-125
@@ -17,136 +17,30 @@
|
||||
#ifndef TNT_FILAMENT_FRAMEINFO_H
|
||||
#define TNT_FILAMENT_FRAMEINFO_H
|
||||
|
||||
#include <filament/Renderer.h>
|
||||
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include "backend/Handle.h"
|
||||
#include <private/backend/DriverApi.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <ratio>
|
||||
#include <type_traits>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace filament {
|
||||
class FEngine;
|
||||
|
||||
namespace details {
|
||||
struct FrameInfo {
|
||||
using duration = std::chrono::duration<float, std::milli>;
|
||||
duration frameTime{}; // frame period
|
||||
duration denoisedFrameTime{}; // frame period (median filter)
|
||||
bool valid = false; // true if the data of the structure is valid
|
||||
};
|
||||
} // namespace details
|
||||
|
||||
struct FrameInfoImpl : public details::FrameInfo {
|
||||
using clock = std::chrono::steady_clock;
|
||||
using time_point = clock::time_point;
|
||||
uint32_t const frameId;
|
||||
time_point beginFrame; // main thread beginFrame time
|
||||
time_point endFrame; // main thread endFrame time
|
||||
time_point backendBeginFrame; // backend thread beginFrame time (makeCurrent time)
|
||||
time_point backendEndFrame; // backend thread endFrame time (present time)
|
||||
std::atomic_bool ready{}; // true once backend thread has populated its data
|
||||
explicit FrameInfoImpl(uint32_t frameId) noexcept
|
||||
: frameId(frameId) {
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, size_t CAPACITY>
|
||||
class CircularQueue {
|
||||
public:
|
||||
using value_type = T;
|
||||
using reference = value_type&;
|
||||
using const_reference = value_type const&;
|
||||
|
||||
size_t capacity() const {
|
||||
return CAPACITY;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return mSize;
|
||||
}
|
||||
|
||||
bool empty() const noexcept {
|
||||
return !size();
|
||||
}
|
||||
|
||||
void pop_back() noexcept {
|
||||
assert_invariant(!empty());
|
||||
--mSize;
|
||||
std::destroy_at(&mStorage[(mFront - mSize) % CAPACITY]);
|
||||
}
|
||||
|
||||
void push_front(T const& v) noexcept {
|
||||
assert_invariant(size() < CAPACITY);
|
||||
mFront = advance(mFront);
|
||||
new(&mStorage[mFront]) T(v);
|
||||
++mSize;
|
||||
}
|
||||
|
||||
void push_front(T&& v) noexcept {
|
||||
assert_invariant(size() < CAPACITY);
|
||||
mFront = advance(mFront);
|
||||
new(&mStorage[mFront]) T(std::move(v));
|
||||
++mSize;
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
T& emplace_front(Args&&... args) noexcept {
|
||||
assert_invariant(size() < CAPACITY);
|
||||
mFront = advance(mFront);
|
||||
new(&mStorage[mFront]) T(std::forward<Args>(args)...);
|
||||
++mSize;
|
||||
return front();
|
||||
}
|
||||
|
||||
T& operator[](size_t pos) noexcept {
|
||||
assert_invariant(pos < size());
|
||||
size_t const index = (mFront + CAPACITY - pos) % CAPACITY;
|
||||
return *std::launder(reinterpret_cast<T*>(&mStorage[index]));
|
||||
}
|
||||
|
||||
T const& operator[](size_t pos) const noexcept {
|
||||
return const_cast<CircularQueue&>(*this)[pos];
|
||||
}
|
||||
|
||||
T const& front() const noexcept {
|
||||
assert_invariant(!empty());
|
||||
return operator[](0);
|
||||
}
|
||||
|
||||
T& front() noexcept {
|
||||
assert_invariant(!empty());
|
||||
return operator[](0);
|
||||
}
|
||||
|
||||
private:
|
||||
using Storage = std::aligned_storage_t<sizeof(T), alignof(T)>;
|
||||
Storage mStorage[CAPACITY];
|
||||
uint32_t mFront = 0; // always index 0
|
||||
uint32_t mSize = 0;
|
||||
[[nodiscard]] inline uint32_t advance(uint32_t v) noexcept {
|
||||
return (v + 1) % CAPACITY;
|
||||
}
|
||||
bool valid = false;
|
||||
};
|
||||
|
||||
class FrameInfoManager {
|
||||
static constexpr size_t POOL_COUNT = 4;
|
||||
static constexpr size_t MAX_FRAMETIME_HISTORY = 16u;
|
||||
static constexpr size_t MAX_FRAMETIME_HISTORY = 31u;
|
||||
|
||||
public:
|
||||
using duration = FrameInfoImpl::duration;
|
||||
using clock = FrameInfoImpl::clock;
|
||||
using duration = FrameInfo::duration;
|
||||
|
||||
struct Config {
|
||||
uint32_t historySize;
|
||||
@@ -163,25 +57,23 @@ public:
|
||||
// call this immediately before "swap buffers"
|
||||
void endFrame(backend::DriverApi& driver) noexcept;
|
||||
|
||||
details::FrameInfo getLastFrameInfo() const noexcept {
|
||||
// if pFront is not set yet, return FrameInfo(). But the `valid` field will be false in this case.
|
||||
return pFront ? *pFront : details::FrameInfo{};
|
||||
FrameInfo const& getLastFrameInfo() const noexcept {
|
||||
return mFrameTimeHistory[0];
|
||||
}
|
||||
|
||||
utils::FixedCapacityVector<Renderer::FrameInfo> getFrameInfoHistory(size_t historySize) const noexcept;
|
||||
duration getLastFrameTime() const noexcept {
|
||||
return getLastFrameInfo().frameTime;
|
||||
}
|
||||
|
||||
private:
|
||||
using FrameHistoryQueue = CircularQueue<FrameInfoImpl, MAX_FRAMETIME_HISTORY>;
|
||||
static void denoiseFrameTime(FrameHistoryQueue& history, Config const& config) noexcept;
|
||||
struct Query {
|
||||
backend::Handle<backend::HwTimerQuery> handle{};
|
||||
FrameInfoImpl* pInfo = nullptr;
|
||||
};
|
||||
std::array<Query, POOL_COUNT> mQueries;
|
||||
uint32_t mIndex = 0; // index of current query
|
||||
uint32_t mLast = 0; // index of oldest query still active
|
||||
FrameInfoImpl* pFront = nullptr; // the most recent slot with a valid frame time
|
||||
FrameHistoryQueue mFrameTimeHistory;
|
||||
void update(Config const& config, duration lastFrameTime) noexcept;
|
||||
backend::Handle<backend::HwTimerQuery> mQueries[POOL_COUNT];
|
||||
duration mFrameTime{};
|
||||
uint32_t mIndex = 0;
|
||||
uint32_t mLast = 0;
|
||||
|
||||
std::array<FrameInfo, MAX_FRAMETIME_HISTORY> mFrameTimeHistory;
|
||||
uint32_t mFrameTimeHistorySize = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -16,22 +16,16 @@
|
||||
|
||||
#include "FrameSkipper.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
using namespace utils;
|
||||
using namespace backend;
|
||||
|
||||
FrameSkipper::FrameSkipper(size_t latency) noexcept
|
||||
: mLast(std::max(latency, MAX_FRAME_LATENCY) - 1) {
|
||||
: mLast(latency - 1) {
|
||||
assert_invariant(latency <= MAX_FRAME_LATENCY);
|
||||
}
|
||||
|
||||
@@ -47,32 +41,31 @@ void FrameSkipper::terminate(DriverApi& driver) noexcept {
|
||||
|
||||
bool FrameSkipper::beginFrame(DriverApi& driver) noexcept {
|
||||
auto& fences = mDelayedFences;
|
||||
if (fences.front()) {
|
||||
// Do we have a latency old fence?
|
||||
auto status = driver.getFenceStatus(fences.front());
|
||||
if (UTILS_UNLIKELY(status == FenceStatus::TIMEOUT_EXPIRED)) {
|
||||
// The fence hasn't signaled yet, skip this frame
|
||||
auto fence = fences.front();
|
||||
if (fence) {
|
||||
auto status = driver.getFenceStatus(fence);
|
||||
if (status == FenceStatus::TIMEOUT_EXPIRED) {
|
||||
// Sync not ready, skip frame
|
||||
return false;
|
||||
}
|
||||
assert_invariant(status == FenceStatus::CONDITION_SATISFIED);
|
||||
driver.destroyFence(fence);
|
||||
}
|
||||
// shift all fences down by 1
|
||||
std::move(fences.begin() + 1, fences.end(), fences.begin());
|
||||
fences.back() = {};
|
||||
return true;
|
||||
}
|
||||
|
||||
void FrameSkipper::endFrame(DriverApi& driver) noexcept {
|
||||
auto& fences = mDelayedFences;
|
||||
size_t const last = mLast;
|
||||
|
||||
// pop the oldest fence and advance the other ones
|
||||
if (fences.front()) {
|
||||
driver.destroyFence(fences.front());
|
||||
// If the user produced a new frame despite the fact that the previous one wasn't finished
|
||||
// (i.e. FrameSkipper::beginFrame() returned false), we need to make sure to replace
|
||||
// a fence that might be here already)
|
||||
auto& fence = mDelayedFences[mLast];
|
||||
if (fence) {
|
||||
driver.destroyFence(fence);
|
||||
}
|
||||
std::move(fences.begin() + 1, fences.end(), fences.begin());
|
||||
|
||||
// add a new fence to the end
|
||||
assert_invariant(!fences[last]);
|
||||
|
||||
fences[last] = driver.createFence();
|
||||
fence = driver.createFence();
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -22,9 +22,6 @@
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
/*
|
||||
@@ -63,7 +60,7 @@ public:
|
||||
private:
|
||||
using Container = std::array<backend::Handle<backend::HwFence>, MAX_FRAME_LATENCY>;
|
||||
mutable Container mDelayedFences{};
|
||||
uint8_t const mLast;
|
||||
size_t mLast;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include <filament/Viewport.h>
|
||||
|
||||
#include <utils/BinaryTreeArray.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Systrace.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
|
||||
@@ -354,12 +354,6 @@ bool MaterialParser::getSpecularAntiAliasingThreshold(float* value) const noexce
|
||||
return mImpl.getFromSimpleChunk(ChunkType::MaterialSpecularAntiAliasingThreshold, value);
|
||||
}
|
||||
|
||||
bool MaterialParser::getStereoscopicType(backend::StereoscopicType* value) const noexcept {
|
||||
static_assert(sizeof(StereoscopicType) == sizeof(uint8_t),
|
||||
"StereoscopicType expected size is wrong");
|
||||
return mImpl.getFromSimpleChunk(ChunkType::MaterialStereoscopicType, reinterpret_cast<uint8_t*>(value));
|
||||
}
|
||||
|
||||
bool MaterialParser::getRequiredAttributes(AttributeBitset* value) const noexcept {
|
||||
uint32_t rawAttributes = 0;
|
||||
if (!mImpl.getFromSimpleChunk(ChunkType::MaterialRequiredAttributes, &rawAttributes)) {
|
||||
|
||||
@@ -122,7 +122,6 @@ public:
|
||||
bool hasSpecularAntiAliasing(bool* value) const noexcept;
|
||||
bool getSpecularAntiAliasingVariance(float* value) const noexcept;
|
||||
bool getSpecularAntiAliasingThreshold(float* value) const noexcept;
|
||||
bool getStereoscopicType(backend::StereoscopicType*) const noexcept;
|
||||
|
||||
bool getShader(filaflat::ShaderContent& shader, backend::ShaderModel shaderModel,
|
||||
Variant variant, backend::ShaderStage stage) noexcept;
|
||||
|
||||
@@ -494,6 +494,7 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
|
||||
|
||||
struct StructureMipmapData {
|
||||
FrameGraphId<FrameGraphTexture> depth;
|
||||
uint32_t rt[8];
|
||||
};
|
||||
|
||||
fg.addPass<StructureMipmapData>("StructureMipmap",
|
||||
@@ -504,7 +505,7 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
|
||||
.level = uint8_t(i)
|
||||
});
|
||||
out = builder.write(out, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
|
||||
builder.declareRenderPass("Structure mip target", {
|
||||
data.rt[i - 1] = builder.declareRenderPass("Structure mip target", {
|
||||
.attachments = { .depth = out }
|
||||
});
|
||||
}
|
||||
@@ -1964,8 +1965,8 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
|
||||
// - visible bloom size changes with dynamic resolution in non-homogenous mode
|
||||
// This allows us to use the 9 sample downsampling filter (instead of 13)
|
||||
// for at least 4 levels.
|
||||
uint32_t width = std::max(16u, uint32_t(std::floor(bloomWidth)));
|
||||
uint32_t height = std::max(16u, uint32_t(std::floor(bloomHeight)));
|
||||
uint32_t width = std::max(1u, uint32_t(std::floor(bloomWidth)));
|
||||
uint32_t height = std::max(1u, uint32_t(std::floor(bloomHeight)));
|
||||
width &= ~((1 << 4) - 1); // at least 4 levels
|
||||
height &= ~((1 << 4) - 1);
|
||||
bloomWidth = float(width);
|
||||
@@ -1977,8 +1978,6 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
|
||||
// we don't need to do the fireflies reduction if we have TAA (it already does it)
|
||||
bool fireflies = threshold && !taaOptions.enabled;
|
||||
|
||||
assert_invariant(bloomWidth && bloomHeight);
|
||||
|
||||
while (2 * bloomWidth < float(desc.width) || 2 * bloomHeight < float(desc.height)) {
|
||||
if (inoutBloomOptions.quality == QualityLevel::LOW ||
|
||||
inoutBloomOptions.quality == QualityLevel::MEDIUM) {
|
||||
@@ -2945,10 +2944,6 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscale(FrameGraph& fg, bool
|
||||
1.0f / outputDesc.height});
|
||||
}
|
||||
|
||||
if (blitterNames[index] == "blitLow") {
|
||||
mi->setParameter("levelOfDetail", 0.0f);
|
||||
}
|
||||
|
||||
mi->setParameter("viewport", float4{
|
||||
(float)vp.left / inputDesc.width,
|
||||
(float)vp.bottom / inputDesc.height,
|
||||
@@ -3007,8 +3002,9 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool tr
|
||||
SamplerMagFilter filterMag,
|
||||
SamplerMinFilter filterMin) noexcept {
|
||||
|
||||
uint32_t const layer = fg.getSubResourceDescriptor(input).layer;
|
||||
float const levelOfDetail = fg.getSubResourceDescriptor(input).level;
|
||||
// TODO: add support for sub-resources
|
||||
assert_invariant(fg.getSubResourceDescriptor(input).layer == 0);
|
||||
assert_invariant(fg.getSubResourceDescriptor(input).level == 0);
|
||||
|
||||
struct QuadBlitData {
|
||||
FrameGraphId<FrameGraphTexture> input;
|
||||
@@ -3034,8 +3030,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool tr
|
||||
// --------------------------------------------------------------------------------
|
||||
// set uniforms
|
||||
|
||||
PostProcessMaterial const& material =
|
||||
getPostProcessMaterial(layer ? "blitArray" : "blitLow");
|
||||
PostProcessMaterial const& material = getPostProcessMaterial("blitLow");
|
||||
auto* mi = material.getMaterialInstance(mEngine);
|
||||
mi->setParameter("color", color, {
|
||||
.filterMag = filterMag,
|
||||
@@ -3047,10 +3042,6 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool tr
|
||||
float(vp.width) / inputDesc.width,
|
||||
float(vp.height) / inputDesc.height
|
||||
});
|
||||
mi->setParameter("levelOfDetail", levelOfDetail);
|
||||
if (layer) {
|
||||
mi->setParameter("layerIndex", layer);
|
||||
}
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
|
||||
|
||||
+37
-57
@@ -38,7 +38,6 @@
|
||||
#include <backend/PipelineState.h>
|
||||
|
||||
#include "private/backend/CircularBuffer.h"
|
||||
#include "private/backend/CommandStream.h"
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
@@ -118,11 +117,10 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce
|
||||
uint32_t const customCommandCount =
|
||||
builder.mCustomCommands.has_value() ? builder.mCustomCommands->size() : 0;
|
||||
|
||||
Command* const commandBegin = builder.mArena.alloc<Command>(commandCount + customCommandCount);
|
||||
Command* commandEnd = commandBegin + (commandCount + customCommandCount);
|
||||
assert_invariant(commandBegin);
|
||||
Command* const curr = builder.mArena.alloc<Command>(commandCount + customCommandCount);
|
||||
assert_invariant(curr);
|
||||
|
||||
if (UTILS_UNLIKELY(builder.mArena.getAllocator().isHeapAllocation(commandBegin))) {
|
||||
if (UTILS_UNLIKELY(builder.mArena.getAllocator().isHeapAllocation(curr))) {
|
||||
static bool sLogOnce = true;
|
||||
if (UTILS_UNLIKELY(sLogOnce)) {
|
||||
sLogOnce = false;
|
||||
@@ -131,7 +129,10 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce
|
||||
}
|
||||
}
|
||||
|
||||
appendCommands(engine, { commandBegin, commandCount },
|
||||
mCommandBegin = curr;
|
||||
mCommandEnd = curr + commandCount + customCommandCount;
|
||||
|
||||
appendCommands(engine, { curr, commandCount },
|
||||
builder.mUboHandle,
|
||||
builder.mVisibleRenderables,
|
||||
builder.mCommandTypeFlags,
|
||||
@@ -142,36 +143,33 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce
|
||||
builder.mCameraForwardVector);
|
||||
|
||||
if (builder.mCustomCommands.has_value()) {
|
||||
Command* p = commandBegin + commandCount;
|
||||
Command* p = curr + commandCount;
|
||||
for (auto [channel, passId, command, order, fn]: builder.mCustomCommands.value()) {
|
||||
appendCustomCommand(p++, channel, passId, command, order, fn);
|
||||
}
|
||||
}
|
||||
|
||||
// sort commands once we're done adding commands
|
||||
commandEnd = resize(builder.mArena,
|
||||
RenderPass::sortCommands(commandBegin, commandEnd));
|
||||
sortCommands(builder.mArena);
|
||||
|
||||
if (engine.isAutomaticInstancingEnabled()) {
|
||||
int32_t stereoscopicEyeCount = 1;
|
||||
uint32_t stereoscopicEyeCount = 1;
|
||||
if (builder.mFlags & IS_INSTANCED_STEREOSCOPIC) {
|
||||
stereoscopicEyeCount *= engine.getConfig().stereoscopicEyeCount;
|
||||
}
|
||||
commandEnd = resize(builder.mArena,
|
||||
instanceify(engine, commandBegin, commandEnd, stereoscopicEyeCount));
|
||||
instanceify(engine, builder.mArena, stereoscopicEyeCount);
|
||||
}
|
||||
|
||||
// these are `const` from this point on...
|
||||
mCommandBegin = commandBegin;
|
||||
mCommandEnd = commandEnd;
|
||||
}
|
||||
|
||||
// this destructor is actually heavy because it inlines ~vector<>
|
||||
RenderPass::~RenderPass() noexcept = default;
|
||||
RenderPass::~RenderPass() noexcept {
|
||||
}
|
||||
|
||||
RenderPass::Command* RenderPass::resize(Arena& arena, Command* const last) noexcept {
|
||||
arena.rewind(last);
|
||||
return last;
|
||||
void RenderPass::resize(Arena& arena, size_t count) noexcept {
|
||||
if (mCommandBegin) {
|
||||
mCommandEnd = mCommandBegin + count;
|
||||
arena.rewind(mCommandEnd);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderPass::appendCommands(FEngine& engine,
|
||||
@@ -223,7 +221,7 @@ void RenderPass::appendCommands(FEngine& engine,
|
||||
work(vr.first, vr.size());
|
||||
} else {
|
||||
auto* jobCommandsParallel = jobs::parallel_for(js, nullptr, vr.first, (uint32_t)vr.size(),
|
||||
std::cref(work), jobs::CountSplitter<JOBS_PARALLEL_FOR_COMMANDS_COUNT>());
|
||||
std::cref(work), jobs::CountSplitter<JOBS_PARALLEL_FOR_COMMANDS_COUNT, 5>());
|
||||
js.runAndWait(jobCommandsParallel);
|
||||
}
|
||||
|
||||
@@ -262,19 +260,18 @@ void RenderPass::appendCustomCommand(Command* commands,
|
||||
commands->key = cmd;
|
||||
}
|
||||
|
||||
RenderPass::Command* RenderPass::sortCommands(
|
||||
Command* const begin, Command* const end) noexcept {
|
||||
SYSTRACE_NAME("sort commands");
|
||||
void RenderPass::sortCommands(Arena& arena) noexcept {
|
||||
SYSTRACE_NAME("sort and trim commands");
|
||||
|
||||
std::sort(begin, end);
|
||||
std::sort(mCommandBegin, mCommandEnd);
|
||||
|
||||
// find the last command
|
||||
Command* const last = std::partition_point(begin, end,
|
||||
Command const* const last = std::partition_point(mCommandBegin, mCommandEnd,
|
||||
[](Command const& c) {
|
||||
return c.key != uint64_t(Pass::SENTINEL);
|
||||
});
|
||||
|
||||
return last;
|
||||
resize(arena, uint32_t(last - mCommandBegin));
|
||||
}
|
||||
|
||||
void RenderPass::execute(RenderPass const& pass,
|
||||
@@ -287,9 +284,7 @@ void RenderPass::execute(RenderPass const& pass,
|
||||
driver.endRenderPass();
|
||||
}
|
||||
|
||||
RenderPass::Command* RenderPass::instanceify(FEngine& engine,
|
||||
Command* curr, Command* const last,
|
||||
int32_t eyeCount) const noexcept {
|
||||
void RenderPass::instanceify(FEngine& engine, Arena& arena, int32_t eyeCount) noexcept {
|
||||
SYSTRACE_NAME("instanceify");
|
||||
|
||||
// instanceify works by scanning the **sorted** command stream, looking for repeat draw
|
||||
@@ -302,12 +297,14 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine,
|
||||
|
||||
UTILS_UNUSED uint32_t drawCallsSavedCount = 0;
|
||||
|
||||
Command* curr = mCommandBegin;
|
||||
Command* const last = mCommandEnd;
|
||||
|
||||
Command* firstSentinel = nullptr;
|
||||
PerRenderableData const* uboData = nullptr;
|
||||
PerRenderableData* stagingBuffer = nullptr;
|
||||
uint32_t stagingBufferSize = 0;
|
||||
uint32_t instancedPrimitiveOffset = 0;
|
||||
size_t const count = last - curr;
|
||||
|
||||
// TODO: for the case of instancing we could actually use 128 instead of 64 instances
|
||||
constexpr size_t maxInstanceCount = CONFIG_MAX_INSTANCES;
|
||||
@@ -346,6 +343,7 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine,
|
||||
if (UTILS_UNLIKELY(!stagingBuffer)) {
|
||||
|
||||
// create a temporary UBO for instancing
|
||||
size_t const count = mCommandEnd - mCommandBegin;
|
||||
mInstancedUboHandle = BufferObjectSharedHandle{
|
||||
engine.getDriverApi().createBufferObject(
|
||||
count * sizeof(PerRenderableData) + sizeof(PerRenderableUib),
|
||||
@@ -387,7 +385,7 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine,
|
||||
|
||||
if (UTILS_UNLIKELY(firstSentinel)) {
|
||||
//slog.d << "auto-instancing, saving " << drawCallsSavedCount << " draw calls, out of "
|
||||
// << count << io::endl;
|
||||
// << mCommandEnd - mCommandBegin << io::endl;
|
||||
|
||||
// we have instanced primitives
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
@@ -403,15 +401,14 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine,
|
||||
stagingBuffer = nullptr;
|
||||
|
||||
// remove all the canceled commands
|
||||
auto lastCommand = std::remove_if(firstSentinel, last, [](auto const& command) {
|
||||
auto lastCommand = std::remove_if(firstSentinel, mCommandEnd, [](auto const& command) {
|
||||
return command.key == uint64_t(Pass::SENTINEL);
|
||||
});
|
||||
|
||||
return lastCommand;
|
||||
resize(arena, uint32_t(lastCommand - mCommandBegin));
|
||||
}
|
||||
|
||||
assert_invariant(stagingBuffer == nullptr);
|
||||
return last;
|
||||
}
|
||||
|
||||
|
||||
@@ -610,7 +607,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
|
||||
// Here, objects close to the camera (but behind) will be drawn first.
|
||||
// An alternative that keeps the mathematical ordering is given here:
|
||||
// distanceBits ^= ((int32_t(distanceBits) >> 31) | 0x80000000u);
|
||||
float const distance = -(dot(soaWorldAABBCenter[i], cameraForward) - cameraPositionDotCameraForward);
|
||||
float const distance = -dot(soaWorldAABBCenter[i], cameraForward) - cameraPositionDotCameraForward;
|
||||
uint32_t const distanceBits = reinterpret_cast<uint32_t const&>(distance);
|
||||
|
||||
// calculate the per-primitive face winding order inversion
|
||||
@@ -888,31 +885,14 @@ void RenderPass::Executor::execute(FEngine& engine,
|
||||
|
||||
// Maximum space occupied in the CircularBuffer by a single `Command`. This must be
|
||||
// reevaluated when the inner loop below adds DriverApi commands or when we change the
|
||||
// CommandStream protocol. Currently, the maximum is 320 bytes.
|
||||
// The batch size is calculated by adding the size of all commands that can possibly be
|
||||
// emitted per draw call:
|
||||
constexpr size_t const maxCommandSizeInBytes =
|
||||
sizeof(CustomCommand) +
|
||||
sizeof(COMMAND_TYPE(scissor)) +
|
||||
sizeof(COMMAND_TYPE(bindUniformBuffer)) +
|
||||
sizeof(COMMAND_TYPE(bindSamplers)) +
|
||||
sizeof(COMMAND_TYPE(bindBufferRange)) +
|
||||
sizeof(COMMAND_TYPE(bindBufferRange)) +
|
||||
sizeof(COMMAND_TYPE(bindSamplers)) +
|
||||
sizeof(COMMAND_TYPE(bindSamplers)) +
|
||||
sizeof(COMMAND_TYPE(bindUniformBuffer)) +
|
||||
sizeof(COMMAND_TYPE(bindSamplers)) +
|
||||
sizeof(COMMAND_TYPE(bindSamplers)) +
|
||||
sizeof(COMMAND_TYPE(bindPipeline)) +
|
||||
sizeof(COMMAND_TYPE(setPushConstant)) +
|
||||
sizeof(COMMAND_TYPE(bindRenderPrimitive)) +
|
||||
sizeof(COMMAND_TYPE(draw2));
|
||||
|
||||
// CommandStream protocol. Currently, the maximum is 240 bytes, and we use 256 to be on
|
||||
// the safer side.
|
||||
size_t const maxCommandSizeInBytes = 256;
|
||||
|
||||
// Number of Commands that can be issued and guaranteed to fit in the current
|
||||
// CircularBuffer allocation. In practice, we'll have tons of headroom especially if
|
||||
// skinning and morphing aren't used. With a 2 MiB buffer (the default) a batch is
|
||||
// 6553 commands (i.e. draw calls).
|
||||
// 8192 commands (i.e. draw calls).
|
||||
size_t const batchCommandCount = capacity / maxCommandSizeInBytes;
|
||||
while(first != last) {
|
||||
Command const* const batchLast = std::min(first + batchCommandCount, last);
|
||||
|
||||
@@ -408,19 +408,17 @@ private:
|
||||
uint8_t channel, Pass pass, CustomCommand custom, uint32_t order,
|
||||
Executor::CustomCommandFn command);
|
||||
|
||||
static Command* resize(Arena& arena, Command* const last) noexcept;
|
||||
void resize(Arena& arena, size_t count) noexcept;
|
||||
|
||||
// sorts commands then trims sentinels
|
||||
static Command* sortCommands(
|
||||
Command* begin, Command* end) noexcept;
|
||||
void sortCommands(Arena& arena) noexcept;
|
||||
|
||||
// instanceify commands then trims sentinels
|
||||
RenderPass::Command* instanceify(FEngine& engine,
|
||||
Command* begin, Command* end,
|
||||
int32_t eyeCount) const noexcept;
|
||||
void instanceify(FEngine& engine, Arena& arena, int32_t eyeCount) noexcept;
|
||||
|
||||
// We choose the command count per job to minimize JobSystem overhead.
|
||||
static constexpr size_t JOBS_PARALLEL_FOR_COMMANDS_COUNT = 128;
|
||||
// On a Pixel 4, 2048 commands is about half a millisecond of processing.
|
||||
static constexpr size_t JOBS_PARALLEL_FOR_COMMANDS_COUNT = 2048;
|
||||
static constexpr size_t JOBS_PARALLEL_FOR_COMMANDS_SIZE =
|
||||
sizeof(Command) * JOBS_PARALLEL_FOR_COMMANDS_COUNT;
|
||||
|
||||
@@ -451,10 +449,10 @@ private:
|
||||
|
||||
FScene::RenderableSoa const& mRenderableSoa;
|
||||
backend::Viewport const mScissorViewport;
|
||||
Command const* /* const */ mCommandBegin = nullptr; // Pointer to the first command
|
||||
Command const* /* const */ mCommandEnd = nullptr; // Pointer to one past the last command
|
||||
Command* mCommandBegin = nullptr; // Pointer to the first command
|
||||
Command* mCommandEnd = nullptr; // Pointer to one past the last command
|
||||
// a UBO for instanced primitives
|
||||
mutable BufferObjectSharedHandle mInstancedUboHandle;
|
||||
BufferObjectSharedHandle mInstancedUboHandle;
|
||||
// a vector for our custom commands
|
||||
using CustomCommandVector = std::vector<Executor::CustomCommandFn,
|
||||
utils::STLAllocator<Executor::CustomCommandFn, LinearAllocatorArena>>;
|
||||
|
||||
@@ -14,21 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <filament/Renderer.h>
|
||||
|
||||
#include "ResourceAllocator.h"
|
||||
#include "details/Renderer.h"
|
||||
|
||||
#include "details/Engine.h"
|
||||
#include "details/Renderer.h"
|
||||
#include "details/View.h"
|
||||
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
using namespace math;
|
||||
@@ -46,10 +36,6 @@ void Renderer::setPresentationTime(int64_t monotonic_clock_ns) {
|
||||
downcast(this)->setPresentationTime(monotonic_clock_ns);
|
||||
}
|
||||
|
||||
void Renderer::skipFrame(uint64_t vsyncSteadyClockTimeNano) {
|
||||
downcast(this)->skipFrame(vsyncSteadyClockTimeNano);
|
||||
}
|
||||
|
||||
bool Renderer::beginFrame(SwapChain* swapChain, uint64_t vsyncSteadyClockTimeNano) {
|
||||
return downcast(this)->beginFrame(downcast(swapChain), vsyncSteadyClockTimeNano);
|
||||
}
|
||||
@@ -107,12 +93,4 @@ void Renderer::setVsyncTime(uint64_t steadyClockTimeNano) noexcept {
|
||||
downcast(this)->setVsyncTime(steadyClockTimeNano);
|
||||
}
|
||||
|
||||
utils::FixedCapacityVector<Renderer::FrameInfo> Renderer::getFrameInfoHistory(size_t historySize) const noexcept {
|
||||
return downcast(this)->getFrameInfoHistory(historySize);
|
||||
}
|
||||
|
||||
size_t Renderer::getMaxFrameHistorySize() const noexcept {
|
||||
return downcast(this)->getMaxFrameHistorySize();
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -75,6 +75,7 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
|
||||
TargetBufferFlags const clearColorFlags = config.clearFlags & TargetBufferFlags::COLOR;
|
||||
TargetBufferFlags clearDepthFlags = config.clearFlags & TargetBufferFlags::DEPTH;
|
||||
TargetBufferFlags clearStencilFlags = config.clearFlags & TargetBufferFlags::STENCIL;
|
||||
uint8_t layerCount = 0;
|
||||
|
||||
data.shadows = blackboard.get<FrameGraphTexture>("shadows");
|
||||
data.ssao = blackboard.get<FrameGraphTexture>("ssao");
|
||||
@@ -171,6 +172,9 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
|
||||
|
||||
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) {
|
||||
layerCount = engine.getConfig().stereoscopicEyeCount;
|
||||
}
|
||||
|
||||
/*
|
||||
* There is a bit of magic happening here regarding the viewport used.
|
||||
@@ -192,7 +196,7 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
|
||||
.stencil = data.stencil },
|
||||
.clearColor = config.clearColor,
|
||||
.samples = config.msaa,
|
||||
.layerCount = static_cast<uint8_t>(colorBufferDesc.depth),
|
||||
.layerCount = layerCount,
|
||||
.clearFlags = clearColorFlags | clearDepthFlags | clearStencilFlags});
|
||||
blackboard["depth"] = data.depth;
|
||||
},
|
||||
|
||||
@@ -27,18 +27,15 @@
|
||||
|
||||
#include "private/backend/DriverApi.h"
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/bitset.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
#include <stddef.h>
|
||||
@@ -92,15 +89,8 @@ void ResourceAllocator::AssociativeContainer<K, V, H>::emplace(ARGS&& ... args)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
ResourceAllocatorInterface::~ResourceAllocatorInterface() = default;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
ResourceAllocatorDisposerInterface::~ResourceAllocatorDisposerInterface() = default;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
size_t ResourceAllocator::TextureKey::getSize() const noexcept {
|
||||
size_t const pixelCount = width * height * depth;
|
||||
size_t size = pixelCount * FTexture::getFormatSize(format);
|
||||
@@ -120,22 +110,16 @@ size_t ResourceAllocator::TextureKey::getSize() const noexcept {
|
||||
|
||||
ResourceAllocator::ResourceAllocator(Engine::Config const& config, DriverApi& driverApi) noexcept
|
||||
: mCacheMaxAge(config.resourceAllocatorCacheMaxAge),
|
||||
mBackend(driverApi),
|
||||
mDisposer(std::make_shared<ResourceAllocatorDisposer>(driverApi)) {
|
||||
}
|
||||
|
||||
ResourceAllocator::ResourceAllocator(std::shared_ptr<ResourceAllocatorDisposer> disposer,
|
||||
Engine::Config const& config, DriverApi& driverApi) noexcept
|
||||
: mCacheMaxAge(config.resourceAllocatorCacheMaxAge),
|
||||
mBackend(driverApi),
|
||||
mDisposer(std::move(disposer)) {
|
||||
mBackend(driverApi) {
|
||||
}
|
||||
|
||||
ResourceAllocator::~ResourceAllocator() noexcept {
|
||||
assert_invariant(mTextureCache.empty());
|
||||
assert_invariant(!mTextureCache.size());
|
||||
assert_invariant(!mInUseTextures.size());
|
||||
}
|
||||
|
||||
void ResourceAllocator::terminate() noexcept {
|
||||
assert_invariant(!mInUseTextures.size());
|
||||
auto& textureCache = mTextureCache;
|
||||
for (auto it = textureCache.begin(); it != textureCache.end();) {
|
||||
mBackend.destroyTexture(it->second.handle);
|
||||
@@ -190,7 +174,7 @@ backend::TextureHandle ResourceAllocator::createTexture(const char* name,
|
||||
swizzle[0], swizzle[1], swizzle[2], swizzle[3]);
|
||||
}
|
||||
}
|
||||
mDisposer->checkout(handle, key);
|
||||
mInUseTextures.emplace(handle, key);
|
||||
} else {
|
||||
if (swizzle == defaultSwizzle) {
|
||||
handle = mBackend.createTexture(
|
||||
@@ -206,99 +190,58 @@ backend::TextureHandle ResourceAllocator::createTexture(const char* name,
|
||||
|
||||
void ResourceAllocator::destroyTexture(TextureHandle h) noexcept {
|
||||
if constexpr (mEnabled) {
|
||||
auto const key = mDisposer->checkin(h);
|
||||
if (UTILS_LIKELY(key.has_value())) {
|
||||
uint32_t const size = key.value().getSize();
|
||||
mTextureCache.emplace(key.value(), TextureCachePayload{ h, mAge, size });
|
||||
mCacheSize += size;
|
||||
mCacheSizeHiWaterMark = std::max(mCacheSizeHiWaterMark, mCacheSize);
|
||||
}
|
||||
// find the texture in the in-use list (it must be there!)
|
||||
auto it = mInUseTextures.find(h);
|
||||
assert_invariant(it != mInUseTextures.end());
|
||||
|
||||
// move it to the cache
|
||||
const TextureKey key = it->second;
|
||||
uint32_t const size = key.getSize();
|
||||
|
||||
mTextureCache.emplace(key, TextureCachePayload{ h, mAge, size });
|
||||
mCacheSize += size;
|
||||
|
||||
// remove it from the in-use list
|
||||
mInUseTextures.erase(it);
|
||||
} else {
|
||||
mBackend.destroyTexture(h);
|
||||
}
|
||||
}
|
||||
|
||||
ResourceAllocatorDisposerInterface& ResourceAllocator::getDisposer() noexcept {
|
||||
return *mDisposer;
|
||||
}
|
||||
void ResourceAllocator::gc() noexcept {
|
||||
// this is called regularly -- usually once per frame of each Renderer
|
||||
|
||||
void ResourceAllocator::gc(bool skippedFrame) noexcept {
|
||||
// this is called regularly -- usually once per frame
|
||||
|
||||
// increase our age at each (non-skipped) frame
|
||||
const size_t age = mAge;
|
||||
if (!skippedFrame) {
|
||||
mAge++;
|
||||
}
|
||||
// increase our age
|
||||
const size_t age = mAge++;
|
||||
|
||||
// Purging strategy:
|
||||
// - remove all entries older than MAX_AGE_SKIPPED_FRAME when skipping a frame
|
||||
// - remove entries older than mCacheMaxAgeSoft
|
||||
// - remove only MAX_EVICTION_COUNT entry per gc(),
|
||||
// - look for the number of unique resource ages present in the cache (this basically gives
|
||||
// us how many buckets of resources we have corresponding to previous frames.
|
||||
// - remove all resources that have an age older than the MAX_UNIQUE_AGE_COUNT'th bucket
|
||||
// - remove entries that are older than a certain age
|
||||
// - remove only one entry per gc(),
|
||||
|
||||
auto& textureCache = mTextureCache;
|
||||
|
||||
// when skipping a frame, the maximum age to keep in the cache
|
||||
constexpr size_t MAX_AGE_SKIPPED_FRAME = 1;
|
||||
|
||||
// maximum entry count to evict per GC, under the mCacheMaxAgeSoft limit
|
||||
constexpr size_t MAX_EVICTION_COUNT = 1;
|
||||
|
||||
// maximum number of unique ages in the cache
|
||||
constexpr size_t MAX_UNIQUE_AGE_COUNT = 3;
|
||||
|
||||
utils::bitset32 ages;
|
||||
uint32_t evictedCount = 0;
|
||||
for (auto it = textureCache.begin(); it != textureCache.end();) {
|
||||
size_t const ageDiff = age - it->second.age;
|
||||
if ((ageDiff >= MAX_AGE_SKIPPED_FRAME && skippedFrame) ||
|
||||
(ageDiff >= mCacheMaxAge && evictedCount < MAX_EVICTION_COUNT)) {
|
||||
evictedCount++;
|
||||
const size_t ageDiff = age - it->second.age;
|
||||
if (ageDiff >= mCacheMaxAge) {
|
||||
purge(it);
|
||||
// only purge a single entry per gc
|
||||
break;
|
||||
} else {
|
||||
// build the set of ages present in the cache after eviction
|
||||
ages.set(std::min(size_t(31), ageDiff));
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// if we have MAX_UNIQUE_AGE_COUNT ages or more, we evict all the resources that
|
||||
// are older than the MAX_UNIQUE_AGE_COUNT'th age.
|
||||
if (!skippedFrame && ages.count() >= MAX_UNIQUE_AGE_COUNT) {
|
||||
uint32_t bits = ages.getValue();
|
||||
// remove from the set the ages we keep
|
||||
for (size_t i = 0; i < MAX_UNIQUE_AGE_COUNT - 1; i++) {
|
||||
bits &= ~(1 << utils::ctz(bits));
|
||||
}
|
||||
size_t const maxAge = utils::ctz(bits);
|
||||
for (auto it = textureCache.begin(); it != textureCache.end();) {
|
||||
const size_t ageDiff = age - it->second.age;
|
||||
if (ageDiff >= maxAge) {
|
||||
purge(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
void ResourceAllocator::dump(bool brief) const noexcept {
|
||||
constexpr float MiB = 1.0f / float(1u << 20u);
|
||||
slog.d << "# entries=" << mTextureCache.size()
|
||||
<< ", sz=" << (float)mCacheSize * MiB << " MiB"
|
||||
<< ", max=" << (float)mCacheSizeHiWaterMark * MiB << " MiB"
|
||||
<< io::endl;
|
||||
slog.d << "# entries=" << mTextureCache.size() << ", sz=" << mCacheSize / float(1u << 20u)
|
||||
<< " MiB" << io::endl;
|
||||
if (!brief) {
|
||||
for (auto const& it : mTextureCache) {
|
||||
auto w = it.first.width;
|
||||
auto h = it.first.height;
|
||||
auto f = FTexture::getFormatSize(it.first.format);
|
||||
slog.d << it.first.name << ": w=" << w << ", h=" << h << ", f=" << f << ", sz="
|
||||
<< (float)it.second.size * MiB << io::endl;
|
||||
<< it.second.size / float(1u << 20u) << io::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -311,46 +254,4 @@ void ResourceAllocator::purge(
|
||||
mTextureCache.erase(pos);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
ResourceAllocatorDisposer::ResourceAllocatorDisposer(DriverApi& driverApi) noexcept
|
||||
: mBackend(driverApi) {
|
||||
}
|
||||
|
||||
ResourceAllocatorDisposer::~ResourceAllocatorDisposer() noexcept {
|
||||
assert_invariant(mInUseTextures.empty());
|
||||
}
|
||||
|
||||
void ResourceAllocatorDisposer::terminate() noexcept {
|
||||
assert_invariant(mInUseTextures.empty());
|
||||
}
|
||||
|
||||
void ResourceAllocatorDisposer::destroy(backend::TextureHandle handle) noexcept {
|
||||
if (handle) {
|
||||
auto r = checkin(handle);
|
||||
if (r.has_value()) {
|
||||
mBackend.destroyTexture(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceAllocatorDisposer::checkout(backend::TextureHandle handle,
|
||||
ResourceAllocator::TextureKey key) {
|
||||
mInUseTextures.emplace(handle, key);
|
||||
}
|
||||
|
||||
std::optional<ResourceAllocator::TextureKey> ResourceAllocatorDisposer::checkin(
|
||||
backend::TextureHandle handle) {
|
||||
// find the texture in the in-use list (it must be there!)
|
||||
auto it = mInUseTextures.find(handle);
|
||||
assert_invariant(it != mInUseTextures.end());
|
||||
if (it == mInUseTextures.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
TextureKey const key = it->second;
|
||||
// remove it from the in-use list
|
||||
mInUseTextures.erase(it);
|
||||
return key;
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -28,29 +28,16 @@
|
||||
#include <utils/Hash.h>
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
#include <cstddef>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
class ResourceAllocatorDisposer;
|
||||
|
||||
// The only reason we use an interface here is for unit-tests, so we can mock this allocator.
|
||||
// This is not too time-critical, so that's okay.
|
||||
|
||||
class ResourceAllocatorDisposerInterface {
|
||||
public:
|
||||
virtual void destroy(backend::TextureHandle handle) noexcept = 0;
|
||||
protected:
|
||||
virtual ~ResourceAllocatorDisposerInterface();
|
||||
};
|
||||
|
||||
class ResourceAllocatorInterface {
|
||||
public:
|
||||
virtual backend::RenderTargetHandle createRenderTarget(const char* name,
|
||||
@@ -73,20 +60,15 @@ public:
|
||||
|
||||
virtual void destroyTexture(backend::TextureHandle h) noexcept = 0;
|
||||
|
||||
virtual ResourceAllocatorDisposerInterface& getDisposer() noexcept = 0;
|
||||
|
||||
protected:
|
||||
virtual ~ResourceAllocatorInterface();
|
||||
};
|
||||
|
||||
|
||||
class ResourceAllocator final : public ResourceAllocatorInterface {
|
||||
public:
|
||||
explicit ResourceAllocator(std::shared_ptr<ResourceAllocatorDisposer> disposer,
|
||||
Engine::Config const& config, backend::DriverApi& driverApi) noexcept;
|
||||
|
||||
explicit ResourceAllocator(
|
||||
Engine::Config const& config, backend::DriverApi& driverApi) noexcept;
|
||||
|
||||
~ResourceAllocator() noexcept override;
|
||||
|
||||
void terminate() noexcept;
|
||||
@@ -111,9 +93,7 @@ public:
|
||||
|
||||
void destroyTexture(backend::TextureHandle h) noexcept override;
|
||||
|
||||
ResourceAllocatorDisposerInterface& getDisposer() noexcept override;
|
||||
|
||||
void gc(bool skippedFrame = false) noexcept;
|
||||
void gc() noexcept;
|
||||
|
||||
private:
|
||||
size_t const mCacheMaxAge;
|
||||
@@ -201,7 +181,6 @@ private:
|
||||
using value_type = typename Container::value_type::second_type;
|
||||
|
||||
size_t size() const { return mContainer.size(); }
|
||||
bool empty() const { return size() == 0; }
|
||||
iterator begin() { return mContainer.begin(); }
|
||||
const_iterator begin() const { return mContainer.begin(); }
|
||||
iterator end() { return mContainer.end(); }
|
||||
@@ -214,36 +193,16 @@ private:
|
||||
};
|
||||
|
||||
using CacheContainer = AssociativeContainer<TextureKey, TextureCachePayload>;
|
||||
using InUseContainer = AssociativeContainer<backend::TextureHandle, TextureKey>;
|
||||
|
||||
void purge(ResourceAllocator::CacheContainer::iterator const& pos);
|
||||
|
||||
backend::DriverApi& mBackend;
|
||||
std::shared_ptr<ResourceAllocatorDisposer> mDisposer;
|
||||
CacheContainer mTextureCache;
|
||||
InUseContainer mInUseTextures;
|
||||
size_t mAge = 0;
|
||||
uint32_t mCacheSize = 0;
|
||||
uint32_t mCacheSizeHiWaterMark = 0;
|
||||
static constexpr bool mEnabled = true;
|
||||
|
||||
friend class ResourceAllocatorDisposer;
|
||||
};
|
||||
|
||||
class ResourceAllocatorDisposer final : public ResourceAllocatorDisposerInterface {
|
||||
using TextureKey = ResourceAllocator::TextureKey;
|
||||
public:
|
||||
explicit ResourceAllocatorDisposer(backend::DriverApi& driverApi) noexcept;
|
||||
~ResourceAllocatorDisposer() noexcept override;
|
||||
void terminate() noexcept;
|
||||
void destroy(backend::TextureHandle handle) noexcept override;
|
||||
|
||||
private:
|
||||
friend class ResourceAllocator;
|
||||
void checkout(backend::TextureHandle handle, TextureKey key);
|
||||
std::optional<TextureKey> checkin(backend::TextureHandle handle);
|
||||
|
||||
using InUseContainer = ResourceAllocator::AssociativeContainer<backend::TextureHandle, TextureKey>;
|
||||
backend::DriverApi& mBackend;
|
||||
InUseContainer mInUseTextures;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace filament {
|
||||
class ResourceListBase {
|
||||
public:
|
||||
using iterator = typename tsl::robin_set<void*>::iterator;
|
||||
using const_iterator = typename tsl::robin_set<void*>::const_iterator;
|
||||
|
||||
explicit ResourceListBase(const char* typeName);
|
||||
ResourceListBase(ResourceListBase&& rhs) noexcept = default;
|
||||
@@ -59,14 +58,6 @@ public:
|
||||
return mList.end();
|
||||
}
|
||||
|
||||
const_iterator begin() const noexcept {
|
||||
return mList.begin();
|
||||
}
|
||||
|
||||
const_iterator end() const noexcept {
|
||||
return mList.end();
|
||||
}
|
||||
|
||||
protected:
|
||||
void forEach(void(*f)(void* user, void *p), void* user) const noexcept;
|
||||
tsl::robin_set<void*> mList;
|
||||
|
||||
+44
-62
@@ -57,6 +57,9 @@ namespace filament {
|
||||
using namespace math;
|
||||
using namespace backend;
|
||||
|
||||
// do this only if depth-clamp is available
|
||||
static constexpr bool USE_DEPTH_CLAMP = false;
|
||||
|
||||
ShadowMap::ShadowMap(FEngine& engine) noexcept
|
||||
: mPerShadowMapUniforms(engine),
|
||||
mShadowType(ShadowType::DIRECTIONAL),
|
||||
@@ -123,8 +126,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
FScene::LightSoa const& lightData, size_t index,
|
||||
filament::CameraInfo const& camera,
|
||||
ShadowMapInfo const& shadowMapInfo,
|
||||
SceneInfo const& sceneInfo,
|
||||
bool useDepthClamp) noexcept {
|
||||
SceneInfo const& sceneInfo) noexcept {
|
||||
|
||||
// reset the visible shadow status
|
||||
mHasVisibleShadows = false;
|
||||
@@ -135,20 +137,13 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
|
||||
const auto direction = lightData.elementAt<FScene::SHADOW_DIRECTION>(index);
|
||||
|
||||
auto const [Mv, znear, zfar, lsClippedShadowVolume, vertexCount, visibleShadows] =
|
||||
computeDirectionalShadowBounds(engine, direction, params, camera, sceneInfo,
|
||||
useDepthClamp);
|
||||
auto [Mv, znear, zfar, lsClippedShadowVolume, vertexCount, visibleShadows] =
|
||||
computeDirectionalShadowBounds(engine, direction, params, camera, sceneInfo);
|
||||
|
||||
if (UTILS_UNLIKELY(!visibleShadows)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// if the 2D bounds of the intersection is empty seen from the light, we have no shadow
|
||||
Aabb const bounds = compute2DBounds(Mv, lsClippedShadowVolume.data(), vertexCount);
|
||||
if (UTILS_UNLIKELY(!(bounds.min.x < bounds.max.x && bounds.min.y < bounds.max.y))) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the light's projection matrix
|
||||
* (directional/point lights, i.e. projection to use, including znear/zfar clip planes)
|
||||
@@ -166,17 +161,12 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
mat4f W, Wp, L;
|
||||
// We can't use LISPSM in stable mode
|
||||
const bool useLispsm = params.options.lispsm && !params.options.stable;
|
||||
|
||||
if (useLispsm ||
|
||||
(!params.options.stable && !engine.debug.shadowmap.disable_light_frustum_align)) {
|
||||
if (useLispsm) {
|
||||
// Orient the shadow map in the direction of the view vector by constructing a
|
||||
const float3 lsCameraFwd = Mv.upperLeft() * camera.getForwardVector();
|
||||
L = computeLightRotation(lsCameraFwd);
|
||||
LMp = L * Mp;
|
||||
LMpMv = LMp * Mv;
|
||||
}
|
||||
|
||||
if (useLispsm) {
|
||||
W = applyLISPSM(Wp, camera, params,
|
||||
LMp, Mv, LMpMv, lsClippedShadowVolume, vertexCount, direction);
|
||||
}
|
||||
@@ -194,7 +184,8 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
// In LiPSM mode, we're using the warped space here.
|
||||
float4 f = computeFocusParams(LMpMv, WLMp,
|
||||
lsClippedShadowVolume, vertexCount,
|
||||
camera, params.options.shadowFar, params.options.stable);
|
||||
camera, sceneInfo.csNearFar,
|
||||
params.options.shadowFar, params.options.stable);
|
||||
|
||||
if (params.options.stable) {
|
||||
const auto lsRef = lightData.elementAt<FScene::SHADOW_REF>(index);
|
||||
@@ -395,8 +386,7 @@ ShadowMap::DirectionalShadowBounds ShadowMap::computeDirectionalShadowBounds(
|
||||
float3 const direction,
|
||||
FLightManager::ShadowParams params,
|
||||
filament::CameraInfo const& camera,
|
||||
SceneInfo const& sceneInfo,
|
||||
bool useDepthClamp) noexcept {
|
||||
SceneInfo const& sceneInfo) noexcept {
|
||||
|
||||
// we use aligned_storage<> here to avoid the default initialization of std::array<>
|
||||
using Storage = std::aligned_storage<sizeof(FrustumBoxIntersection)>::type;
|
||||
@@ -458,49 +448,36 @@ ShadowMap::DirectionalShadowBounds ShadowMap::computeDirectionalShadowBounds(
|
||||
|
||||
// Compute the intersection of the view volume with the intersection of receivers and casters
|
||||
// in light space. This returns a set of points on the convex-hull of the intersection.
|
||||
mat4f const projection = camera.cullingProjection *
|
||||
(camera.view * FCamera::rigidTransformInverse(MvAtOrigin));
|
||||
size_t const vertexCount = intersectFrustumWithBox(lsClippedShadowVolume,
|
||||
projection, lsLightFrustumBounds);
|
||||
camera.cullingProjection * camera.view * FCamera::rigidTransformInverse(MvAtOrigin),
|
||||
sceneInfo.csNearFar, lsLightFrustumBounds);
|
||||
|
||||
if (UTILS_UNLIKELY(vertexCount < 4)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust the scene's zmax (i.e. Near plane) and zmin (i.e. Far plane) in light space.
|
||||
* (near/far correspond to max/min because the light looks down the -z axis).
|
||||
*
|
||||
* - For the Far plane, the light frustum is already set to the closest of receivers and
|
||||
* casters (i.e.: shadow casters behind the last receivers can't cast any shadows).
|
||||
* But it can be moved closer up to the farthest point in the camera frustum & light
|
||||
* frustum intersection.
|
||||
*
|
||||
* - The Near plane is already set to the shadow casters max z (i.e. closest to the light)
|
||||
*
|
||||
* If "depth clamp" is supported, we can further tighten the near plane to the
|
||||
* shadow receiver (i.e. to the closest point of camera frustum & light frustum intersection).
|
||||
*
|
||||
* Note: L has no influence here, since we're only interested in z values
|
||||
* (L is a rotation around z)
|
||||
*/
|
||||
* Adjust the scene's zmax (i.e. Near plane) and zmin (i.e. Far plane) in light space.
|
||||
* (near/far correspond to max/min because the light looks down the -z axis).
|
||||
* - The Near plane is set to the shadow casters max z (i.e. closest to the light)
|
||||
* - The Far plane is set to the closest of the farthest shadow casters and receivers
|
||||
* i.e.: shadow casters behind the last receivers can't cast any shadows
|
||||
*
|
||||
* If "depth clamp" is supported, we can further tighten the near plane to the
|
||||
* shadow receiver.
|
||||
*
|
||||
* Note: L has no influence here, since we're only interested in z values
|
||||
* (L is a rotation around z)
|
||||
*/
|
||||
|
||||
float lsClippedShadowVolumeFarthest = std::numeric_limits<float>::max();
|
||||
float lsClippedShadowVolumeNearest = std::numeric_limits<float>::lowest();
|
||||
for (size_t i = 0; i < vertexCount; ++i) {
|
||||
float3 const v = lsClippedShadowVolume[i];
|
||||
lsClippedShadowVolumeFarthest = std::min(lsClippedShadowVolumeFarthest, v.z);
|
||||
lsClippedShadowVolumeNearest = std::max(lsClippedShadowVolumeNearest, v.z);
|
||||
// far: figure out the farthest shadow receivers
|
||||
lsLightFrustumBounds.min.z = min(lsLightFrustumBounds.min.z, v.z);
|
||||
if constexpr (USE_DEPTH_CLAMP) {
|
||||
lsLightFrustumBounds.max.z = max(lsLightFrustumBounds.max.z, v.z);
|
||||
}
|
||||
}
|
||||
|
||||
lsLightFrustumBounds.min.z =
|
||||
std::max(lsLightFrustumBounds.min.z, lsClippedShadowVolumeFarthest);
|
||||
|
||||
if (useDepthClamp) {
|
||||
lsLightFrustumBounds.max.z =
|
||||
std::min(lsLightFrustumBounds.max.z, lsClippedShadowVolumeNearest);
|
||||
}
|
||||
|
||||
if (engine.debug.shadowmap.far_uses_shadowcasters) {
|
||||
// far: closest of the farthest shadow casters and receivers
|
||||
lsLightFrustumBounds.min.z =
|
||||
@@ -634,7 +611,7 @@ math::float4 ShadowMap::computeFocusParams(
|
||||
mat4f const& LMpMv,
|
||||
mat4f const& WLMp,
|
||||
FrustumBoxIntersection const& lsShadowVolume, size_t vertexCount,
|
||||
filament::CameraInfo const& camera,
|
||||
filament::CameraInfo const& camera, float2 const& csNearFar,
|
||||
float shadowFar, bool stable) noexcept {
|
||||
float2 s, o;
|
||||
if (stable) {
|
||||
@@ -649,7 +626,7 @@ math::float4 ShadowMap::computeFocusParams(
|
||||
return wsViewVolumeBoundingSphere;
|
||||
} else {
|
||||
mat4f const viewFromClip = inverse(camera.cullingProjection);
|
||||
Corners const wsFrustumVertices = computeFrustumCorners(viewFromClip);
|
||||
Corners const wsFrustumVertices = computeFrustumCorners(viewFromClip, csNearFar);
|
||||
float4 const wsViewVolumeBoundingSphere =
|
||||
computeBoundingSphere(wsFrustumVertices.vertices, 8);
|
||||
return wsViewVolumeBoundingSphere;
|
||||
@@ -817,14 +794,15 @@ Aabb ShadowMap::compute2DBounds(const mat4f& lightView,
|
||||
}
|
||||
|
||||
ShadowMap::Corners ShadowMap::computeFrustumCorners(
|
||||
mat4f const& projectionInverse) noexcept {
|
||||
mat4f const& projectionInverse,
|
||||
float2 csNearFar) noexcept {
|
||||
|
||||
Corners out;
|
||||
|
||||
// compute view frustum in world space (from its NDC)
|
||||
// matrix to convert: ndc -> camera -> world
|
||||
float const near = -1.0f;
|
||||
float const far = 1.0f;
|
||||
float const near = csNearFar.x;
|
||||
float const far = csNearFar.y;
|
||||
|
||||
Corners const csViewFrustumCorners = {
|
||||
.vertices = {
|
||||
@@ -917,11 +895,11 @@ void ShadowMap::snapLightFrustum(float2& s, float2& o,
|
||||
|
||||
size_t ShadowMap::intersectFrustumWithBox(
|
||||
FrustumBoxIntersection& UTILS_RESTRICT outVertices,
|
||||
mat4f const& UTILS_RESTRICT projection,
|
||||
mat4f const& UTILS_RESTRICT projection, math::float2 const& UTILS_RESTRICT csNearFar,
|
||||
Aabb const& UTILS_RESTRICT box)
|
||||
{
|
||||
Frustum const frustum{ projection };
|
||||
Corners const frustumVertices{ computeFrustumCorners(inverse(projection)) };
|
||||
Corners const frustumVertices{ computeFrustumCorners(inverse(projection), csNearFar) };
|
||||
|
||||
constexpr const float EPSILON = 1.0f / 8192.0f; // ~0.012 mm
|
||||
size_t vertexCount = 0;
|
||||
@@ -1225,11 +1203,12 @@ void ShadowMap::visitScene(const FScene& scene, uint32_t visibleLayers,
|
||||
}
|
||||
|
||||
ShadowMap::SceneInfo::SceneInfo(
|
||||
FScene const& scene, uint8_t visibleLayers) noexcept
|
||||
: visibleLayers(visibleLayers) {
|
||||
FScene const& scene, uint8_t visibleLayers, mat4f const& viewMatrix) noexcept
|
||||
: vsNearFar(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max()),
|
||||
visibleLayers(visibleLayers) {
|
||||
|
||||
// the code below only works with affine transforms
|
||||
// Filament's API requires viewMatrix to be rigid (and thus affine).
|
||||
assert_invariant(transpose(viewMatrix)[3] == float4(0, 0, 0, 1));
|
||||
|
||||
// We assume the light is at the origin to compute the SceneInfo. This is consumed later by
|
||||
// computeShadowCameraDirectional() which takes this into account.
|
||||
@@ -1245,6 +1224,9 @@ ShadowMap::SceneInfo::SceneInfo(
|
||||
[&](Aabb receiver, Culler::result_type) {
|
||||
wsShadowReceiversVolume.min = min(wsShadowReceiversVolume.min, receiver.min);
|
||||
wsShadowReceiversVolume.max = max(wsShadowReceiversVolume.max, receiver.max);
|
||||
auto r = Aabb::transform(viewMatrix.upperLeft(), viewMatrix[3].xyz, receiver);
|
||||
vsNearFar.x = std::max(vsNearFar.x, r.max.z);
|
||||
vsNearFar.y = std::min(vsNearFar.y, r.min.z);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -109,13 +109,20 @@ public:
|
||||
struct SceneInfo {
|
||||
|
||||
SceneInfo() noexcept = default;
|
||||
SceneInfo(FScene const& scene, uint8_t visibleLayers) noexcept;
|
||||
SceneInfo(FScene const& scene, uint8_t visibleLayers, math::mat4f const& viewMatrix) noexcept;
|
||||
|
||||
// scratch data: The near and far planes, in clip space, to use for this shadow map
|
||||
math::float2 csNearFar = { -1.0f, 1.0f };
|
||||
|
||||
// scratch data: light's near/far expressed in light-space, calculated from the scene's
|
||||
// content assuming the light is at the origin.
|
||||
math::float2 lsCastersNearFar;
|
||||
math::float2 lsReceiversNearFar;
|
||||
|
||||
// Viewing camera's near/far expressed in view-space, calculated from the
|
||||
// scene's content.
|
||||
math::float2 vsNearFar;
|
||||
|
||||
// World-space shadow-casters volume
|
||||
Aabb wsShadowCastersVolume;
|
||||
|
||||
@@ -147,8 +154,7 @@ public:
|
||||
const FScene::LightSoa& lightData, size_t index,
|
||||
filament::CameraInfo const& camera,
|
||||
ShadowMapInfo const& shadowMapInfo,
|
||||
SceneInfo const& sceneInfo,
|
||||
bool useDepthClamp) noexcept;
|
||||
SceneInfo const& sceneInfo) noexcept;
|
||||
|
||||
ShaderParameters updateSpot(FEngine& engine,
|
||||
const FScene::LightSoa& lightData, size_t index,
|
||||
@@ -238,8 +244,7 @@ private:
|
||||
math::float3 direction,
|
||||
FLightManager::ShadowParams params,
|
||||
filament::CameraInfo const& camera,
|
||||
SceneInfo const& sceneInfo,
|
||||
bool useDepthClamp) noexcept;
|
||||
SceneInfo const& sceneInfo) noexcept;
|
||||
|
||||
static math::mat4f applyLISPSM(math::mat4f& Wp,
|
||||
filament::CameraInfo const& camera, FLightManager::ShadowParams const& params,
|
||||
@@ -255,7 +260,7 @@ private:
|
||||
math::mat4f const& LMpMv,
|
||||
math::mat4f const& WLMp,
|
||||
FrustumBoxIntersection const& lsShadowVolume, size_t vertexCount,
|
||||
filament::CameraInfo const& camera,
|
||||
filament::CameraInfo const& camera, math::float2 const& csNearFar,
|
||||
float shadowFar, bool stable) noexcept;
|
||||
|
||||
static inline void snapLightFrustum(math::float2& s, math::float2& o,
|
||||
@@ -266,7 +271,8 @@ private:
|
||||
SceneInfo const& sceneInfo,
|
||||
bool stable, bool focusShadowCasters, bool farUsesShadowCasters) noexcept;
|
||||
|
||||
static Corners computeFrustumCorners(const math::mat4f& projectionInverse) noexcept;
|
||||
static Corners computeFrustumCorners(const math::mat4f& projectionInverse,
|
||||
math::float2 csNearFar = { -1.0f, 1.0f }) noexcept;
|
||||
|
||||
static inline math::float2 computeNearFar(math::mat4f const& view,
|
||||
math::float3 const* wsVertices, size_t count) noexcept;
|
||||
@@ -301,7 +307,7 @@ private:
|
||||
|
||||
static size_t intersectFrustumWithBox(
|
||||
FrustumBoxIntersection& outVertices,
|
||||
math::mat4f const& projection,
|
||||
math::mat4f const& projection, math::float2 const& csNearFar,
|
||||
Aabb const& box);
|
||||
|
||||
static math::mat4f warpFrustum(float n, float f) noexcept;
|
||||
|
||||
@@ -39,20 +39,17 @@
|
||||
#include <backend/DriverApiForward.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/BitmaskEnum.h>
|
||||
#include <utils/Range.h>
|
||||
#include <utils/Slice.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <math/half.h>
|
||||
#include <math/mat4.h>
|
||||
#include <math/vec4.h>
|
||||
#include <math/scalar.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <new>
|
||||
@@ -66,15 +63,12 @@ namespace filament {
|
||||
using namespace backend;
|
||||
using namespace math;
|
||||
|
||||
// do this only if depth-clamp is available
|
||||
static constexpr bool USE_DEPTH_CLAMP = false;
|
||||
|
||||
ShadowMapManager::ShadowMapManager(FEngine& engine) {
|
||||
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
|
||||
debugRegistry.registerProperty("d.shadowmap.visualize_cascades",
|
||||
&engine.debug.shadowmap.visualize_cascades);
|
||||
debugRegistry.registerProperty("d.shadowmap.disable_light_frustum_align",
|
||||
&engine.debug.shadowmap.disable_light_frustum_align);
|
||||
debugRegistry.registerProperty("d.shadowmap.tightly_bound_scene",
|
||||
&engine.debug.shadowmap.tightly_bound_scene);
|
||||
}
|
||||
|
||||
ShadowMapManager::~ShadowMapManager() {
|
||||
@@ -153,7 +147,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::update(
|
||||
calculateTextureRequirements(engine, view, lightData);
|
||||
|
||||
// Compute scene-dependent values shared across all shadow maps
|
||||
ShadowMap::SceneInfo const info{ *view.getScene(), view.getVisibleLayers() };
|
||||
ShadowMap::SceneInfo const info{ *view.getScene(), view.getVisibleLayers(), cameraInfo.view };
|
||||
|
||||
shadowTechnique |= updateCascadeShadowMaps(
|
||||
engine, view, cameraInfo, renderableData, lightData, info);
|
||||
@@ -549,9 +543,65 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
FLightManager::ShadowParams const& params = lcm.getShadowParams(directionalLight);
|
||||
|
||||
// Adjust the camera's projection for the light's shadowFar
|
||||
cameraInfo.zf = params.options.shadowFar > 0.0f ? params.options.shadowFar : cameraInfo.zf;
|
||||
if (UTILS_UNLIKELY(params.options.shadowFar > 0.0f)) {
|
||||
cameraInfo.zf = params.options.shadowFar;
|
||||
updateNearFarPlanes(&cameraInfo.cullingProjection, cameraInfo.zn, cameraInfo.zf);
|
||||
float const n = cameraInfo.zn;
|
||||
float const f = cameraInfo.zf;
|
||||
|
||||
/*
|
||||
* Updating a projection matrix near and far planes:
|
||||
*
|
||||
* We assume that the near and far plane equations are of the form:
|
||||
* N = { 0, 0, 1, n }
|
||||
* F = { 0, 0, -1, -f }
|
||||
*
|
||||
* We also assume that the lower-left 2x2 of the projection is all 0:
|
||||
* P = A 0 C 0
|
||||
* 0 B D 0
|
||||
* 0 0 E F
|
||||
* 0 0 G H
|
||||
*
|
||||
* It result that we need to calculate E and F while leaving all other parameter unchanged.
|
||||
*
|
||||
* We know that:
|
||||
* with N, F the near/far normalized plane equation parameters
|
||||
* sn, sf arbitrary scale factors (they don't affect the planes)
|
||||
* m is the transpose of projection (see Frustum.cpp)
|
||||
*
|
||||
* sn * N == -m[3] - m[2]
|
||||
* sf * F == -m[3] + m[2]
|
||||
*
|
||||
* sn * N + sf * F == -2 * m[3]
|
||||
* sn * N - sf * F == -2 * m[2]
|
||||
*
|
||||
* sn * N.z + sf * F.z == -2 * m[3].z
|
||||
* sn * N.w + sf * F.w == -2 * m[3].w
|
||||
* sn * N.z - sf * F.z == -2 * m[2].z
|
||||
* sn * N.w - sf * F.w == -2 * m[2].w
|
||||
*
|
||||
* sn * N.z + sf * F.z == -2 * p[2].w
|
||||
* sn * N.w + sf * F.w == -2 * p[3].w
|
||||
* sn * N.z - sf * F.z == -2 * p[2].z
|
||||
* sn * N.w - sf * F.w == -2 * p[3].z
|
||||
*
|
||||
* We now need to solve for { p[2].z, p[3].z, sn, sf } :
|
||||
*
|
||||
* sn == -2 * (p[2].w * F.w - p[3].w * F.z) / (N.z * F.w - N.w * F.z)
|
||||
* sf == -2 * (p[2].w * N.w - p[3].w * N.z) / (F.z * N.w - F.w * N.z)
|
||||
* p[2].z == (sf * F.z - sn * N.z) / 2
|
||||
* p[3].z == (sf * F.w - sn * N.w) / 2
|
||||
*/
|
||||
auto& p = cameraInfo.cullingProjection;
|
||||
float4 const N = { 0, 0, 1, n }; // near plane equation
|
||||
float4 const F = { 0, 0, -1, -f }; // far plane equation
|
||||
// near plane equation scale factor
|
||||
float const sn = -2.0f * (p[2].w * F.w - p[3].w * F.z) / (N.z * F.w - N.w * F.z);
|
||||
// far plane equation scale factor
|
||||
float const sf = -2.0f * (p[2].w * N.w - p[3].w * N.z) / (F.z * N.w - F.w * N.z);
|
||||
// New values for the projection
|
||||
p[2].z = (sf * F.z - sn * N.z) * 0.5f;
|
||||
p[3].z = (sf * F.w - sn * N.w) * 0.5f;
|
||||
}
|
||||
|
||||
const ShadowMap::ShadowMapInfo shadowMapInfo{
|
||||
@@ -581,10 +631,8 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
// Compute scene-dependent values shared across all cascades
|
||||
ShadowMap::updateSceneInfoDirectional(MvAtOrigin, *scene, sceneInfo);
|
||||
|
||||
// we always do culling without depth clamp, because objects behind the camera
|
||||
// must be rendered regardless
|
||||
shadowMap.updateDirectional(engine,
|
||||
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo, false);
|
||||
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo);
|
||||
|
||||
hasVisibleShadows = shadowMap.hasVisibleShadows();
|
||||
|
||||
@@ -600,7 +648,15 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
uint32_t cascadeHasVisibleShadows = 0;
|
||||
|
||||
if (hasVisibleShadows) {
|
||||
uint32_t const cascadeCount = cascadedShadowMaps.size();
|
||||
// Adjust the near and far planes to tightly bound the scene.
|
||||
float vsNear = -cameraInfo.zn;
|
||||
float vsFar = -cameraInfo.zf;
|
||||
if (engine.debug.shadowmap.tightly_bound_scene && !params.options.stable) {
|
||||
vsNear = std::min(vsNear, sceneInfo.vsNearFar.x);
|
||||
vsFar = std::max(vsFar, sceneInfo.vsNearFar.y);
|
||||
}
|
||||
|
||||
const size_t cascadeCount = cascadedShadowMaps.size();
|
||||
|
||||
// We divide the camera frustum into N cascades. This gives us N + 1 split positions.
|
||||
// The first split position is the near plane; the last split position is the far plane.
|
||||
@@ -611,8 +667,9 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
}
|
||||
|
||||
const CascadeSplits splits({
|
||||
.near = -cameraInfo.zn,
|
||||
.far = -cameraInfo.zf,
|
||||
.proj = cameraInfo.cullingProjection,
|
||||
.near = vsNear,
|
||||
.far = vsFar,
|
||||
.cascadeCount = cascadeCount,
|
||||
.splitPositions = splitPercentages
|
||||
});
|
||||
@@ -622,7 +679,10 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
static_assert(CONFIG_MAX_SHADOW_CASCADES <= 5,
|
||||
"At most, a float4 can fit 4 split positions for 5 shadow cascades");
|
||||
float4 wsSplitPositionUniform{ -std::numeric_limits<float>::infinity() };
|
||||
std::copy(splits.begin() + 1, splits.end(), &wsSplitPositionUniform[0]);
|
||||
std::copy(splits.beginWs() + 1, splits.endWs(), &wsSplitPositionUniform[0]);
|
||||
|
||||
float csSplitPosition[CONFIG_MAX_SHADOW_CASCADES + 1];
|
||||
std::copy(splits.beginCs(), splits.endCs(), csSplitPosition);
|
||||
|
||||
mShadowMappingUniforms.cascadeSplits = wsSplitPositionUniform;
|
||||
|
||||
@@ -635,14 +695,10 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
ShadowMap& shadowMap = cascadedShadowMaps[i];
|
||||
assert_invariant(shadowMap.getLightIndex() == 0);
|
||||
|
||||
// update cameraInfo culling projection for the cascade
|
||||
float const* nearFarPlanes = splits.begin();
|
||||
cameraInfo.zn = -nearFarPlanes[i];
|
||||
cameraInfo.zf = -nearFarPlanes[i + 1];
|
||||
updateNearFarPlanes(&cameraInfo.cullingProjection, cameraInfo.zn, cameraInfo.zf);
|
||||
sceneInfo.csNearFar = { csSplitPosition[i], csSplitPosition[i + 1] };
|
||||
|
||||
auto shaderParameters = shadowMap.updateDirectional(engine,
|
||||
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo, USE_DEPTH_CLAMP);
|
||||
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo);
|
||||
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
const size_t shadowIndex = shadowMap.getShadowIndex();
|
||||
@@ -1013,78 +1069,8 @@ ShadowMapManager::CascadeSplits::CascadeSplits(Params const& params) noexcept
|
||||
: mSplitCount(params.cascadeCount + 1) {
|
||||
for (size_t s = 0; s < mSplitCount; s++) {
|
||||
mSplitsWs[s] = params.near + (params.far - params.near) * params.splitPositions[s];
|
||||
mSplitsCs[s] = mat4f::project(params.proj, float3(0.0f, 0.0f, mSplitsWs[s])).z;
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowMapManager::updateNearFarPlanes(mat4f* projection,
|
||||
float nearDistance, float farDistance) noexcept {
|
||||
float const n = nearDistance;
|
||||
float const f = farDistance;
|
||||
|
||||
/*
|
||||
* Updating a projection matrix near and far planes:
|
||||
*
|
||||
* We assume that the near and far plane equations are of the form:
|
||||
* N = { 0, 0, 1, n }
|
||||
* F = { 0, 0, -1, -f }
|
||||
*
|
||||
* We also assume that the lower-left 2x2 of the projection is all 0:
|
||||
* P = A 0 C 0
|
||||
* 0 B D 0
|
||||
* 0 0 E F
|
||||
* 0 0 G H
|
||||
*
|
||||
* It result that we need to calculate E and F while leaving all other parameter unchanged.
|
||||
*
|
||||
* We know that:
|
||||
* with N, F the near/far normalized plane equation parameters
|
||||
* sn, sf arbitrary scale factors (they don't affect the planes)
|
||||
* m is the transpose of projection (see Frustum.cpp)
|
||||
*
|
||||
* sn * N == -m[3] - m[2]
|
||||
* sf * F == -m[3] + m[2]
|
||||
*
|
||||
* sn * N + sf * F == -2 * m[3]
|
||||
* sn * N - sf * F == -2 * m[2]
|
||||
*
|
||||
* sn * N.z + sf * F.z == -2 * m[3].z
|
||||
* sn * N.w + sf * F.w == -2 * m[3].w
|
||||
* sn * N.z - sf * F.z == -2 * m[2].z
|
||||
* sn * N.w - sf * F.w == -2 * m[2].w
|
||||
*
|
||||
* sn * N.z + sf * F.z == -2 * p[2].w
|
||||
* sn * N.w + sf * F.w == -2 * p[3].w
|
||||
* sn * N.z - sf * F.z == -2 * p[2].z
|
||||
* sn * N.w - sf * F.w == -2 * p[3].z
|
||||
*
|
||||
* We now need to solve for { p[2].z, p[3].z, sn, sf } :
|
||||
*
|
||||
* sn == -2 * (p[2].w * F.w - p[3].w * F.z) / (N.z * F.w - N.w * F.z)
|
||||
* sf == -2 * (p[2].w * N.w - p[3].w * N.z) / (F.z * N.w - F.w * N.z)
|
||||
* p[2].z == (sf * F.z - sn * N.z) / 2
|
||||
* p[3].z == (sf * F.w - sn * N.w) / 2
|
||||
*/
|
||||
auto& p = *projection;
|
||||
float4 const N = { 0, 0, 1, n }; // near plane equation
|
||||
float4 const F = { 0, 0, -1, -f }; // far plane equation
|
||||
// near plane equation scale factor
|
||||
float const sn = -2.0f * (p[2].w * F.w - p[3].w * F.z) / (N.z * F.w - N.w * F.z);
|
||||
// far plane equation scale factor
|
||||
float const sf = -2.0f * (p[2].w * N.w - p[3].w * N.z) / (F.z * N.w - F.w * N.z);
|
||||
// New values for the projection
|
||||
p[2].z = (sf * F.z - sn * N.z) * 0.5f;
|
||||
p[3].z = (sf * F.w - sn * N.w) * 0.5f;
|
||||
}
|
||||
|
||||
utils::FixedCapacityVector<Camera const*>
|
||||
ShadowMapManager::getDirectionalShadowCameras() const noexcept {
|
||||
if (!mInitialized) return {};
|
||||
auto const csm = getCascadedShadowMap();
|
||||
auto result = utils::FixedCapacityVector<Camera const*>::with_capacity(csm.size());
|
||||
for (ShadowMap const& sm : csm) {
|
||||
result.push_back(sm.hasVisibleShadows() ? sm.getDebugCamera() : nullptr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
|
||||
#include <utils/BitmaskEnum.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Range.h>
|
||||
#include <utils/Slice.h>
|
||||
@@ -59,7 +58,6 @@
|
||||
|
||||
namespace filament {
|
||||
|
||||
class Camera;
|
||||
class FCamera;
|
||||
class FView;
|
||||
class FrameGraph;
|
||||
@@ -140,16 +138,16 @@ public:
|
||||
bool hasSpotShadows() const { return !mSpotShadowMapCount; }
|
||||
|
||||
// for debugging only
|
||||
utils::FixedCapacityVector<Camera const*> getDirectionalShadowCameras() const noexcept;
|
||||
FCamera const* getDirectionalShadowCamera() const noexcept {
|
||||
if (!mInitialized) return nullptr;
|
||||
return getShadowMap(0).getDebugCamera();
|
||||
}
|
||||
|
||||
private:
|
||||
explicit ShadowMapManager(FEngine& engine);
|
||||
|
||||
void terminate(FEngine& engine);
|
||||
|
||||
static void updateNearFarPlanes(math::mat4f* projection,
|
||||
float nearDistance, float farDistance) noexcept;
|
||||
|
||||
ShadowMapManager::ShadowTechnique updateCascadeShadowMaps(FEngine& engine,
|
||||
FView& view, CameraInfo cameraInfo, FScene::RenderableSoa& renderableData,
|
||||
FScene::LightSoa const& lightData, ShadowMap::SceneInfo sceneInfo) noexcept;
|
||||
@@ -188,21 +186,27 @@ private:
|
||||
constexpr static size_t SPLIT_COUNT = CONFIG_MAX_SHADOW_CASCADES + 1;
|
||||
|
||||
struct Params {
|
||||
math::mat4f proj;
|
||||
float near = 0.0f;
|
||||
float far = 0.0f;
|
||||
uint32_t cascadeCount = 1;
|
||||
size_t cascadeCount = 1;
|
||||
std::array<float, SPLIT_COUNT> splitPositions = { 0.0f };
|
||||
};
|
||||
|
||||
explicit CascadeSplits(Params const& params) noexcept;
|
||||
|
||||
// Split positions in world-space.
|
||||
const float* begin() const { return mSplitsWs; }
|
||||
const float* end() const { return mSplitsWs + mSplitCount; }
|
||||
const float* beginWs() const { return mSplitsWs; }
|
||||
const float* endWs() const { return mSplitsWs + mSplitCount; }
|
||||
|
||||
// Split positions in clip-space.
|
||||
const float* beginCs() const { return mSplitsCs; }
|
||||
const float* endCs() const { return mSplitsCs + mSplitCount; }
|
||||
|
||||
private:
|
||||
float mSplitsWs[SPLIT_COUNT];
|
||||
uint32_t mSplitCount;
|
||||
float mSplitsCs[SPLIT_COUNT];
|
||||
size_t mSplitCount;
|
||||
};
|
||||
|
||||
// Atlas requirements, updated in ShadowMapManager::update(),
|
||||
@@ -244,14 +248,10 @@ private:
|
||||
}
|
||||
|
||||
utils::Slice<ShadowMap> getCascadedShadowMap() noexcept {
|
||||
ShadowMap const* const p = &getShadowMap(0);
|
||||
ShadowMap* const p = &getShadowMap(0);
|
||||
return { p, mDirectionalShadowMapCount };
|
||||
}
|
||||
|
||||
utils::Slice<ShadowMap> getCascadedShadowMap() const noexcept {
|
||||
return const_cast<ShadowMapManager*>(this)->getCascadedShadowMap();
|
||||
}
|
||||
|
||||
utils::Slice<ShadowMap> getSpotShadowMaps() noexcept {
|
||||
ShadowMap* const p = &getShadowMap(CONFIG_MAX_SHADOW_CASCADES);
|
||||
return { p, mSpotShadowMapCount };
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
|
||||
#include "details/View.h"
|
||||
#include "filament/View.h"
|
||||
|
||||
|
||||
namespace filament {
|
||||
|
||||
@@ -73,8 +71,8 @@ const char* View::getName() const noexcept {
|
||||
return downcast(this)->getName();
|
||||
}
|
||||
|
||||
utils::FixedCapacityVector<Camera const*> View::getDirectionalShadowCameras() const noexcept {
|
||||
return downcast(this)->getDirectionalShadowCameras();
|
||||
Camera const* View::getDirectionalShadowCamera() const noexcept {
|
||||
return downcast(this)->getDirectionalShadowCamera();
|
||||
}
|
||||
|
||||
void View::setShadowingEnabled(bool enabled) noexcept {
|
||||
@@ -314,8 +312,4 @@ utils::Entity View::getFogEntity() const noexcept {
|
||||
return downcast(this)->getFogEntity();
|
||||
}
|
||||
|
||||
void View::clearFrameHistory(Engine& engine) noexcept {
|
||||
downcast(this)->clearFrameHistory(downcast(engine));
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -20,14 +20,13 @@
|
||||
|
||||
#include "details/Engine.h"
|
||||
|
||||
#include <filament/LightManager.h>
|
||||
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <math/fast.h>
|
||||
#include <math/scalar.h>
|
||||
|
||||
#include <utils/debug.h>
|
||||
#include <filament/LightManager.h>
|
||||
|
||||
|
||||
using namespace filament::math;
|
||||
using namespace utils;
|
||||
|
||||
|
||||
@@ -443,10 +443,10 @@ RenderableManager::Builder::Result RenderableManager::Builder::build(Engine& eng
|
||||
<< ") instances when supplying transforms via an InstanceBuffer.";
|
||||
|
||||
if (mImpl->mGeometryType == GeometryType::STATIC) {
|
||||
FILAMENT_CHECK_PRECONDITION(mImpl->mSkinningBoneCount == 0)
|
||||
FILAMENT_CHECK_PRECONDITION(mImpl->mSkinningBoneCount > 0)
|
||||
<< "Skinning can't be used with STATIC geometry";
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(mImpl->mMorphTargetCount == 0)
|
||||
FILAMENT_CHECK_PRECONDITION(mImpl->mMorphTargetCount > 0)
|
||||
<< "Morphing can't be used with STATIC geometry";
|
||||
}
|
||||
|
||||
@@ -588,9 +588,6 @@ void FRenderableManager::create(
|
||||
// full size of the UBO.
|
||||
instances.handle = driver.createBufferObject(sizeof(PerRenderableUib),
|
||||
BufferObjectBinding::UNIFORM, backend::BufferUsage::DYNAMIC);
|
||||
if (auto name = instances.buffer->getName(); !name.empty()) {
|
||||
driver.setDebugTag(instances.handle.getId(), std::move(name));
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t boneCount = builder->mSkinningBoneCount;
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
|
||||
#include "FilamentAPI-impl.h"
|
||||
|
||||
#include <utils/CString.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
struct BufferObject::BuilderDetails {
|
||||
@@ -58,9 +56,6 @@ FBufferObject::FBufferObject(FEngine& engine, const BufferObject::Builder& build
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
mHandle = driver.createBufferObject(builder->mByteCount, builder->mBindingType,
|
||||
backend::BufferUsage::STATIC);
|
||||
if (auto name = builder.getName(); !name.empty()) {
|
||||
driver.setDebugTag(mHandle.getId(), std::move(name));
|
||||
}
|
||||
}
|
||||
|
||||
void FBufferObject::terminate(FEngine& engine) {
|
||||
|
||||
@@ -118,32 +118,19 @@ template bool FDebugRegistry::getProperty<float2>(const char* name, float2* v) c
|
||||
template bool FDebugRegistry::getProperty<float3>(const char* name, float3* v) const noexcept;
|
||||
template bool FDebugRegistry::getProperty<float4>(const char* name, float4* v) const noexcept;
|
||||
|
||||
bool FDebugRegistry::registerDataSource(std::string_view name,
|
||||
void FDebugRegistry::registerDataSource(std::string_view name,
|
||||
void const* data, size_t count) noexcept {
|
||||
auto& dataSourceMap = mDataSourceMap;
|
||||
bool const found = dataSourceMap.find(name) == dataSourceMap.end();
|
||||
if (found) {
|
||||
if (dataSourceMap.find(name) == dataSourceMap.end()) {
|
||||
dataSourceMap[name] = { data, count };
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
bool FDebugRegistry::registerDataSource(std::string_view name,
|
||||
void FDebugRegistry::registerDataSource(std::string_view name,
|
||||
utils::Invocable<DataSource()>&& creator) noexcept {
|
||||
auto& dataSourceCreatorMap = mDataSourceCreatorMap;
|
||||
bool const found = dataSourceCreatorMap.find(name) == dataSourceCreatorMap.end();
|
||||
if (found) {
|
||||
dataSourceCreatorMap[name] = std::move(creator);
|
||||
}
|
||||
return found;
|
||||
mDataSourceCreatorMap[name] = std::move(creator);
|
||||
}
|
||||
|
||||
void FDebugRegistry::unregisterDataSource(std::string_view name) noexcept {
|
||||
mDataSourceCreatorMap.erase(name);
|
||||
mDataSourceMap.erase(name);
|
||||
}
|
||||
|
||||
|
||||
DebugRegistry::DataSource FDebugRegistry::getDataSource(const char* name) const noexcept {
|
||||
std::string_view const key{ name };
|
||||
auto& dataSourceMap = mDataSourceMap;
|
||||
|
||||
@@ -101,14 +101,12 @@ public:
|
||||
}
|
||||
|
||||
// registers a DataSource directly
|
||||
bool registerDataSource(std::string_view name, void const* data, size_t count) noexcept;
|
||||
void registerDataSource(std::string_view name, void const* data, size_t count) noexcept;
|
||||
|
||||
// registers a DataSource lazily
|
||||
bool registerDataSource(std::string_view name,
|
||||
void registerDataSource(std::string_view name,
|
||||
utils::Invocable<DataSource()>&& creator) noexcept;
|
||||
|
||||
void unregisterDataSource(std::string_view name) noexcept;
|
||||
|
||||
#if !defined(_MSC_VER)
|
||||
private:
|
||||
#endif
|
||||
|
||||
@@ -71,7 +71,7 @@ struct Engine::BuilderDetails {
|
||||
FeatureLevel mFeatureLevel = FeatureLevel::FEATURE_LEVEL_1;
|
||||
void* mSharedContext = nullptr;
|
||||
bool mPaused = false;
|
||||
static Config validateConfig(Config config) noexcept;
|
||||
static Config validateConfig(const Config* pConfig) noexcept;
|
||||
};
|
||||
|
||||
Engine* FEngine::create(Engine::Builder const& builder) {
|
||||
@@ -163,7 +163,8 @@ FEngine* FEngine::getEngine(void* token) {
|
||||
FILAMENT_CHECK_PRECONDITION(ThreadUtils::isThisThread(instance->mMainThreadId))
|
||||
<< "Engine::createAsync() and Engine::getEngine() must be called on the same thread.";
|
||||
|
||||
if (!instance->mInitialized) {
|
||||
// we use mResourceAllocator as a proxy for "am I already initialized"
|
||||
if (!instance->mResourceAllocator) {
|
||||
if (UTILS_UNLIKELY(!instance->mDriver)) {
|
||||
// something went horribly wrong during driver initialization
|
||||
instance->mDriverThread.join();
|
||||
@@ -261,7 +262,7 @@ void FEngine::init() {
|
||||
slog.i << "FEngine feature level: " << int(mActiveFeatureLevel) << io::endl;
|
||||
|
||||
|
||||
mResourceAllocatorDisposer = std::make_shared<ResourceAllocatorDisposer>(driverApi);
|
||||
mResourceAllocator = new ResourceAllocator(mConfig, driverApi);
|
||||
|
||||
mFullScreenTriangleVb = downcast(VertexBuffer::Builder()
|
||||
.vertexCount(3)
|
||||
@@ -424,13 +425,11 @@ void FEngine::init() {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
mInitialized = true;
|
||||
}
|
||||
|
||||
FEngine::~FEngine() noexcept {
|
||||
SYSTRACE_CALL();
|
||||
assert_invariant(!mResourceAllocatorDisposer);
|
||||
delete mResourceAllocator;
|
||||
delete mDriver;
|
||||
if (mOwnPlatform) {
|
||||
PlatformFactory::destroy(&mPlatform);
|
||||
@@ -441,7 +440,7 @@ void FEngine::shutdown() {
|
||||
SYSTRACE_CALL();
|
||||
|
||||
// by construction this should never be nullptr
|
||||
assert_invariant(mResourceAllocatorDisposer);
|
||||
assert_invariant(mResourceAllocator);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(ThreadUtils::isThisThread(mMainThreadId))
|
||||
<< "Engine::shutdown() called from the wrong thread!";
|
||||
@@ -461,8 +460,7 @@ void FEngine::shutdown() {
|
||||
*/
|
||||
|
||||
mPostProcessManager.terminate(driver); // free-up post-process manager resources
|
||||
mResourceAllocatorDisposer->terminate();
|
||||
mResourceAllocatorDisposer.reset();
|
||||
mResourceAllocator->terminate();
|
||||
mDFG.terminate(*this); // free-up the DFG
|
||||
mRenderableManager.terminate(); // free-up all renderables
|
||||
mLightManager.terminate(); // free-up all lights
|
||||
@@ -1197,22 +1195,6 @@ bool FEngine::isValid(const FInstanceBuffer* p) const {
|
||||
return isValid(p, mInstanceBuffers);
|
||||
}
|
||||
|
||||
size_t FEngine::getBufferObjectCount() const noexcept { return mBufferObjects.size(); }
|
||||
size_t FEngine::getViewCount() const noexcept { return mViews.size(); }
|
||||
size_t FEngine::getSceneCount() const noexcept { return mScenes.size(); }
|
||||
size_t FEngine::getSwapChainCount() const noexcept { return mSwapChains.size(); }
|
||||
size_t FEngine::getStreamCount() const noexcept { return mStreams.size(); }
|
||||
size_t FEngine::getIndexBufferCount() const noexcept { return mIndexBuffers.size(); }
|
||||
size_t FEngine::getSkinningBufferCount() const noexcept { return mSkinningBuffers.size(); }
|
||||
size_t FEngine::getMorphTargetBufferCount() const noexcept { return mMorphTargetBuffers.size(); }
|
||||
size_t FEngine::getInstanceBufferCount() const noexcept { return mInstanceBuffers.size(); }
|
||||
size_t FEngine::getVertexBufferCount() const noexcept { return mVertexBuffers.size(); }
|
||||
size_t FEngine::getIndirectLightCount() const noexcept { return mIndirectLights.size(); }
|
||||
size_t FEngine::getMaterialCount() const noexcept { return mMaterials.size(); }
|
||||
size_t FEngine::getTextureCount() const noexcept { return mTextures.size(); }
|
||||
size_t FEngine::getSkyboxeCount() const noexcept { return mSkyboxes.size(); }
|
||||
size_t FEngine::getColorGradingCount() const noexcept { return mColorGradings.size(); }
|
||||
size_t FEngine::getRenderTargetCount() const noexcept { return mRenderTargets.size(); }
|
||||
|
||||
void* FEngine::streamAlloc(size_t size, size_t alignment) noexcept {
|
||||
// we allow this only for small allocations
|
||||
@@ -1302,7 +1284,7 @@ Engine::Builder& Engine::Builder::platform(Platform* platform) noexcept {
|
||||
}
|
||||
|
||||
Engine::Builder& Engine::Builder::config(Engine::Config const* config) noexcept {
|
||||
mImpl->mConfig = config ? *config : Engine::Config{};
|
||||
mImpl->mConfig = BuilderDetails::validateConfig(config);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -1330,15 +1312,22 @@ void Engine::Builder::build(Invocable<void(void*)>&& callback) const {
|
||||
#endif
|
||||
|
||||
Engine* Engine::Builder::build() const {
|
||||
mImpl->mConfig = BuilderDetails::validateConfig(mImpl->mConfig);
|
||||
return FEngine::create(*this);
|
||||
}
|
||||
|
||||
Engine::Config Engine::BuilderDetails::validateConfig(Config config) noexcept {
|
||||
Engine::Config Engine::BuilderDetails::validateConfig(const Config* const pConfig) noexcept {
|
||||
// Rule of thumb: perRenderPassArenaMB must be roughly 1 MB larger than perFrameCommandsMB
|
||||
constexpr uint32_t COMMAND_ARENA_OVERHEAD = 1;
|
||||
constexpr uint32_t CONCURRENT_FRAME_COUNT = 3;
|
||||
|
||||
Config config;
|
||||
if (!pConfig) {
|
||||
return config;
|
||||
}
|
||||
|
||||
// make sure to copy all the fields
|
||||
config = *pConfig;
|
||||
|
||||
// Use at least the defaults set by the build system
|
||||
config.minCommandBufferSizeMB = std::max(
|
||||
config.minCommandBufferSizeMB,
|
||||
|
||||
@@ -86,7 +86,6 @@ namespace filament {
|
||||
|
||||
class Renderer;
|
||||
class MaterialParser;
|
||||
class ResourceAllocatorDisposer;
|
||||
|
||||
namespace backend {
|
||||
class Driver;
|
||||
@@ -255,13 +254,9 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
ResourceAllocatorDisposer& getResourceAllocatorDisposer() noexcept {
|
||||
assert_invariant(mResourceAllocatorDisposer);
|
||||
return *mResourceAllocatorDisposer;
|
||||
}
|
||||
|
||||
std::shared_ptr<ResourceAllocatorDisposer> const& getSharedResourceAllocatorDisposer() noexcept {
|
||||
return mResourceAllocatorDisposer;
|
||||
ResourceAllocator& getResourceAllocator() noexcept {
|
||||
assert_invariant(mResourceAllocator);
|
||||
return *mResourceAllocator;
|
||||
}
|
||||
|
||||
void* streamAlloc(size_t size, size_t alignment) noexcept;
|
||||
@@ -351,23 +346,6 @@ public:
|
||||
bool isValid(const FView* p) const;
|
||||
bool isValid(const FInstanceBuffer* p) const;
|
||||
|
||||
size_t getBufferObjectCount() const noexcept;
|
||||
size_t getViewCount() const noexcept;
|
||||
size_t getSceneCount() const noexcept;
|
||||
size_t getSwapChainCount() const noexcept;
|
||||
size_t getStreamCount() const noexcept;
|
||||
size_t getIndexBufferCount() const noexcept;
|
||||
size_t getSkinningBufferCount() const noexcept;
|
||||
size_t getMorphTargetBufferCount() const noexcept;
|
||||
size_t getInstanceBufferCount() const noexcept;
|
||||
size_t getVertexBufferCount() const noexcept;
|
||||
size_t getIndirectLightCount() const noexcept;
|
||||
size_t getMaterialCount() const noexcept;
|
||||
size_t getTextureCount() const noexcept;
|
||||
size_t getSkyboxeCount() const noexcept;
|
||||
size_t getColorGradingCount() const noexcept;
|
||||
size_t getRenderTargetCount() const noexcept;
|
||||
|
||||
void destroy(utils::Entity e);
|
||||
|
||||
bool isPaused() const noexcept;
|
||||
@@ -512,7 +490,7 @@ private:
|
||||
FTransformManager mTransformManager;
|
||||
FLightManager mLightManager;
|
||||
FCameraManager mCameraManager;
|
||||
std::shared_ptr<ResourceAllocatorDisposer> mResourceAllocatorDisposer;
|
||||
ResourceAllocator* mResourceAllocator = nullptr;
|
||||
HwVertexBufferInfoFactory mHwVertexBufferInfoFactory;
|
||||
|
||||
ResourceList<FBufferObject> mBufferObjects{ "BufferObject" };
|
||||
@@ -584,14 +562,11 @@ private:
|
||||
|
||||
std::thread::id mMainThreadId{};
|
||||
|
||||
bool mInitialized = false;
|
||||
|
||||
// Creation parameters
|
||||
Config mConfig;
|
||||
|
||||
public:
|
||||
// These are the debug properties used by FDebug.
|
||||
// They're accessed directly by modules who need them.
|
||||
// these are the debug properties used by FDebug. They're accessed directly by modules who need them.
|
||||
struct {
|
||||
struct {
|
||||
bool debug_directional_shadowmap = false;
|
||||
@@ -599,7 +574,7 @@ public:
|
||||
bool far_uses_shadowcasters = true;
|
||||
bool focus_shadowcasters = true;
|
||||
bool visualize_cascades = false;
|
||||
bool disable_light_frustum_align = false;
|
||||
bool tightly_bound_scene = true;
|
||||
float dzn = -1.0f;
|
||||
float dzf = 1.0f;
|
||||
float display_shadow_texture_scale = 0.25f;
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
|
||||
#include "FilamentAPI-impl.h"
|
||||
|
||||
#include <utils/CString.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
struct IndexBuffer::BuilderDetails {
|
||||
@@ -60,9 +58,6 @@ FIndexBuffer::FIndexBuffer(FEngine& engine, const IndexBuffer::Builder& builder)
|
||||
(backend::ElementType)builder->mIndexType,
|
||||
uint32_t(builder->mIndexCount),
|
||||
backend::BufferUsage::STATIC);
|
||||
if (auto name = builder.getName(); !name.empty()) {
|
||||
driver.setDebugTag(mHandle.getId(), std::move(name));
|
||||
}
|
||||
}
|
||||
|
||||
void FIndexBuffer::terminate(FEngine& engine) {
|
||||
|
||||
@@ -61,8 +61,7 @@ InstanceBuffer* InstanceBuffer::Builder::build(Engine& engine) {
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
FInstanceBuffer::FInstanceBuffer(FEngine& engine, const Builder& builder)
|
||||
: mName(builder.getName()) {
|
||||
FInstanceBuffer::FInstanceBuffer(FEngine& engine, const Builder& builder) {
|
||||
mInstanceCount = builder->mInstanceCount;
|
||||
|
||||
mLocalTransforms.reserve(mInstanceCount);
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
#include <math/mat4.h>
|
||||
|
||||
#include <utils/CString.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
namespace filament {
|
||||
@@ -47,13 +46,10 @@ public:
|
||||
void prepare(FEngine& engine, math::mat4f rootTransform, const PerRenderableData& ubo,
|
||||
backend::Handle<backend::HwBufferObject> handle);
|
||||
|
||||
utils::CString const& getName() const noexcept { return mName; }
|
||||
|
||||
private:
|
||||
friend class RenderableManager;
|
||||
|
||||
utils::FixedCapacityVector<math::mat4f> mLocalTransforms;
|
||||
utils::CString mName;
|
||||
size_t mInstanceCount;
|
||||
};
|
||||
|
||||
|
||||
@@ -192,30 +192,6 @@ Material* Material::Builder::build(Engine& engine) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Print a warning if the material's stereo type doesn't align with the engine's setting.
|
||||
MaterialDomain materialDomain;
|
||||
UserVariantFilterMask variantFilterMask;
|
||||
materialParser->getMaterialDomain(&materialDomain);
|
||||
materialParser->getMaterialVariantFilterMask(&variantFilterMask);
|
||||
bool const hasStereoVariants = !(variantFilterMask & UserVariantFilterMask(UserVariantFilterBit::STE));
|
||||
if (materialDomain == MaterialDomain::SURFACE && hasStereoVariants) {
|
||||
StereoscopicType const engineStereoscopicType = engine.getConfig().stereoscopicType;
|
||||
// Default materials are always compiled with either 'instanced' or 'multiview'.
|
||||
// So, we only verify compatibility if the engine is set up for stereo.
|
||||
if (engineStereoscopicType != StereoscopicType::NONE) {
|
||||
StereoscopicType materialStereoscopicType = StereoscopicType::NONE;
|
||||
materialParser->getStereoscopicType(&materialStereoscopicType);
|
||||
if (materialStereoscopicType != engineStereoscopicType) {
|
||||
CString name;
|
||||
materialParser->getName(&name);
|
||||
slog.w << "The stereoscopic type in the compiled material '" << name.c_str_safe()
|
||||
<< "' is " << (int)materialStereoscopicType
|
||||
<< ", which is not compatiable with the engine's setting "
|
||||
<< (int)engineStereoscopicType << "." << io::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return downcast(engine).createMaterial(*this, std::move(materialParser));
|
||||
}
|
||||
|
||||
@@ -224,7 +200,8 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder,
|
||||
: mIsDefaultMaterial(builder->mDefaultMaterial),
|
||||
mEngine(engine),
|
||||
mMaterialId(engine.getMaterialId()),
|
||||
mMaterialParser(std::move(materialParser)) {
|
||||
mMaterialParser(std::move(materialParser))
|
||||
{
|
||||
MaterialParser* const parser = mMaterialParser.get();
|
||||
|
||||
UTILS_UNUSED_IN_RELEASE bool const nameOk = parser->getName(&mName);
|
||||
@@ -618,7 +595,6 @@ Program FMaterial::getProgramWithVariants(
|
||||
|
||||
void FMaterial::createAndCacheProgram(Program&& p, Variant variant) const noexcept {
|
||||
auto program = mEngine.getDriverApi().createProgram(std::move(p));
|
||||
mEngine.getDriverApi().setDebugTag(program.getId(), mName);
|
||||
assert_invariant(program);
|
||||
mCachedPrograms[variant.key] = program;
|
||||
}
|
||||
@@ -897,7 +873,7 @@ void FMaterial::processSpecializationConstants(FEngine& engine, Material::Builde
|
||||
engine.getDriverApi().getMaxUniformBufferSize() / 16u);
|
||||
|
||||
bool const staticTextureWorkaround =
|
||||
engine.getDriverApi().isWorkaroundNeeded(Workaround::METAL_STATIC_TEXTURE_TARGET_ERROR);
|
||||
engine.getDriverApi().isWorkaroundNeeded(Workaround::A8X_STATIC_TEXTURE_TARGET_ERROR);
|
||||
|
||||
bool const powerVrShaderWorkarounds =
|
||||
engine.getDriverApi().isWorkaroundNeeded(Workaround::POWER_VR_SHADER_WORKAROUNDS);
|
||||
|
||||
@@ -51,7 +51,6 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material)
|
||||
mUniforms = UniformBuffer(material->getUniformInterfaceBlock().getSize());
|
||||
mUbHandle = driver.createBufferObject(mUniforms.getSize(),
|
||||
BufferObjectBinding::UNIFORM, backend::BufferUsage::STATIC);
|
||||
driver.setDebugTag(mUbHandle.getId(), material->getName());
|
||||
}
|
||||
|
||||
if (!material->getSamplerInterfaceBlock().isEmpty()) {
|
||||
@@ -117,7 +116,6 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
|
||||
mUniforms.setUniforms(other->getUniformBuffer());
|
||||
mUbHandle = driver.createBufferObject(mUniforms.getSize(),
|
||||
BufferObjectBinding::UNIFORM, backend::BufferUsage::DYNAMIC);
|
||||
driver.setDebugTag(mUbHandle.getId(), material->getName());
|
||||
}
|
||||
|
||||
if (!material->getSamplerInterfaceBlock().isEmpty()) {
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
#include <math/mat4.h>
|
||||
#include <math/norm.h>
|
||||
|
||||
#include <utils/CString.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
using namespace backend;
|
||||
@@ -126,11 +124,6 @@ FMorphTargetBuffer::FMorphTargetBuffer(FEngine& engine, const Builder& builder)
|
||||
mCount,
|
||||
TextureUsage::DEFAULT);
|
||||
|
||||
if (auto name = builder.getName(); !name.empty()) {
|
||||
driver.setDebugTag(mPbHandle.getId(), name);
|
||||
driver.setDebugTag(mTbHandle.getId(), std::move(name));
|
||||
}
|
||||
|
||||
// create and update sampler group
|
||||
mSbHandle = driver.createSamplerGroup(PerRenderPrimitiveMorphingSib::SAMPLER_COUNT,
|
||||
utils::FixedSizeString<32>("Morph target samplers"));
|
||||
|
||||
@@ -60,7 +60,6 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <stddef.h>
|
||||
@@ -89,11 +88,7 @@ FRenderer::FRenderer(FEngine& engine) :
|
||||
mHdrQualityMedium(TextureFormat::R11F_G11F_B10F),
|
||||
mHdrQualityHigh(TextureFormat::RGB16F),
|
||||
mIsRGB8Supported(false),
|
||||
mUserEpoch(engine.getEngineEpoch()),
|
||||
mResourceAllocator(std::make_unique<ResourceAllocator>(
|
||||
engine.getSharedResourceAllocatorDisposer(),
|
||||
engine.getConfig(),
|
||||
engine.getDriverApi()))
|
||||
mUserEpoch(engine.getEngineEpoch())
|
||||
{
|
||||
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
|
||||
debugRegistry.registerProperty("d.renderer.doFrameCapture",
|
||||
@@ -181,7 +176,6 @@ void FRenderer::terminate(FEngine& engine) {
|
||||
}
|
||||
mFrameInfoManager.terminate(driver);
|
||||
mFrameSkipper.terminate(driver);
|
||||
mResourceAllocator->terminate();
|
||||
}
|
||||
|
||||
void FRenderer::resetUserTime() {
|
||||
@@ -242,39 +236,6 @@ void FRenderer::setVsyncTime(uint64_t steadyClockTimeNano) noexcept {
|
||||
mVsyncSteadyClockTimeNano = steadyClockTimeNano;
|
||||
}
|
||||
|
||||
void FRenderer::skipFrame(uint64_t vsyncSteadyClockTimeNano) {
|
||||
SYSTRACE_CALL();
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!mSwapChain) <<
|
||||
"skipFrame() can't be called between beginFrame() and endFrame()";
|
||||
|
||||
if (!vsyncSteadyClockTimeNano) {
|
||||
vsyncSteadyClockTimeNano = mVsyncSteadyClockTimeNano;
|
||||
mVsyncSteadyClockTimeNano = 0;
|
||||
}
|
||||
|
||||
FEngine& engine = mEngine;
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
|
||||
// Gives the backend a chance to execute periodic tasks. This must be called before
|
||||
// the frame skipper.
|
||||
driver.tick();
|
||||
|
||||
// do this before engine.flush()
|
||||
mResourceAllocator->gc(true);
|
||||
|
||||
// Run the component managers' GC in parallel
|
||||
// WARNING: while doing this we can't access any component manager
|
||||
auto& js = engine.getJobSystem();
|
||||
|
||||
auto *job = js.runAndRetain(jobs::createJob(js, nullptr, &FEngine::gc, &engine)); // gc all managers
|
||||
|
||||
engine.flush(); // flush command stream
|
||||
|
||||
// make sure we're done with the gcs
|
||||
js.waitAndRelease(job);
|
||||
}
|
||||
|
||||
bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeNano) {
|
||||
assert_invariant(swapChain);
|
||||
|
||||
@@ -344,14 +305,10 @@ bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeN
|
||||
* to ignore the return value and render the frame anyway -- which is perfectly fine.
|
||||
* The remaining work will be done when the first render() call is made.
|
||||
*/
|
||||
auto beginFrameInternal = [this, appVsync, swapChain]() {
|
||||
auto beginFrameInternal = [this, appVsync]() {
|
||||
FEngine& engine = mEngine;
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
|
||||
// we need to re-set mSwapChain here because if a frame was marked as "skip" but the
|
||||
// user ignored us, we'd still want mSwapChain to be set.
|
||||
mSwapChain = swapChain;
|
||||
|
||||
driver.beginFrame(
|
||||
appVsync.time_since_epoch().count(),
|
||||
mDisplayInfo.refreshRate == 0.0 ? 0 : int64_t(
|
||||
@@ -380,10 +337,6 @@ bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeN
|
||||
// so we need to delay this work until that happens.
|
||||
mBeginFrameInternal = beginFrameInternal;
|
||||
|
||||
// clear mSwapChain because the frame should be skipped (it will be re-set if the user
|
||||
// ignores the skip)
|
||||
mSwapChain = nullptr;
|
||||
|
||||
// we need to flush in this case, to make sure the tick() call is executed at some point
|
||||
engine.flush();
|
||||
|
||||
@@ -427,7 +380,7 @@ void FRenderer::endFrame() {
|
||||
}
|
||||
|
||||
// do this before engine.flush()
|
||||
mResourceAllocator->gc();
|
||||
engine.getResourceAllocator().gc();
|
||||
|
||||
// Run the component managers' GC in parallel
|
||||
// WARNING: while doing this we can't access any component manager
|
||||
@@ -515,9 +468,6 @@ void FRenderer::renderStandaloneView(FView const* view) {
|
||||
FILAMENT_CHECK_PRECONDITION(view->getRenderTarget())
|
||||
<< "View \"" << view->getName() << "\" must have a RenderTarget associated";
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!mSwapChain)
|
||||
<< "renderStandaloneView() must be called outside of beginFrame() / endFrame()";
|
||||
|
||||
if (UTILS_LIKELY(view->getScene())) {
|
||||
mPreviousRenderTargets.clear();
|
||||
mFrameId++;
|
||||
@@ -542,6 +492,8 @@ void FRenderer::renderStandaloneView(FView const* view) {
|
||||
void FRenderer::render(FView const* view) {
|
||||
SYSTRACE_CALL();
|
||||
|
||||
assert_invariant(mSwapChain);
|
||||
|
||||
if (UTILS_UNLIKELY(mBeginFrameInternal)) {
|
||||
// this should not happen, the user should not call render() if we returned false from
|
||||
// beginFrame(). But because this is allowed, we handle it gracefully.
|
||||
@@ -549,9 +501,6 @@ void FRenderer::render(FView const* view) {
|
||||
mBeginFrameInternal = {};
|
||||
}
|
||||
|
||||
// after beginFrame() is called, mSwapChain should be true
|
||||
assert_invariant(mSwapChain);
|
||||
|
||||
if (UTILS_LIKELY(view && view->getScene() && view->hasCamera())) {
|
||||
if (mViewRenderedCount) {
|
||||
// This is a good place to kick the GPU, since we've rendered a View before,
|
||||
@@ -798,7 +747,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
* Frame graph
|
||||
*/
|
||||
|
||||
FrameGraph fg(*mResourceAllocator,
|
||||
FrameGraph fg(engine.getResourceAllocator(),
|
||||
isProtectedContent ? FrameGraph::Mode::PROTECTED : FrameGraph::Mode::UNPROTECTED);
|
||||
auto& blackboard = fg.getBlackboard();
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Allocator.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <math/vec4.h>
|
||||
|
||||
@@ -46,7 +45,6 @@
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <stddef.h>
|
||||
@@ -54,8 +52,6 @@
|
||||
|
||||
namespace filament {
|
||||
|
||||
class ResourceAllocator;
|
||||
|
||||
namespace backend {
|
||||
class Driver;
|
||||
} // namespace backend
|
||||
@@ -91,9 +87,6 @@ public:
|
||||
|
||||
void setVsyncTime(uint64_t steadyClockTimeNano) noexcept;
|
||||
|
||||
// skip a frame
|
||||
void skipFrame(uint64_t vsyncSteadyClockTimeNano);
|
||||
|
||||
// start a frame
|
||||
bool beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeNano);
|
||||
|
||||
@@ -147,14 +140,6 @@ public:
|
||||
return mClearOptions;
|
||||
}
|
||||
|
||||
utils::FixedCapacityVector<Renderer::FrameInfo> getFrameInfoHistory(size_t historySize) const noexcept {
|
||||
return mFrameInfoManager.getFrameInfoHistory(historySize);
|
||||
}
|
||||
|
||||
size_t getMaxFrameHistorySize() const noexcept {
|
||||
return MAX_FRAMETIME_HISTORY;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Renderer;
|
||||
using Command = RenderPass::Command;
|
||||
@@ -212,7 +197,6 @@ private:
|
||||
tsl::robin_set<FRenderTarget*> mPreviousRenderTargets;
|
||||
std::function<void()> mBeginFrameInternal;
|
||||
uint64_t mVsyncSteadyClockTimeNano = 0;
|
||||
std::unique_ptr<ResourceAllocator> mResourceAllocator{};
|
||||
};
|
||||
|
||||
FILAMENT_DOWNCAST(Renderer)
|
||||
|
||||
@@ -248,7 +248,7 @@ void FScene::prepare(utils::JobSystem& js,
|
||||
|
||||
auto* renderableJob = jobs::parallel_for(js, rootJob,
|
||||
renderableInstances.data(), renderableInstances.size(),
|
||||
std::cref(renderableWork), jobs::CountSplitter<64>());
|
||||
std::cref(renderableWork), jobs::CountSplitter<128, 5>());
|
||||
|
||||
auto* lightJob = jobs::parallel_for(js, rootJob,
|
||||
lightInstances.data(), lightInstances.size(),
|
||||
|
||||
@@ -27,8 +27,6 @@
|
||||
#include <math/half.h>
|
||||
#include <math/mat4.h>
|
||||
|
||||
#include <utils/CString.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace filament {
|
||||
@@ -82,11 +80,6 @@ FSkinningBuffer::FSkinningBuffer(FEngine& engine, const Builder& builder)
|
||||
BufferObjectBinding::UNIFORM,
|
||||
BufferUsage::DYNAMIC);
|
||||
|
||||
if (auto name = builder.getName(); !name.empty()) {
|
||||
// TODO: We should also tag the texture created inside createIndicesAndWeightsHandle.
|
||||
driver.setDebugTag(mHandle.getId(), std::move(name));
|
||||
}
|
||||
|
||||
if (builder->mInitialize) {
|
||||
// initialize the bones to identity (before rounding up)
|
||||
auto* out = driver.allocatePod<PerRenderableBoneUib::BoneData>(mBoneCount);
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
|
||||
#include <backend/PixelBufferDescriptor.h>
|
||||
|
||||
#include <utils/CString.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <filament/Stream.h>
|
||||
|
||||
|
||||
namespace filament {
|
||||
|
||||
using namespace backend;
|
||||
@@ -80,10 +80,6 @@ FStream::FStream(FEngine& engine, const Builder& builder) noexcept
|
||||
} else {
|
||||
mStreamHandle = engine.getDriverApi().createStreamAcquired();
|
||||
}
|
||||
|
||||
if (auto name = builder.getName(); !name.empty()) {
|
||||
engine.getDriverApi().setDebugTag(mStreamHandle.getId(), std::move(name));
|
||||
}
|
||||
}
|
||||
|
||||
void FStream::terminate(FEngine& engine) noexcept {
|
||||
|
||||
@@ -243,9 +243,6 @@ FTexture::FTexture(FEngine& engine, const Builder& builder) {
|
||||
mHandle = driver.importTexture(builder->mImportedId,
|
||||
mTarget, mLevelCount, mFormat, mSampleCount, mWidth, mHeight, mDepth, mUsage);
|
||||
}
|
||||
if (auto name = builder.getName(); !name.empty()) {
|
||||
driver.setDebugTag(mHandle.getId(), std::move(name));
|
||||
}
|
||||
}
|
||||
|
||||
// frees driver resources, object becomes invalid
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
|
||||
#include <utils/bitset.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
@@ -260,9 +259,7 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build
|
||||
mBufferCount, mDeclaredAttributes.count(), mAttributes);
|
||||
|
||||
mHandle = driver.createVertexBuffer(mVertexCount, mVertexBufferInfoHandle);
|
||||
if (auto name = builder.getName(); !name.empty()) {
|
||||
driver.setDebugTag(mHandle.getId(), name);
|
||||
}
|
||||
|
||||
|
||||
// calculate buffer sizes
|
||||
size_t bufferSizes[MAX_VERTEX_BUFFER_COUNT] = {};
|
||||
@@ -290,9 +287,6 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build
|
||||
if (!mBufferObjects[i]) {
|
||||
BufferObjectHandle bo = driver.createBufferObject(bufferSizes[i],
|
||||
backend::BufferObjectBinding::VERTEX, backend::BufferUsage::STATIC);
|
||||
if (auto name = builder.getName(); !name.empty()) {
|
||||
driver.setDebugTag(bo.getId(), name);
|
||||
}
|
||||
driver.setVertexBufferObject(mHandle, i, bo);
|
||||
mBufferObjects[i] = bo;
|
||||
}
|
||||
@@ -309,9 +303,6 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build
|
||||
if (!mBufferObjects[i]) {
|
||||
BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i],
|
||||
backend::BufferObjectBinding::VERTEX, backend::BufferUsage::STATIC);
|
||||
if (auto name = builder.getName(); !name.empty()) {
|
||||
driver.setDebugTag(bo.getId(), name);
|
||||
}
|
||||
driver.setVertexBufferObject(mHandle, i, bo);
|
||||
mBufferObjects[i] = bo;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "details/View.h"
|
||||
|
||||
#include "Culler.h"
|
||||
#include "FrameHistory.h"
|
||||
#include "Froxelizer.h"
|
||||
#include "RenderPrimitive.h"
|
||||
#include "ResourceAllocator.h"
|
||||
@@ -31,17 +30,15 @@
|
||||
#include "details/Skybox.h"
|
||||
|
||||
#include <filament/Exposure.h>
|
||||
#include <filament/DebugRegistry.h>
|
||||
#include <filament/TextureSampler.h>
|
||||
#include <filament/View.h>
|
||||
|
||||
#include <private/filament/UibStructs.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Profiler.h>
|
||||
#include <utils/Slice.h>
|
||||
#include <utils/Systrace.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Zip2Iterator.h>
|
||||
|
||||
#include <math/scalar.h>
|
||||
@@ -49,7 +46,6 @@
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
|
||||
using namespace utils;
|
||||
|
||||
@@ -81,29 +77,21 @@ FView::FView(FEngine& engine)
|
||||
mPidController.setOutputDeadBand(-0.01f, 0.05f);
|
||||
|
||||
#ifndef NDEBUG
|
||||
// This can fail if another view has already registered this data source
|
||||
mDebugState->owner = debugRegistry.registerDataSource("d.view.frame_info",
|
||||
[weak = std::weak_ptr<DebugState>(mDebugState)]() -> DebugRegistry::DataSource {
|
||||
// the View could have been destroyed by the time we do this
|
||||
auto const state = weak.lock();
|
||||
if (!state) {
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
// Lazily allocate the buffer for the debug data source, and mark this
|
||||
// data source as active. It can never go back to inactive.
|
||||
assert_invariant(!state->debugFrameHistory);
|
||||
state->active = true;
|
||||
state->debugFrameHistory =
|
||||
std::make_unique<std::array<DebugRegistry::FrameHistory, 5 * 60>>();
|
||||
return { state->debugFrameHistory->data(), state->debugFrameHistory->size() };
|
||||
debugRegistry.registerDataSource("d.view.frame_info",
|
||||
[this]() -> DebugRegistry::DataSource {
|
||||
assert_invariant(!mDebugFrameHistory);
|
||||
mDebugFrameHistory = std::make_unique<std::array<DebugRegistry::FrameHistory, 5*60>>();
|
||||
return { mDebugFrameHistory->data(), mDebugFrameHistory->size() };
|
||||
});
|
||||
|
||||
if (UTILS_UNLIKELY(mDebugState->owner)) {
|
||||
// publish the properties (they will be initialized in the main loop)
|
||||
debugRegistry.registerProperty("d.view.pid.kp", &engine.debug.view.pid.kp);
|
||||
debugRegistry.registerProperty("d.view.pid.ki", &engine.debug.view.pid.ki);
|
||||
debugRegistry.registerProperty("d.view.pid.kd", &engine.debug.view.pid.kd);
|
||||
}
|
||||
debugRegistry.registerProperty("d.view.pid.kp", &engine.debug.view.pid.kp);
|
||||
debugRegistry.registerProperty("d.view.pid.ki", &engine.debug.view.pid.ki);
|
||||
debugRegistry.registerProperty("d.view.pid.kd", &engine.debug.view.pid.kd);
|
||||
// default parameters for debugging UI
|
||||
engine.debug.view.pid.kp = 1.0f - std::exp(-1.0f / 8.0f);
|
||||
engine.debug.view.pid.ki = PID_CONTROLLER_Ki;
|
||||
engine.debug.view.pid.kd = PID_CONTROLLER_Kd;
|
||||
mPidController.setParallelGains(
|
||||
engine.debug.view.pid.kp, engine.debug.view.pid.ki, engine.debug.view.pid.kd);
|
||||
#endif
|
||||
|
||||
// allocate UBOs
|
||||
@@ -130,19 +118,13 @@ void FView::terminate(FEngine& engine) {
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
driver.destroyBufferObject(mLightUbh);
|
||||
driver.destroyBufferObject(mRenderableUbh);
|
||||
clearFrameHistory(engine);
|
||||
drainFrameHistory(engine);
|
||||
|
||||
ShadowMapManager::terminate(engine, mShadowMapManager);
|
||||
mPerViewUniforms.terminate(driver);
|
||||
mFroxelizer.terminate(driver);
|
||||
|
||||
engine.getEntityManager().destroy(mFogEntity);
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (UTILS_UNLIKELY(mDebugState->owner)) {
|
||||
engine.getDebugRegistry().unregisterDataSource("d.view.frame_info");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void FView::setViewport(filament::Viewport const& viewport) noexcept {
|
||||
@@ -180,20 +162,10 @@ void FView::setDynamicLightingOptions(float zLightNear, float zLightFar) noexcep
|
||||
}
|
||||
|
||||
float2 FView::updateScale(FEngine& engine,
|
||||
filament::details::FrameInfo const& info,
|
||||
FrameInfo const& info,
|
||||
Renderer::FrameRateOptions const& frameRateOptions,
|
||||
Renderer::DisplayInfo const& displayInfo) noexcept {
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (UTILS_LIKELY(!mDebugState->active)) {
|
||||
// if we're not active, update the debug properties with the normal values
|
||||
// and use that for configuring the PID controller.
|
||||
engine.debug.view.pid.kp = 1.0f - std::exp(-frameRateOptions.scaleRate);
|
||||
engine.debug.view.pid.ki = PID_CONTROLLER_Ki;
|
||||
engine.debug.view.pid.kd = PID_CONTROLLER_Kd;
|
||||
}
|
||||
#endif
|
||||
|
||||
DynamicResolutionOptions const& options = mDynamicResolution;
|
||||
if (options.enabled) {
|
||||
if (!UTILS_UNLIKELY(info.valid)) {
|
||||
@@ -277,15 +249,14 @@ float2 FView::updateScale(FEngine& engine,
|
||||
|
||||
#ifndef NDEBUG
|
||||
// only for debugging...
|
||||
if (UTILS_UNLIKELY(mDebugState->active && mDebugState->debugFrameHistory)) {
|
||||
auto* const debugFrameHistory = mDebugState->debugFrameHistory.get();
|
||||
if (mDebugFrameHistory) {
|
||||
using namespace std::chrono;
|
||||
using duration_ms = duration<float, std::milli>;
|
||||
const float target = (1000.0f * float(frameRateOptions.interval)) / displayInfo.refreshRate;
|
||||
const float targetWithHeadroom = target * (1.0f - frameRateOptions.headRoomRatio);
|
||||
std::move(debugFrameHistory->begin() + 1,
|
||||
debugFrameHistory->end(), debugFrameHistory->begin());
|
||||
debugFrameHistory->back() = {
|
||||
std::move(mDebugFrameHistory->begin() + 1,
|
||||
mDebugFrameHistory->end(), mDebugFrameHistory->begin());
|
||||
mDebugFrameHistory->back() = {
|
||||
.target = target,
|
||||
.targetWithHeadroom = targetWithHeadroom,
|
||||
.frameTime = duration_cast<duration_ms>(info.frameTime).count(),
|
||||
@@ -1036,29 +1007,20 @@ FrameGraphId<FrameGraphTexture> FView::renderShadowMaps(FEngine& engine, FrameGr
|
||||
|
||||
void FView::commitFrameHistory(FEngine& engine) noexcept {
|
||||
// Here we need to destroy resources in mFrameHistory.back()
|
||||
auto& disposer = engine.getResourceAllocatorDisposer();
|
||||
auto& frameHistory = mFrameHistory;
|
||||
|
||||
FrameHistoryEntry& last = frameHistory.back();
|
||||
disposer.destroy(last.taa.color.handle);
|
||||
disposer.destroy(last.ssr.color.handle);
|
||||
last.taa.color.handle.clear();
|
||||
last.ssr.color.handle.clear();
|
||||
last.taa.color.destroy(engine.getResourceAllocator());
|
||||
last.ssr.color.destroy(engine.getResourceAllocator());
|
||||
|
||||
// and then push the new history entry to the history stack
|
||||
frameHistory.commit();
|
||||
}
|
||||
|
||||
void FView::clearFrameHistory(FEngine& engine) noexcept {
|
||||
void FView::drainFrameHistory(FEngine& engine) noexcept {
|
||||
// make sure we free all resources in the history
|
||||
auto& disposer = engine.getResourceAllocatorDisposer();
|
||||
auto& frameHistory = mFrameHistory;
|
||||
for (size_t i = 0; i < frameHistory.size(); ++i) {
|
||||
FrameHistoryEntry& last = frameHistory[i];
|
||||
disposer.destroy(last.taa.color.handle);
|
||||
disposer.destroy(last.ssr.color.handle);
|
||||
last.taa.color.handle.clear();
|
||||
last.ssr.color.handle.clear();
|
||||
for (size_t i = 0; i < mFrameHistory.size(); ++i) {
|
||||
commitFrameHistory(engine);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -201,9 +201,9 @@ public:
|
||||
|
||||
void setStereoscopicOptions(StereoscopicOptions const& options) noexcept;
|
||||
|
||||
utils::FixedCapacityVector<Camera const*> getDirectionalShadowCameras() const noexcept {
|
||||
if (!mShadowMapManager) return {};
|
||||
return mShadowMapManager->getDirectionalShadowCameras();
|
||||
FCamera const* getDirectionalShadowCamera() const noexcept {
|
||||
if (!mShadowMapManager) return nullptr;
|
||||
return mShadowMapManager->getDirectionalShadowCamera();
|
||||
}
|
||||
|
||||
void setRenderTarget(FRenderTarget* renderTarget) noexcept {
|
||||
@@ -283,7 +283,7 @@ public:
|
||||
}
|
||||
|
||||
math::float2 updateScale(FEngine& engine,
|
||||
details::FrameInfo const& info,
|
||||
FrameInfo const& info,
|
||||
Renderer::FrameRateOptions const& frameRateOptions,
|
||||
Renderer::DisplayInfo const& displayInfo) noexcept;
|
||||
|
||||
@@ -422,10 +422,6 @@ public:
|
||||
// (e.g.: after the FrameGraph execution).
|
||||
void commitFrameHistory(FEngine& engine) noexcept;
|
||||
|
||||
// Clean-up the whole history, free all resources. This is typically called when the View is
|
||||
// being terminated. Or we're changing Renderer.
|
||||
void clearFrameHistory(FEngine& engine) noexcept;
|
||||
|
||||
// create the picking query
|
||||
View::PickingQuery& pick(uint32_t x, uint32_t y, backend::CallbackHandler* handler,
|
||||
View::PickingQueryResultCallback callback) noexcept;
|
||||
@@ -489,6 +485,10 @@ private:
|
||||
Culler::result_type* visibleMask,
|
||||
size_t count);
|
||||
|
||||
// Clean-up the whole history, free all resources. This is typically called when the View is
|
||||
// being terminated.
|
||||
void drainFrameHistory(FEngine& engine) noexcept;
|
||||
|
||||
// we don't inline this one, because the function is quite large and there is not much to
|
||||
// gain from inlining.
|
||||
static FScene::RenderableSoa::iterator partition(
|
||||
@@ -576,12 +576,7 @@ private:
|
||||
}};
|
||||
|
||||
#ifndef NDEBUG
|
||||
struct DebugState {
|
||||
std::unique_ptr<std::array<DebugRegistry::FrameHistory, 5*60>> debugFrameHistory{};
|
||||
bool owner = false;
|
||||
bool active = false;
|
||||
};
|
||||
std::shared_ptr<DebugState> mDebugState{ new DebugState };
|
||||
std::unique_ptr<std::array<DebugRegistry::FrameHistory, 5*60>> mDebugFrameHistory;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user