Compare commits
686
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
563c32b95b | ||
|
|
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 |
@@ -205,7 +205,7 @@ jobs:
|
||||
TAG: ${{ steps.git_ref.outputs.tag }}
|
||||
run: |
|
||||
build\windows\build-github.bat release
|
||||
move out\filament-windows.tgz out\filament-%TAG%-windows.tgz
|
||||
move out\filament-windows.tgz out\filament-$Env:TAG-windows.tgz
|
||||
shell: cmd
|
||||
- uses: actions/github-script@v6
|
||||
env:
|
||||
|
||||
@@ -45,8 +45,6 @@ option(FILAMENT_ENABLE_TSAN "Enable Thread Sanitizer" OFF)
|
||||
|
||||
option(FILAMENT_ENABLE_FEATURE_LEVEL_0 "Enable Feature Level 0" ON)
|
||||
|
||||
option(FILAMENT_ENABLE_MULTIVIEW "Enable multiview for Filament" OFF)
|
||||
|
||||
set(FILAMENT_NDK_VERSION "" CACHE STRING
|
||||
"Android NDK version or version prefix to be used when building for Android."
|
||||
)
|
||||
@@ -533,21 +531,6 @@ else()
|
||||
option(FILAMENT_DISABLE_MATOPT "Disable material optimizations" ON)
|
||||
endif()
|
||||
|
||||
# This only affects the prebuilt shader files in gltfio and samples, not filament library.
|
||||
# The value can be either "instanced" or "multiview".
|
||||
set(FILAMENT_SAMPLES_STEREO_TYPE "instanced" CACHE STRING
|
||||
"Stereoscopic type that shader files in gltfio and samples are built for."
|
||||
)
|
||||
string(TOLOWER "${FILAMENT_SAMPLES_STEREO_TYPE}" FILAMENT_SAMPLES_STEREO_TYPE)
|
||||
if (NOT FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "instanced" AND NOT FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "multiview")
|
||||
message(FATAL_ERROR "Invalid stereo type: \"${FILAMENT_SAMPLES_STEREO_TYPE}\" choose either \"instanced\" or \"multiview\" ")
|
||||
endif ()
|
||||
|
||||
# Compiling samples for multiview implies enabling multiview feature as well.
|
||||
if (FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "multiview")
|
||||
set(FILAMENT_ENABLE_MULTIVIEW ON)
|
||||
endif ()
|
||||
|
||||
# ==================================================================================================
|
||||
# Material compilation flags
|
||||
# ==================================================================================================
|
||||
|
||||
@@ -7,5 +7,3 @@ for next branch cut* header.
|
||||
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
||||
|
||||
## Release notes for next branch cut
|
||||
|
||||
- engine: Add experimental APIs `Engine::builder::paused()` and `Engine::setPaused()`
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.51.0'
|
||||
implementation 'com.google.android.filament:filament-android:1.50.4'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
||||
iOS projects can use CocoaPods to install the latest release:
|
||||
|
||||
```shell
|
||||
pod 'Filament', '~> 1.51.0'
|
||||
pod 'Filament', '~> 1.50.4'
|
||||
```
|
||||
|
||||
### 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.51.1
|
||||
|
||||
|
||||
## v1.51.0
|
||||
|
||||
- materials: add support for post-lighting mix factor (b/328498606) [⚠️ **New Material Version**]
|
||||
|
||||
## v1.50.6
|
||||
|
||||
- Add new API `SwapChain::getFrameScheduledCallback`
|
||||
- vulkan: fixed validation error VUID-vkAcquireNextImageKHR-semaphore-01779
|
||||
- opengl: Add support for protected content swapchains and contexts
|
||||
|
||||
## v1.50.5
|
||||
|
||||
- android: NDK 26.1.10909125 is used by default
|
||||
- android: Minimum API level on Android is now API 21 instead of API 19. This allows the use of OpenGL ES 3.1
|
||||
- rendering: New PBR Neutral tone mapper, designed to preserve materials color appearance
|
||||
- android: Change default frameRateOptions.interval to 1.0
|
||||
|
||||
## v1.50.4
|
||||
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ buildscript {
|
||||
|
||||
ext.versions = [
|
||||
'jdk': 17,
|
||||
'minSdk': 21,
|
||||
'minSdk': 19,
|
||||
'targetSdk': 34,
|
||||
'compileSdk': 34,
|
||||
'kotlin': '1.9.21',
|
||||
|
||||
@@ -391,13 +391,6 @@ Java_com_google_android_filament_Engine_nFlush(JNIEnv*, jclass,
|
||||
engine->flush();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Engine_nSetPaused(JNIEnv*, jclass,
|
||||
jlong nativeEngine, jboolean paused) {
|
||||
Engine* engine = (Engine*) nativeEngine;
|
||||
engine->setPaused(paused);
|
||||
}
|
||||
|
||||
// Managers...
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
@@ -494,8 +487,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
|
||||
jlong jobSystemThreadCount,
|
||||
jlong textureUseAfterFreePoolSize, jboolean disableParallelShaderCompile,
|
||||
jint stereoscopicType, jlong stereoscopicEyeCount,
|
||||
jlong resourceAllocatorCacheSizeMB, jlong resourceAllocatorCacheMaxAge,
|
||||
jboolean disableHandleUseAfterFreeCheck) {
|
||||
jlong resourceAllocatorCacheSizeMB, jlong resourceAllocatorCacheMaxAge) {
|
||||
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
|
||||
Engine::Config config = {
|
||||
.commandBufferSizeMB = (uint32_t) commandBufferSizeMB,
|
||||
@@ -510,7 +502,6 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
|
||||
.stereoscopicEyeCount = (uint8_t) stereoscopicEyeCount,
|
||||
.resourceAllocatorCacheSizeMB = (uint32_t) resourceAllocatorCacheSizeMB,
|
||||
.resourceAllocatorCacheMaxAge = (uint8_t) resourceAllocatorCacheMaxAge,
|
||||
.disableHandleUseAfterFreeCheck = (bool) disableHandleUseAfterFreeCheck,
|
||||
};
|
||||
builder->config(&config);
|
||||
}
|
||||
@@ -527,12 +518,6 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
|
||||
builder->sharedContext((void*) sharedContext);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderPaused(
|
||||
JNIEnv*, jclass, jlong nativeBuilder, jboolean paused) {
|
||||
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
|
||||
builder->paused((bool) paused);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_Engine_nBuilderBuild(JNIEnv*, jclass, jlong nativeBuilder) {
|
||||
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
|
||||
|
||||
@@ -104,14 +104,6 @@ Java_com_google_android_filament_RenderableManager_nBuilderGeometry__JIIJJIIII(J
|
||||
(size_t) count);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_RenderableManager_nBuilderGeometryType(JNIEnv*, jclass,
|
||||
jlong nativeBuilder, int type) {
|
||||
RenderableManager::Builder *builder = (RenderableManager::Builder *) nativeBuilder;
|
||||
builder->geometryType((RenderableManager::Builder::GeometryType)type);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_RenderableManager_nBuilderMaterial(JNIEnv*, jclass,
|
||||
|
||||
@@ -34,15 +34,7 @@ Java_com_google_android_filament_SwapChain_nSetFrameCompletedCallback(JNIEnv* en
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_SwapChain_nIsSRGBSwapChainSupported(
|
||||
JNIEnv *, jclass, jlong nativeEngine) {
|
||||
Java_com_google_android_filament_SwapChain_nIsSRGBSwapChainSupported(JNIEnv *, jclass, jlong nativeEngine) {
|
||||
Engine* engine = (Engine*) nativeEngine;
|
||||
return (jboolean)SwapChain::isSRGBSwapChainSupported(*engine);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_SwapChain_nIsProtectedContentSupported(
|
||||
JNIEnv *, jclass, jlong nativeEngine) {
|
||||
Engine* engine = (Engine*) nativeEngine;
|
||||
return (jboolean)SwapChain::isProtectedContentSupported(*engine);
|
||||
return (bool)SwapChain::isSRGBSwapChainSupported(*engine);
|
||||
}
|
||||
|
||||
@@ -47,11 +47,6 @@ Java_com_google_android_filament_ToneMapper_nCreateFilmicToneMapper(JNIEnv*, jcl
|
||||
return (jlong) new FilmicToneMapper();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_ToneMapper_nCreatePBRNeutralToneMapper(JNIEnv*, jclass) {
|
||||
return (jlong) new PBRNeutralToneMapper();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_ToneMapper_nCreateAgxToneMapper(JNIEnv*, jclass, jint look) {
|
||||
return (jlong) new AgxToneMapper(AgxToneMapper::AgxLook(look));
|
||||
|
||||
@@ -224,8 +224,7 @@ public class Engine {
|
||||
config.jobSystemThreadCount,
|
||||
config.textureUseAfterFreePoolSize, config.disableParallelShaderCompile,
|
||||
config.stereoscopicType.ordinal(), config.stereoscopicEyeCount,
|
||||
config.resourceAllocatorCacheSizeMB, config.resourceAllocatorCacheMaxAge,
|
||||
config.disableHandleUseAfterFreeCheck);
|
||||
config.resourceAllocatorCacheSizeMB, config.resourceAllocatorCacheMaxAge);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -240,20 +239,6 @@ public class Engine {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial paused state of the rendering thread.
|
||||
*
|
||||
* <p>Warning: This is an experimental API. See {@link Engine#setPaused(boolean)} for
|
||||
* caveats.
|
||||
*
|
||||
* @param paused Whether to start the rendering thread paused.
|
||||
* @return A reference to this Builder for chaining calls.
|
||||
*/
|
||||
public Builder paused(boolean paused) {
|
||||
nSetBuilderPaused(mNativeBuilder, paused);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of Engine
|
||||
*
|
||||
@@ -415,19 +400,16 @@ public class Engine {
|
||||
public long stereoscopicEyeCount = 2;
|
||||
|
||||
/*
|
||||
* @Deprecated This value is no longer used.
|
||||
* Size in MiB of the frame graph texture cache. This should be adjusted based on the
|
||||
* size of used render targets (typically the screen).
|
||||
*/
|
||||
public long resourceAllocatorCacheSizeMB = 64;
|
||||
|
||||
/*
|
||||
* This value determines for how many frames are texture entries kept in the cache.
|
||||
* The default value of 30 corresponds to about half a second at 60 fps.
|
||||
*/
|
||||
public long resourceAllocatorCacheMaxAge = 2;
|
||||
|
||||
/*
|
||||
* Disable backend handles use-after-free checks.
|
||||
*/
|
||||
public boolean disableHandleUseAfterFreeCheck = false;
|
||||
public long resourceAllocatorCacheMaxAge = 30;
|
||||
}
|
||||
|
||||
private Engine(long nativeEngine, Config config) {
|
||||
@@ -674,7 +656,7 @@ public class Engine {
|
||||
*/
|
||||
@NonNull
|
||||
public SwapChain createSwapChain(@NonNull Object surface) {
|
||||
return createSwapChain(surface, SwapChainFlags.CONFIG_DEFAULT);
|
||||
return createSwapChain(surface, SwapChain.CONFIG_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -682,15 +664,15 @@ public class Engine {
|
||||
*
|
||||
* @param surface on Android, <b>must be</b> an instance of {@link android.view.Surface}
|
||||
*
|
||||
* @param flags configuration flags, see {@link SwapChainFlags}
|
||||
* @param flags configuration flags, see {@link SwapChain}
|
||||
*
|
||||
* @return a newly created {@link SwapChain} object
|
||||
*
|
||||
* @exception IllegalStateException can be thrown if the SwapChain couldn't be created
|
||||
*
|
||||
* @see SwapChainFlags#CONFIG_DEFAULT
|
||||
* @see SwapChainFlags#CONFIG_TRANSPARENT
|
||||
* @see SwapChainFlags#CONFIG_READABLE
|
||||
* @see SwapChain#CONFIG_DEFAULT
|
||||
* @see SwapChain#CONFIG_TRANSPARENT
|
||||
* @see SwapChain#CONFIG_READABLE
|
||||
*
|
||||
*/
|
||||
@NonNull
|
||||
@@ -708,22 +690,21 @@ public class Engine {
|
||||
*
|
||||
* @param width width of the rendering buffer
|
||||
* @param height height of the rendering buffer
|
||||
* @param flags configuration flags, see {@link SwapChainFlags}
|
||||
* @param flags configuration flags, see {@link SwapChain}
|
||||
*
|
||||
* @return a newly created {@link SwapChain} object
|
||||
*
|
||||
* @exception IllegalStateException can be thrown if the SwapChain couldn't be created
|
||||
*
|
||||
* @see SwapChainFlags#CONFIG_DEFAULT
|
||||
* @see SwapChainFlags#CONFIG_TRANSPARENT
|
||||
* @see SwapChainFlags#CONFIG_READABLE
|
||||
* @see SwapChain#CONFIG_DEFAULT
|
||||
* @see SwapChain#CONFIG_TRANSPARENT
|
||||
* @see SwapChain#CONFIG_READABLE
|
||||
*
|
||||
*/
|
||||
@NonNull
|
||||
public SwapChain createSwapChain(int width, int height, long flags) {
|
||||
if (width >= 0 && height >= 0) {
|
||||
long nativeSwapChain =
|
||||
nCreateSwapChainHeadless(getNativeObject(), width, height, flags);
|
||||
long nativeSwapChain = nCreateSwapChainHeadless(getNativeObject(), width, height, flags);
|
||||
if (nativeSwapChain == 0) throw new IllegalStateException("Couldn't create SwapChain");
|
||||
return new SwapChain(nativeSwapChain, null);
|
||||
}
|
||||
@@ -735,12 +716,11 @@ public class Engine {
|
||||
*
|
||||
* @param surface a properly initialized {@link NativeSurface}
|
||||
*
|
||||
* @param flags configuration flags, see {@link SwapChainFlags}
|
||||
* @param flags configuration flags, see {@link SwapChain}
|
||||
*
|
||||
* @return a newly created {@link SwapChain} object
|
||||
*
|
||||
* @exception IllegalStateException can be thrown if the {@link SwapChainFlags} couldn't be
|
||||
* created
|
||||
* @exception IllegalStateException can be thrown if the {@link SwapChain} couldn't be created
|
||||
*/
|
||||
@NonNull
|
||||
public SwapChain createSwapChainFromNativeSurface(@NonNull NativeSurface surface, long flags) {
|
||||
@@ -1209,22 +1189,6 @@ public class Engine {
|
||||
nFlush(getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause or resume the rendering thread.
|
||||
*
|
||||
* <p>Warning: This is an experimental API. In particular, note the following caveats.
|
||||
*
|
||||
* <ul><li>
|
||||
* Buffer callbacks will never be called as long as the rendering thread is paused.
|
||||
* Do not rely on a buffer callback to unpause the thread.
|
||||
* </li><li>
|
||||
* While the rendering thread is paused, rendering commands will continue to be queued until the
|
||||
* buffer limit is reached. When the limit is reached, the program will abort.
|
||||
* </li></ul>
|
||||
*/
|
||||
public void setPaused(boolean paused) {
|
||||
nSetPaused(getNativeObject(), paused);
|
||||
}
|
||||
|
||||
@UsedByReflection("TextureHelper.java")
|
||||
public long getNativeObject() {
|
||||
@@ -1299,7 +1263,6 @@ public class Engine {
|
||||
private static native void nDestroyEntity(long nativeEngine, int entity);
|
||||
private static native void nFlushAndWait(long nativeEngine);
|
||||
private static native void nFlush(long nativeEngine);
|
||||
private static native void nSetPaused(long nativeEngine, boolean paused);
|
||||
private static native long nGetTransformManager(long nativeEngine);
|
||||
private static native long nGetLightManager(long nativeEngine);
|
||||
private static native long nGetRenderableManager(long nativeEngine);
|
||||
@@ -1320,10 +1283,8 @@ public class Engine {
|
||||
long minCommandBufferSizeMB, long perFrameCommandsSizeMB, long jobSystemThreadCount,
|
||||
long textureUseAfterFreePoolSize, boolean disableParallelShaderCompile,
|
||||
int stereoscopicType, long stereoscopicEyeCount,
|
||||
long resourceAllocatorCacheSizeMB, long resourceAllocatorCacheMaxAge,
|
||||
boolean disableHandleUseAfterFreeCheck);
|
||||
long resourceAllocatorCacheSizeMB, long resourceAllocatorCacheMaxAge);
|
||||
private static native void nSetBuilderFeatureLevel(long nativeBuilder, int ordinal);
|
||||
private static native void nSetBuilderSharedContext(long nativeBuilder, long sharedContext);
|
||||
private static native void nSetBuilderPaused(long nativeBuilder, boolean paused);
|
||||
private static native long nBuilderBuild(long nativeBuilder);
|
||||
}
|
||||
|
||||
-27
@@ -175,32 +175,6 @@ public class RenderableManager {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of geometry for a Renderable
|
||||
*/
|
||||
public enum GeometryType {
|
||||
/** dynamic gemoetry has no restriction */
|
||||
DYNAMIC,
|
||||
/** bounds and world space transform are immutable */
|
||||
STATIC_BOUNDS,
|
||||
/** skinning/morphing not allowed and Vertex/IndexBuffer immutables */
|
||||
STATIC
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether this renderable has static bounds. In this context his means that
|
||||
* the renderable's bounding box cannot change and that the renderable's transform is
|
||||
* assumed immutable. Changing the renderable's transform via the TransformManager
|
||||
* can lead to corrupted graphics. Note that skinning and morphing are not forbidden.
|
||||
* Disabled by default.
|
||||
* @param enable whether this renderable has static bounds. false by default.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder geometryType(GeometryType type) {
|
||||
nBuilderGeometryType(mNativeBuilder, type.ordinal());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a material instance to the specified primitive.
|
||||
*
|
||||
@@ -990,7 +964,6 @@ public class RenderableManager {
|
||||
private static native void nBuilderGeometry(long nativeBuilder, int index, int value, long nativeVertexBuffer, long nativeIndexBuffer);
|
||||
private static native void nBuilderGeometry(long nativeBuilder, int index, int value, long nativeVertexBuffer, long nativeIndexBuffer, int offset, int count);
|
||||
private static native void nBuilderGeometry(long nativeBuilder, int index, int value, long nativeVertexBuffer, long nativeIndexBuffer, int offset, int minIndex, int maxIndex, int count);
|
||||
private static native void nBuilderGeometryType(long nativeBuilder, int type);
|
||||
private static native void nBuilderMaterial(long nativeBuilder, int index, long nativeMaterialInstance);
|
||||
private static native void nBuilderBlendOrder(long nativeBuilder, int index, int blendOrder);
|
||||
private static native void nBuilderGlobalBlendOrderEnabled(long nativeBuilder, int index, boolean enabled);
|
||||
|
||||
@@ -101,7 +101,7 @@ public class Renderer {
|
||||
/**
|
||||
* Desired frame interval in unit of 1 / DisplayInfo.refreshRate.
|
||||
*/
|
||||
public float interval = 1.0f;
|
||||
public float interval = 1.0f / 60.0f;
|
||||
|
||||
/**
|
||||
* Additional headroom for the GPU as a ratio of the targetFrameTime.
|
||||
|
||||
@@ -68,30 +68,77 @@ public class SwapChain {
|
||||
private final Object mSurface;
|
||||
private long mNativeObject;
|
||||
|
||||
public static final long CONFIG_DEFAULT = 0x0;
|
||||
|
||||
/**
|
||||
* This flag indicates that the <code>SwapChain</code> must be allocated with an
|
||||
* alpha-channel.
|
||||
*/
|
||||
public static final long CONFIG_TRANSPARENT = 0x1;
|
||||
|
||||
/**
|
||||
* This flag indicates that the <code>SwapChain</code> may be used as a source surface
|
||||
* for reading back render results. This config must be set when creating
|
||||
* any <code>SwapChain</code> that will be used as the source for a blit operation.
|
||||
*
|
||||
* @see Renderer#copyFrame
|
||||
*/
|
||||
public static final long CONFIG_READABLE = 0x2;
|
||||
|
||||
/**
|
||||
* Indicates that the native X11 window is an XCB window rather than an XLIB window.
|
||||
* This is ignored on non-Linux platforms and in builds that support only one X11 API.
|
||||
*/
|
||||
public static final long CONFIG_ENABLE_XCB = 0x4;
|
||||
|
||||
/**
|
||||
* Indicates that the SwapChain must automatically perform linear to sRGB encoding.
|
||||
*
|
||||
* This flag is ignored if isSRGBSwapChainSupported() is false.
|
||||
*
|
||||
* When using this flag, post-processing should be disabled.
|
||||
*
|
||||
* @see SwapChain#isSRGBSwapChainSupported
|
||||
* @see View#setPostProcessingEnabled
|
||||
*/
|
||||
public static final long CONFIG_SRGB_COLORSPACE = 0x10;
|
||||
|
||||
/**
|
||||
* Indicates that this SwapChain should allocate a stencil buffer in addition to a depth buffer.
|
||||
*
|
||||
* This flag is necessary when using View::setStencilBufferEnabled and rendering directly into
|
||||
* the SwapChain (when post-processing is disabled).
|
||||
*
|
||||
* The specific format of the stencil buffer depends on platform support. The following pixel
|
||||
* formats are tried, in order of preference:
|
||||
*
|
||||
* Depth only (without CONFIG_HAS_STENCIL_BUFFER):
|
||||
* - DEPTH32F
|
||||
* - DEPTH24
|
||||
*
|
||||
* Depth + stencil (with CONFIG_HAS_STENCIL_BUFFER):
|
||||
* - DEPTH32F_STENCIL8
|
||||
* - DEPTH24F_STENCIL8
|
||||
*
|
||||
* Note that enabling the stencil buffer may hinder depth precision and should only be used if
|
||||
* necessary.
|
||||
*
|
||||
* @see View#setStencilBufferEnabled
|
||||
* @see View#setPostProcessingEnabled
|
||||
*/
|
||||
public static final long CONFIG_HAS_STENCIL_BUFFER = 0x20;
|
||||
|
||||
SwapChain(long nativeSwapChain, Object surface) {
|
||||
mNativeObject = nativeSwapChain;
|
||||
mSurface = surface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether createSwapChain supports the CONFIG_PROTECTED_CONTENT flag.
|
||||
* Return whether createSwapChain supports the SWAP_CHAIN_CONFIG_SRGB_COLORSPACE flag.
|
||||
* The default implementation returns false.
|
||||
*
|
||||
* @param engine A reference to the filament Engine
|
||||
* @return true if CONFIG_PROTECTED_CONTENT is supported, false otherwise.
|
||||
* @see SwapChainFlags#CONFIG_PROTECTED_CONTENT
|
||||
*/
|
||||
public static boolean isProtectedContentSupported(@NonNull Engine engine) {
|
||||
return nIsProtectedContentSupported(engine.getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether createSwapChain supports the CONFIG_SRGB_COLORSPACE flag.
|
||||
* The default implementation returns false.
|
||||
*
|
||||
* @param engine A reference to the filament Engine
|
||||
* @return true if CONFIG_SRGB_COLORSPACE is supported, false otherwise.
|
||||
* @see SwapChainFlags#CONFIG_SRGB_COLORSPACE
|
||||
* @return true if SWAP_CHAIN_CONFIG_SRGB_COLORSPACE is supported, false otherwise.
|
||||
*/
|
||||
public static boolean isSRGBSwapChainSupported(@NonNull Engine engine) {
|
||||
return nIsSRGBSwapChainSupported(engine.getNativeObject());
|
||||
@@ -139,5 +186,4 @@ public class SwapChain {
|
||||
|
||||
private static native void nSetFrameCompletedCallback(long nativeSwapChain, Object handler, Runnable callback);
|
||||
private static native boolean nIsSRGBSwapChainSupported(long nativeEngine);
|
||||
private static native boolean nIsProtectedContentSupported(long nativeEngine);
|
||||
}
|
||||
|
||||
-97
@@ -1,97 +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.
|
||||
*/
|
||||
|
||||
package com.google.android.filament;
|
||||
|
||||
// Note: SwapChainFlags is kept separate from SwapChain so that UiHelper does not need to depend
|
||||
// on SwapChain. This allows clients to use UiHelper without requiring all of Filament's Java
|
||||
// classes.
|
||||
|
||||
/**
|
||||
* Flags that a <code>SwapChain</code> can be created with to control behavior.
|
||||
*
|
||||
* @see Engine#createSwapChain
|
||||
* @see Engine#createSwapChainFromNativeSurface
|
||||
*/
|
||||
public final class SwapChainFlags {
|
||||
|
||||
public static final long CONFIG_DEFAULT = 0x0;
|
||||
|
||||
/**
|
||||
* This flag indicates that the <code>SwapChain</code> must be allocated with an
|
||||
* alpha-channel.
|
||||
*/
|
||||
public static final long CONFIG_TRANSPARENT = 0x1;
|
||||
|
||||
/**
|
||||
* This flag indicates that the <code>SwapChain</code> may be used as a source surface
|
||||
* for reading back render results. This config must be set when creating
|
||||
* any <code>SwapChain</code> that will be used as the source for a blit operation.
|
||||
*
|
||||
* @see Renderer#copyFrame
|
||||
*/
|
||||
public static final long CONFIG_READABLE = 0x2;
|
||||
|
||||
/**
|
||||
* Indicates that the native X11 window is an XCB window rather than an XLIB window.
|
||||
* This is ignored on non-Linux platforms and in builds that support only one X11 API.
|
||||
*/
|
||||
public static final long CONFIG_ENABLE_XCB = 0x4;
|
||||
|
||||
/**
|
||||
* Indicates that the SwapChain must automatically perform linear to sRGB encoding.
|
||||
*
|
||||
* This flag is ignored if isSRGBSwapChainSupported() is false.
|
||||
*
|
||||
* When using this flag, post-processing should be disabled.
|
||||
*
|
||||
* @see SwapChain#isSRGBSwapChainSupported
|
||||
* @see View#setPostProcessingEnabled
|
||||
*/
|
||||
public static final long CONFIG_SRGB_COLORSPACE = 0x10;
|
||||
|
||||
/**
|
||||
* Indicates that this SwapChain should allocate a stencil buffer in addition to a depth buffer.
|
||||
*
|
||||
* This flag is necessary when using View::setStencilBufferEnabled and rendering directly into
|
||||
* the SwapChain (when post-processing is disabled).
|
||||
*
|
||||
* The specific format of the stencil buffer depends on platform support. The following pixel
|
||||
* formats are tried, in order of preference:
|
||||
*
|
||||
* Depth only (without CONFIG_HAS_STENCIL_BUFFER):
|
||||
* - DEPTH32F
|
||||
* - DEPTH24
|
||||
*
|
||||
* Depth + stencil (with CONFIG_HAS_STENCIL_BUFFER):
|
||||
* - DEPTH32F_STENCIL8
|
||||
* - DEPTH24F_STENCIL8
|
||||
*
|
||||
* Note that enabling the stencil buffer may hinder depth precision and should only be used if
|
||||
* necessary.
|
||||
*
|
||||
* @see View#setStencilBufferEnabled
|
||||
* @see View#setPostProcessingEnabled
|
||||
*/
|
||||
public static final long CONFIG_HAS_STENCIL_BUFFER = 0x20;
|
||||
|
||||
/**
|
||||
* The SwapChain contains protected content. Only supported when isProtectedContentSupported()
|
||||
* is true.
|
||||
*/
|
||||
public static final long CONFIG_PROTECTED_CONTENT = 0x40;
|
||||
}
|
||||
|
||||
@@ -16,14 +16,12 @@ package com.google.android.filament;
|
||||
* <li>Configurable tone mapping operators</li>
|
||||
* <ul>
|
||||
* <li>GenericToneMapper</li>
|
||||
* <li>AgXToneMapper</li>
|
||||
* </ul>
|
||||
* <li>Fixed-aesthetic tone mapping operators</li>
|
||||
* <ul>
|
||||
* <li>ACESToneMapper</li>
|
||||
* <li>ACESLegacyToneMapper</li>
|
||||
* <li>FilmicToneMapper</li>
|
||||
* <li>PBRNeutralToneMapper</li>
|
||||
* </ul>
|
||||
* <li>Debug/validation tone mapping operators</li>
|
||||
* <ul>
|
||||
@@ -102,21 +100,11 @@ public class ToneMapper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Khronos PBR Neutral tone mapping operator. This tone mapper was designed
|
||||
* to preserve the appearance of materials across lighting conditions while
|
||||
* avoiding artifacts in the highlights in high dynamic range conditions.
|
||||
*/
|
||||
public static class PBRNeutralToneMapper extends ToneMapper {
|
||||
public PBRNeutralToneMapper() {
|
||||
super(nCreatePBRNeutralToneMapper());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AgX tone mapping operator.
|
||||
*/
|
||||
public static class Agx extends ToneMapper {
|
||||
|
||||
public enum AgxLook {
|
||||
/**
|
||||
* Base contrast with no look applied
|
||||
@@ -245,7 +233,6 @@ public class ToneMapper {
|
||||
private static native long nCreateACESToneMapper();
|
||||
private static native long nCreateACESLegacyToneMapper();
|
||||
private static native long nCreateFilmicToneMapper();
|
||||
private static native long nCreatePBRNeutralToneMapper();
|
||||
private static native long nCreateAgxToneMapper(int look);
|
||||
private static native long nCreateGenericToneMapper(
|
||||
float contrast, float midGrayIn, float midGrayOut, float hdrMax);
|
||||
|
||||
+2
-2
@@ -27,7 +27,7 @@ import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.TextureView;
|
||||
|
||||
import com.google.android.filament.SwapChainFlags;
|
||||
import com.google.android.filament.SwapChain;
|
||||
|
||||
/**
|
||||
* UiHelper is a simple class that can manage either a SurfaceView, TextureView, or a SurfaceHolder
|
||||
@@ -538,7 +538,7 @@ public class UiHelper {
|
||||
* the options set on this UiHelper.
|
||||
*/
|
||||
public long getSwapChainFlags() {
|
||||
return isOpaque() ? SwapChainFlags.CONFIG_DEFAULT : SwapChainFlags.CONFIG_TRANSPARENT;
|
||||
return isOpaque() ? SwapChain.CONFIG_DEFAULT : SwapChain.CONFIG_TRANSPARENT;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -81,17 +81,9 @@ set(GLTFIO_SRCS
|
||||
${GLTFIO_DIR}/src/TangentsJob.cpp
|
||||
${GLTFIO_DIR}/src/TangentsJob.h
|
||||
${GLTFIO_DIR}/src/UbershaderProvider.cpp
|
||||
${GLTFIO_DIR}/src/Utility.cpp
|
||||
${GLTFIO_DIR}/src/Utility.h
|
||||
${GLTFIO_DIR}/src/Wireframe.cpp
|
||||
${GLTFIO_DIR}/src/Wireframe.h
|
||||
${GLTFIO_DIR}/src/downcast.h
|
||||
${GLTFIO_DIR}/src/extended/AssetLoaderExtended.h
|
||||
${GLTFIO_DIR}/src/extended/TangentsJobExtended.cpp
|
||||
${GLTFIO_DIR}/src/extended/TangentsJobExtended.h
|
||||
${GLTFIO_DIR}/src/extended/TangentSpaceMeshWrapper.cpp
|
||||
${GLTFIO_DIR}/src/extended/TangentSpaceMeshWrapper.h
|
||||
|
||||
|
||||
src/main/cpp/Animator.cpp
|
||||
src/main/cpp/AssetLoader.cpp
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.51.0
|
||||
VERSION_NAME=1.50.4
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ android {
|
||||
compileSdkVersion versions.compileSdk
|
||||
defaultConfig {
|
||||
applicationId "com.google.android.filament.gltf"
|
||||
minSdkVersion versions.minSdk
|
||||
minSdkVersion 19
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
|
||||
@@ -516,7 +516,7 @@ function build_android {
|
||||
if [[ "${BUILD_ANDROID_SAMPLES}" == "true" ]]; then
|
||||
for sample in ${ANDROID_SAMPLES}; do
|
||||
echo "Installing out/${sample}-debug.apk"
|
||||
cp samples/${sample}/build/outputs/apk/debug/${sample}-debug.apk \
|
||||
cp samples/${sample}/build/outputs/apk/debug/${sample}-debug-unsigned.apk \
|
||||
../out/${sample}-debug.apk
|
||||
done
|
||||
fi
|
||||
|
||||
@@ -1 +1 @@
|
||||
26.1.10909125
|
||||
25.1.8937393
|
||||
@@ -21,7 +21,7 @@ set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_VERSION 1)
|
||||
|
||||
# android
|
||||
set(API_LEVEL 21)
|
||||
set(API_LEVEL 19)
|
||||
|
||||
# architecture
|
||||
set(ARCH armv7a-linux-androideabi)
|
||||
|
||||
@@ -21,7 +21,7 @@ set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_VERSION 1)
|
||||
|
||||
# android
|
||||
set(API_LEVEL 21)
|
||||
set(API_LEVEL 19)
|
||||
|
||||
# architecture
|
||||
set(ARCH i686-linux-android)
|
||||
|
||||
+21
-44
@@ -66,7 +66,6 @@ set(SRCS
|
||||
src/Froxelizer.cpp
|
||||
src/Frustum.cpp
|
||||
src/HwRenderPrimitiveFactory.cpp
|
||||
src/HwVertexBufferInfoFactory.cpp
|
||||
src/IndexBuffer.cpp
|
||||
src/IndirectLight.cpp
|
||||
src/InstanceBuffer.cpp
|
||||
@@ -138,7 +137,6 @@ set(SRCS
|
||||
|
||||
set(PRIVATE_HDRS
|
||||
src/Allocators.h
|
||||
src/Bimap.h
|
||||
src/BufferPoolAllocator.h
|
||||
src/ColorSpaceUtils.h
|
||||
src/Culler.h
|
||||
@@ -149,7 +147,6 @@ set(PRIVATE_HDRS
|
||||
src/FrameSkipper.h
|
||||
src/Froxelizer.h
|
||||
src/HwRenderPrimitiveFactory.h
|
||||
src/HwVertexBufferInfoFactory.h
|
||||
src/Intersections.h
|
||||
src/MaterialParser.h
|
||||
src/PerViewUniforms.h
|
||||
@@ -215,7 +212,6 @@ set(MATERIAL_SRCS
|
||||
src/materials/antiAliasing/fxaa.mat
|
||||
src/materials/antiAliasing/taa.mat
|
||||
src/materials/blitLow.mat
|
||||
src/materials/blitArray.mat
|
||||
src/materials/bloom/bloomDownsample.mat
|
||||
src/materials/bloom/bloomDownsample2x.mat
|
||||
src/materials/bloom/bloomDownsample9.mat
|
||||
@@ -252,13 +248,8 @@ set(MATERIAL_SRCS
|
||||
)
|
||||
|
||||
set(MATERIAL_FL0_SRCS
|
||||
src/materials/defaultMaterial.mat
|
||||
src/materials/skybox.mat
|
||||
)
|
||||
|
||||
set(MATERIAL_MULTIVIEW_SRCS
|
||||
src/materials/defaultMaterial.mat
|
||||
src/materials/skybox.mat
|
||||
src/materials/defaultMaterial0.mat
|
||||
src/materials/skybox0.mat
|
||||
)
|
||||
|
||||
# Embed the binary resource blob for materials.
|
||||
@@ -290,11 +281,6 @@ if (FILAMENT_ENABLE_FEATURE_LEVEL_0)
|
||||
add_definitions(-DFILAMENT_ENABLE_FEATURE_LEVEL_0)
|
||||
endif()
|
||||
|
||||
# Whether to include MULTIVIEW materials.
|
||||
if (FILAMENT_ENABLE_MULTIVIEW)
|
||||
add_definitions(-DFILAMENT_ENABLE_MULTIVIEW)
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
# Definitions
|
||||
# ==================================================================================================
|
||||
@@ -325,43 +311,34 @@ foreach (mat_src ${MATERIAL_SRCS})
|
||||
get_filename_component(localname "${mat_src}" NAME_WE)
|
||||
get_filename_component(fullname "${mat_src}" ABSOLUTE)
|
||||
set(output_path "${MATERIAL_DIR}/${localname}.filamat")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${output_path}
|
||||
COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname}
|
||||
MAIN_DEPENDENCY ${fullname}
|
||||
DEPENDS matc
|
||||
COMMENT "Compiling material ${fullname} to ${output_path}"
|
||||
COMMENT "Compiling material ${mat_src} to ${output_path}"
|
||||
)
|
||||
list(APPEND MATERIAL_BINS ${output_path})
|
||||
|
||||
list(FIND MATERIAL_FL0_SRCS ${mat_src} index)
|
||||
if (${index} GREATER -1 AND FILAMENT_ENABLE_FEATURE_LEVEL_0)
|
||||
string(REGEX REPLACE "[.]filamat$" "_fl0.filamat" output_path_fl0 ${output_path})
|
||||
add_custom_command(
|
||||
OUTPUT ${output_path_fl0}
|
||||
COMMAND matc ${MATC_BASE_FLAGS} -PfeatureLevel=0 -o ${output_path_fl0} ${fullname}
|
||||
MAIN_DEPENDENCY ${fullname}
|
||||
DEPENDS matc
|
||||
COMMENT "Compiling material ${fullname} to ${output_path_fl0}"
|
||||
)
|
||||
list(APPEND MATERIAL_BINS ${output_path_fl0})
|
||||
endif ()
|
||||
|
||||
list(FIND MATERIAL_MULTIVIEW_SRCS ${mat_src} index)
|
||||
if (${index} GREATER -1 AND FILAMENT_ENABLE_MULTIVIEW)
|
||||
string(REGEX REPLACE "[.]filamat$" "_multiview.filamat" output_path_multiview ${output_path})
|
||||
add_custom_command(
|
||||
OUTPUT ${output_path_multiview}
|
||||
COMMAND matc ${MATC_BASE_FLAGS} -PstereoscopicType=multiview -o ${output_path_multiview} ${fullname}
|
||||
MAIN_DEPENDENCY ${fullname}
|
||||
DEPENDS matc
|
||||
COMMENT "Compiling material ${fullname} to ${output_path_multiview}"
|
||||
)
|
||||
list(APPEND MATERIAL_BINS ${output_path_multiview})
|
||||
endif ()
|
||||
|
||||
endforeach()
|
||||
|
||||
if (FILAMENT_ENABLE_FEATURE_LEVEL_0)
|
||||
foreach (mat_src ${MATERIAL_FL0_SRCS})
|
||||
get_filename_component(localname "${mat_src}" NAME_WE)
|
||||
get_filename_component(fullname "${mat_src}" ABSOLUTE)
|
||||
set(output_path "${MATERIAL_DIR}/${localname}.filamat")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${output_path}
|
||||
COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname}
|
||||
MAIN_DEPENDENCY ${fullname}
|
||||
DEPENDS matc
|
||||
COMMENT "Compiling material ${mat_src} to ${output_path}"
|
||||
)
|
||||
list(APPEND MATERIAL_BINS ${output_path})
|
||||
endforeach ()
|
||||
endif ()
|
||||
|
||||
# Additional dependencies on included files for materials
|
||||
|
||||
add_custom_command(
|
||||
|
||||
@@ -166,8 +166,6 @@ endif()
|
||||
if (FILAMENT_SUPPORTS_VULKAN)
|
||||
list(APPEND SRCS
|
||||
include/backend/platforms/VulkanPlatform.h
|
||||
src/vulkan/caching/VulkanDescriptorSet.cpp
|
||||
src/vulkan/caching/VulkanDescriptorSet.h
|
||||
src/vulkan/platform/VulkanPlatform.cpp
|
||||
src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp
|
||||
src/vulkan/platform/VulkanPlatformSwapChainImpl.h
|
||||
|
||||
@@ -81,14 +81,7 @@ static constexpr uint64_t SWAP_CHAIN_CONFIG_SRGB_COLORSPACE = 0x10;
|
||||
/**
|
||||
* Indicates that the SwapChain should also contain a stencil component.
|
||||
*/
|
||||
static constexpr uint64_t SWAP_CHAIN_CONFIG_HAS_STENCIL_BUFFER = 0x20;
|
||||
static constexpr uint64_t SWAP_CHAIN_HAS_STENCIL_BUFFER = SWAP_CHAIN_CONFIG_HAS_STENCIL_BUFFER;
|
||||
|
||||
/**
|
||||
* The SwapChain contains protected content. Currently only supported by OpenGLPlatform and
|
||||
* only when OpenGLPlatform::isProtectedContextSupported() is true.
|
||||
*/
|
||||
static constexpr uint64_t SWAP_CHAIN_CONFIG_PROTECTED_CONTENT = 0x40;
|
||||
static constexpr uint64_t SWAP_CHAIN_HAS_STENCIL_BUFFER = 0x20;
|
||||
|
||||
|
||||
static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES.
|
||||
@@ -136,12 +129,6 @@ enum class Backend : uint8_t {
|
||||
NOOP = 4, //!< Selects the no-op driver for testing purposes.
|
||||
};
|
||||
|
||||
enum class TimerQueryResult : int8_t {
|
||||
ERROR = -1, // an error occurred, result won't be available
|
||||
NOT_READY = 0, // result to ready yet
|
||||
AVAILABLE = 1, // result is available
|
||||
};
|
||||
|
||||
static constexpr const char* backendToString(Backend backend) {
|
||||
switch (backend) {
|
||||
case Backend::NOOP:
|
||||
@@ -672,17 +659,16 @@ enum class TextureFormat : uint16_t {
|
||||
};
|
||||
|
||||
//! Bitmask describing the intended Texture Usage
|
||||
enum class TextureUsage : uint16_t {
|
||||
NONE = 0x0000,
|
||||
COLOR_ATTACHMENT = 0x0001, //!< Texture can be used as a color attachment
|
||||
DEPTH_ATTACHMENT = 0x0002, //!< Texture can be used as a depth attachment
|
||||
STENCIL_ATTACHMENT = 0x0004, //!< Texture can be used as a stencil attachment
|
||||
UPLOADABLE = 0x0008, //!< Data can be uploaded into this texture (default)
|
||||
SAMPLEABLE = 0x0010, //!< Texture can be sampled (default)
|
||||
SUBPASS_INPUT = 0x0020, //!< Texture can be used as a subpass input
|
||||
BLIT_SRC = 0x0040, //!< Texture can be used the source of a blit()
|
||||
BLIT_DST = 0x0080, //!< Texture can be used the destination of a blit()
|
||||
PROTECTED = 0x0100, //!< Texture can be used the destination of a blit()
|
||||
enum class TextureUsage : uint8_t {
|
||||
NONE = 0x00,
|
||||
COLOR_ATTACHMENT = 0x01, //!< Texture can be used as a color attachment
|
||||
DEPTH_ATTACHMENT = 0x02, //!< Texture can be used as a depth attachment
|
||||
STENCIL_ATTACHMENT = 0x04, //!< Texture can be used as a stencil attachment
|
||||
UPLOADABLE = 0x08, //!< Data can be uploaded into this texture (default)
|
||||
SAMPLEABLE = 0x10, //!< Texture can be sampled (default)
|
||||
SUBPASS_INPUT = 0x20, //!< Texture can be used as a subpass input
|
||||
BLIT_SRC = 0x40, //!< Texture can be used the source of a blit()
|
||||
BLIT_DST = 0x80, //!< Texture can be used the destination of a blit()
|
||||
DEFAULT = UPLOADABLE | SAMPLEABLE //!< Default texture usage
|
||||
};
|
||||
|
||||
@@ -1186,27 +1172,11 @@ struct StencilState {
|
||||
|
||||
//! Stencil operations for front-facing polygons
|
||||
StencilOperations front = {
|
||||
.stencilFunc = StencilFunction::A,
|
||||
.stencilOpStencilFail = StencilOperation::KEEP,
|
||||
.padding0 = 0,
|
||||
.stencilOpDepthFail = StencilOperation::KEEP,
|
||||
.stencilOpDepthStencilPass = StencilOperation::KEEP,
|
||||
.padding1 = 0,
|
||||
.ref = 0,
|
||||
.readMask = 0xff,
|
||||
.writeMask = 0xff };
|
||||
.stencilFunc = StencilFunction::A, .ref = 0, .readMask = 0xff, .writeMask = 0xff };
|
||||
|
||||
//! Stencil operations for back-facing polygons
|
||||
StencilOperations back = {
|
||||
.stencilFunc = StencilFunction::A,
|
||||
.stencilOpStencilFail = StencilOperation::KEEP,
|
||||
.padding0 = 0,
|
||||
.stencilOpDepthFail = StencilOperation::KEEP,
|
||||
.stencilOpDepthStencilPass = StencilOperation::KEEP,
|
||||
.padding1 = 0,
|
||||
.ref = 0,
|
||||
.readMask = 0xff,
|
||||
.writeMask = 0xff };
|
||||
.stencilFunc = StencilFunction::A, .ref = 0, .readMask = 0xff, .writeMask = 0xff };
|
||||
|
||||
//! Whether stencil-buffer writes are enabled
|
||||
bool stencilWrite = false;
|
||||
|
||||
@@ -39,7 +39,6 @@ struct HwStream;
|
||||
struct HwSwapChain;
|
||||
struct HwTexture;
|
||||
struct HwTimerQuery;
|
||||
struct HwVertexBufferInfo;
|
||||
struct HwVertexBuffer;
|
||||
|
||||
/*
|
||||
@@ -115,19 +114,18 @@ private:
|
||||
|
||||
// Types used by the command stream
|
||||
// (we use this renaming because the macro-system doesn't deal well with "<" and ">")
|
||||
using BufferObjectHandle = Handle<HwBufferObject>;
|
||||
using FenceHandle = Handle<HwFence>;
|
||||
using IndexBufferHandle = Handle<HwIndexBuffer>;
|
||||
using ProgramHandle = Handle<HwProgram>;
|
||||
using RenderPrimitiveHandle = Handle<HwRenderPrimitive>;
|
||||
using RenderTargetHandle = Handle<HwRenderTarget>;
|
||||
using SamplerGroupHandle = Handle<HwSamplerGroup>;
|
||||
using StreamHandle = Handle<HwStream>;
|
||||
using SwapChainHandle = Handle<HwSwapChain>;
|
||||
using TextureHandle = Handle<HwTexture>;
|
||||
using TimerQueryHandle = Handle<HwTimerQuery>;
|
||||
using VertexBufferHandle = Handle<HwVertexBuffer>;
|
||||
using VertexBufferInfoHandle = Handle<HwVertexBufferInfo>;
|
||||
using BufferObjectHandle = Handle<HwBufferObject>;
|
||||
using FenceHandle = Handle<HwFence>;
|
||||
using IndexBufferHandle = Handle<HwIndexBuffer>;
|
||||
using ProgramHandle = Handle<HwProgram>;
|
||||
using RenderPrimitiveHandle = Handle<HwRenderPrimitive>;
|
||||
using RenderTargetHandle = Handle<HwRenderTarget>;
|
||||
using SamplerGroupHandle = Handle<HwSamplerGroup>;
|
||||
using StreamHandle = Handle<HwStream>;
|
||||
using SwapChainHandle = Handle<HwSwapChain>;
|
||||
using TextureHandle = Handle<HwTexture>;
|
||||
using TimerQueryHandle = Handle<HwTimerQuery>;
|
||||
using VertexBufferHandle = Handle<HwVertexBuffer>;
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
|
||||
@@ -29,13 +29,11 @@ namespace filament::backend {
|
||||
//! \privatesection
|
||||
|
||||
struct PipelineState {
|
||||
Handle<HwProgram> program; // 4
|
||||
Handle<HwVertexBufferInfo> vertexBufferInfo; // 4
|
||||
RasterState rasterState; // 4
|
||||
StencilState stencilState; // 12
|
||||
PolygonOffset polygonOffset; // 8
|
||||
PrimitiveType primitiveType = PrimitiveType::TRIANGLES; // 1
|
||||
uint8_t padding[3] = {}; // 3
|
||||
Handle<HwProgram> program;
|
||||
RasterState rasterState;
|
||||
StencilState stencilState;
|
||||
PolygonOffset polygonOffset;
|
||||
Viewport scissor{ 0, 0, (uint32_t)INT32_MAX, (uint32_t)INT32_MAX };
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include <utils/Invocable.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -57,14 +56,9 @@ public:
|
||||
|
||||
/**
|
||||
* Set to `true` to forcibly disable parallel shader compilation in the backend.
|
||||
* Currently only honored by the GL and Metal backends.
|
||||
* Currently only honored by the GL backend.
|
||||
*/
|
||||
bool disableParallelShaderCompile = false;
|
||||
|
||||
/**
|
||||
* Disable backend handles use-after-free checks.
|
||||
*/
|
||||
bool disableHandleUseAfterFreeCheck = false;
|
||||
};
|
||||
|
||||
Platform() noexcept;
|
||||
@@ -194,40 +188,9 @@ public:
|
||||
size_t retrieveBlob(const void* UTILS_NONNULL key, size_t keySize,
|
||||
void* UTILS_NONNULL value, size_t valueSize);
|
||||
|
||||
using DebugUpdateStatFunc = utils::Invocable<void(const char* UTILS_NONNULL key, uint64_t value)>;
|
||||
|
||||
/**
|
||||
* Sets the callback function that the backend can use to update backend-specific statistics
|
||||
* to aid with debugging. This callback is guaranteed to be called on the Filament driver
|
||||
* thread.
|
||||
*
|
||||
* @param debugUpdateStat an Invocable that updates debug statistics
|
||||
*/
|
||||
void setDebugUpdateStatFunc(DebugUpdateStatFunc&& debugUpdateStat) noexcept;
|
||||
|
||||
/**
|
||||
* @return true if debugUpdateStat is valid.
|
||||
*/
|
||||
bool hasDebugUpdateStatFunc() const noexcept;
|
||||
|
||||
/**
|
||||
* To track backend-specific statistics, the backend implementation can call the
|
||||
* application-provided callback function debugUpdateStatFunc to associate or update a value
|
||||
* with a given key. It is possible for this function to be called multiple times with the
|
||||
* same key, in which case newer values should overwrite older values.
|
||||
*
|
||||
* This function is guaranteed to be called only on a single thread, the Filament driver
|
||||
* thread.
|
||||
*
|
||||
* @param key a null-terminated C-string with the key of the debug statistic
|
||||
* @param value the updated value of key
|
||||
*/
|
||||
void debugUpdateStat(const char* UTILS_NONNULL key, uint64_t value);
|
||||
|
||||
private:
|
||||
InsertBlobFunc mInsertBlob;
|
||||
RetrieveBlobFunc mRetrieveBlob;
|
||||
DebugUpdateStatFunc mDebugUpdateStat;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -116,8 +116,6 @@ public:
|
||||
|
||||
Program& cacheId(uint64_t cacheId) noexcept;
|
||||
|
||||
Program& multiview(bool multiview) noexcept;
|
||||
|
||||
ShaderSource const& getShadersSource() const noexcept { return mShadersSource; }
|
||||
ShaderSource& getShadersSource() noexcept { return mShadersSource; }
|
||||
|
||||
@@ -145,8 +143,6 @@ public:
|
||||
|
||||
uint64_t getCacheId() const noexcept { return mCacheId; }
|
||||
|
||||
bool isMultiview() const noexcept { return mMultiview; }
|
||||
|
||||
CompilerPriorityQueue getPriorityQueue() const noexcept { return mPriorityQueue; }
|
||||
|
||||
private:
|
||||
@@ -162,11 +158,6 @@ private:
|
||||
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> mAttributes;
|
||||
std::array<UniformInfo, Program::UNIFORM_BINDING_COUNT> mBindingUniformInfo;
|
||||
CompilerPriorityQueue mPriorityQueue = CompilerPriorityQueue::HIGH;
|
||||
// Indicates the current engine was initialized with multiview stereo, and the variant for this
|
||||
// program contains STE flag. This will be referred later for the OpenGL shader compiler to
|
||||
// determine whether shader code replacement for the num_views should be performed.
|
||||
// This variable could be promoted as a more generic variable later if other similar needs occur.
|
||||
bool mMultiview = false;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -32,10 +32,6 @@ struct TargetBufferInfo {
|
||||
// texture to be used as render target
|
||||
Handle<HwTexture> handle;
|
||||
|
||||
// starting layer index for multiview. This value is only used when the `layerCount` for the
|
||||
// render target is greater than 1.
|
||||
uint8_t baseViewIndex = 0;
|
||||
|
||||
// level to be used
|
||||
uint8_t level = 0;
|
||||
|
||||
@@ -84,7 +80,7 @@ public:
|
||||
|
||||
// this is here for backward compatibility
|
||||
MRT(Handle<HwTexture> handle, uint8_t level, uint16_t layer) noexcept
|
||||
: mInfos{{ handle, 0, level, layer }} {
|
||||
: mInfos{{ handle, level, layer }} {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -22,9 +22,7 @@
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Invocable.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
@@ -64,22 +62,6 @@ public:
|
||||
*/
|
||||
virtual void terminate() noexcept = 0;
|
||||
|
||||
/**
|
||||
* Return whether createSwapChain supports the SWAP_CHAIN_CONFIG_SRGB_COLORSPACE flag.
|
||||
* The default implementation returns false.
|
||||
*
|
||||
* @return true if SWAP_CHAIN_CONFIG_SRGB_COLORSPACE is supported, false otherwise.
|
||||
*/
|
||||
virtual bool isSRGBSwapChainSupported() const noexcept;
|
||||
|
||||
/**
|
||||
* Return whether protected contexts are supported by this backend.
|
||||
* If protected context are supported, the SWAP_CHAIN_CONFIG_PROTECTED_CONTENT flag can be
|
||||
* used when creating a SwapChain.
|
||||
* The default implementation returns false.
|
||||
*/
|
||||
virtual bool isProtectedContextSupported() const noexcept;
|
||||
|
||||
/**
|
||||
* Called by the driver to create a SwapChain for this driver.
|
||||
*
|
||||
@@ -89,9 +71,17 @@ public:
|
||||
* @return The driver's SwapChain object.
|
||||
*
|
||||
*/
|
||||
virtual SwapChain* UTILS_NULLABLE createSwapChain(
|
||||
virtual SwapChain* UTILS_NONNULL createSwapChain(
|
||||
void* UTILS_NULLABLE nativeWindow, uint64_t flags) noexcept = 0;
|
||||
|
||||
/**
|
||||
* Return whether createSwapChain supports the SWAP_CHAIN_CONFIG_SRGB_COLORSPACE flag.
|
||||
* The default implementation returns false.
|
||||
*
|
||||
* @return true if SWAP_CHAIN_CONFIG_SRGB_COLORSPACE is supported, false otherwise.
|
||||
*/
|
||||
virtual bool isSRGBSwapChainSupported() const noexcept;
|
||||
|
||||
/**
|
||||
* Called by the driver create a headless SwapChain.
|
||||
*
|
||||
@@ -125,71 +115,21 @@ public:
|
||||
*/
|
||||
virtual TargetBufferFlags getPreservedFlags(SwapChain* UTILS_NONNULL swapChain) noexcept;
|
||||
|
||||
/**
|
||||
* Returns true if the swapchain is protected
|
||||
*/
|
||||
virtual bool isSwapChainProtected(Platform::SwapChain* UTILS_NONNULL swapChain) noexcept;
|
||||
|
||||
/**
|
||||
* Called by the driver to establish the default FBO. The default implementation returns 0.
|
||||
*
|
||||
* This method can be called either on the regular or protected OpenGL contexts and can return
|
||||
* a different or identical name, since these names exist in different namespaces.
|
||||
*
|
||||
* @return a GLuint casted to a uint32_t that is an OpenGL framebuffer object.
|
||||
* @return a GLuint casted to a uint32_t that is an OpenGL framebuffer object.
|
||||
*/
|
||||
virtual uint32_t getDefaultFramebufferObject() noexcept;
|
||||
|
||||
|
||||
/**
|
||||
* Type of contexts available
|
||||
*/
|
||||
enum class ContextType {
|
||||
NONE, //!< No current context
|
||||
UNPROTECTED, //!< current context is unprotected
|
||||
PROTECTED //!< current context supports protected content
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the type of the context currently in use. This value is updated by makeCurrent()
|
||||
* and therefore can be cached between calls. ContextType::PROTECTED can only be returned
|
||||
* if isProtectedContextSupported() is true.
|
||||
* @return ContextType
|
||||
*/
|
||||
virtual ContextType getCurrentContextType() const noexcept;
|
||||
|
||||
/**
|
||||
* Binds the requested context to the current thread and drawSwapChain to the default FBO
|
||||
* returned by getDefaultFramebufferObject().
|
||||
*
|
||||
* @param type type of context to bind to the current thread.
|
||||
* @param drawSwapChain SwapChain to draw to. It must be bound to the default FBO.
|
||||
* @param readSwapChain SwapChain to read from (for operation like `glBlitFramebuffer`)
|
||||
* @return true on success, false on error.
|
||||
*/
|
||||
virtual bool makeCurrent(ContextType type,
|
||||
SwapChain* UTILS_NONNULL drawSwapChain,
|
||||
SwapChain* UTILS_NONNULL readSwapChain) noexcept = 0;
|
||||
virtual uint32_t createDefaultRenderTarget() noexcept;
|
||||
|
||||
/**
|
||||
* Called by the driver to make the OpenGL context active on the calling thread and bind
|
||||
* the drawSwapChain to the default FBO returned by getDefaultFramebufferObject().
|
||||
* The context used is either the default context or the protected context. When a context
|
||||
* change is necessary, the preContextChange and postContextChange callbacks are called,
|
||||
* before and after the context change respectively. postContextChange is given the index
|
||||
* of the new context (0 for default and 1 for protected).
|
||||
* The default implementation just calls makeCurrent(getCurrentContextType(), SwapChain*, SwapChain*).
|
||||
*
|
||||
* the drawSwapChain to the default render target (FBO) created with createDefaultRenderTarget.
|
||||
* @param drawSwapChain SwapChain to draw to. It must be bound to the default FBO.
|
||||
* @param readSwapChain SwapChain to read from (for operation like `glBlitFramebuffer`)
|
||||
* @param preContextChange called before the context changes
|
||||
* @param postContextChange called after the context changes
|
||||
*/
|
||||
virtual void makeCurrent(
|
||||
SwapChain* UTILS_NONNULL drawSwapChain,
|
||||
SwapChain* UTILS_NONNULL readSwapChain,
|
||||
utils::Invocable<void()> preContextChange,
|
||||
utils::Invocable<void(size_t index)> postContextChange) noexcept;
|
||||
SwapChain* UTILS_NONNULL readSwapChain) noexcept = 0;
|
||||
|
||||
/**
|
||||
* Called by the driver once the current frame finishes drawing. Typically, this should present
|
||||
|
||||
@@ -57,7 +57,7 @@ protected:
|
||||
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||
void destroySwapChain(SwapChain* swapChain) noexcept override;
|
||||
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
void commit(SwapChain* swapChain) noexcept override;
|
||||
OpenGLPlatform::ExternalTexture* createExternalImageTexture() noexcept override;
|
||||
void destroyExternalImage(ExternalTexture* texture) noexcept override;
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
|
||||
void terminate() noexcept override;
|
||||
|
||||
uint32_t getDefaultFramebufferObject() noexcept override;
|
||||
uint32_t createDefaultRenderTarget() noexcept override;
|
||||
|
||||
bool isExtraContextSupported() const noexcept override;
|
||||
void createContext(bool shared) override;
|
||||
@@ -53,7 +53,7 @@ public:
|
||||
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||
void destroySwapChain(SwapChain* swapChain) noexcept override;
|
||||
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
void commit(SwapChain* swapChain) noexcept override;
|
||||
|
||||
OpenGLPlatform::ExternalTexture* createExternalImageTexture() noexcept override;
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
#include <EGL/eglext.h>
|
||||
#include <EGL/eglplatform.h>
|
||||
|
||||
#include <utils/Invocable.h>
|
||||
|
||||
#include <initializer_list>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -43,11 +41,15 @@ class PlatformEGL : public OpenGLPlatform {
|
||||
public:
|
||||
|
||||
PlatformEGL() noexcept;
|
||||
bool isExtraContextSupported() const noexcept override;
|
||||
void createContext(bool shared) override;
|
||||
void releaseContext() noexcept override;
|
||||
|
||||
// Return true if we're on an OpenGL platform (as opposed to OpenGL ES). false by default.
|
||||
virtual bool isOpenGL() const noexcept;
|
||||
|
||||
protected:
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// Helper for EGL configs and attributes parameters
|
||||
|
||||
@@ -87,30 +89,13 @@ protected:
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// OpenGLPlatform Interface
|
||||
|
||||
bool isExtraContextSupported() const noexcept override;
|
||||
void createContext(bool shared) override;
|
||||
void releaseContext() noexcept override;
|
||||
|
||||
void terminate() noexcept override;
|
||||
|
||||
bool isProtectedContextSupported() const noexcept override;
|
||||
|
||||
bool isSRGBSwapChainSupported() const noexcept override;
|
||||
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||
void destroySwapChain(SwapChain* swapChain) noexcept override;
|
||||
bool isSwapChainProtected(SwapChain* swapChain) noexcept override;
|
||||
|
||||
ContextType getCurrentContextType() const noexcept override;
|
||||
|
||||
bool makeCurrent(ContextType type,
|
||||
SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain) noexcept override;
|
||||
|
||||
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain,
|
||||
utils::Invocable<void()> preContextChange,
|
||||
utils::Invocable<void(size_t index)> postContextChange) noexcept override;
|
||||
|
||||
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
void commit(SwapChain* swapChain) noexcept override;
|
||||
|
||||
bool canCreateFence() noexcept override;
|
||||
@@ -137,27 +122,16 @@ protected:
|
||||
static void clearGlError() noexcept;
|
||||
|
||||
/**
|
||||
* Always use this instead of eglMakeCurrent(), as it tracks some state.
|
||||
* Always use this instead of eglMakeCurrent().
|
||||
*/
|
||||
|
||||
EGLContext getContextForType(ContextType type) const noexcept;
|
||||
|
||||
// makes the draw and read surface current without changing the current context
|
||||
EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept {
|
||||
return egl.makeCurrent(drawSurface, readSurface);
|
||||
}
|
||||
|
||||
// makes context current and set draw and read surfaces to EGL_NO_SURFACE
|
||||
EGLBoolean makeCurrent(EGLContext context) noexcept {
|
||||
return egl.makeCurrent(context, mEGLDummySurface, mEGLDummySurface);
|
||||
}
|
||||
EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept;
|
||||
|
||||
// TODO: this should probably use getters instead.
|
||||
EGLDisplay mEGLDisplay = EGL_NO_DISPLAY;
|
||||
EGLContext mEGLContext = EGL_NO_CONTEXT;
|
||||
EGLContext mEGLContextProtected = EGL_NO_CONTEXT;
|
||||
EGLSurface mCurrentDrawSurface = EGL_NO_SURFACE;
|
||||
EGLSurface mCurrentReadSurface = EGL_NO_SURFACE;
|
||||
EGLSurface mEGLDummySurface = EGL_NO_SURFACE;
|
||||
ContextType mCurrentContextType = ContextType::NONE;
|
||||
// mEGLConfig is valid only if ext.egl.KHR_no_config_context is false
|
||||
EGLConfig mEGLConfig = EGL_NO_CONFIG_KHR;
|
||||
Config mContextAttribs;
|
||||
@@ -174,38 +148,13 @@ protected:
|
||||
bool KHR_gl_colorspace = false;
|
||||
bool KHR_no_config_context = false;
|
||||
bool KHR_surfaceless_context = false;
|
||||
bool EXT_protected_content = false;
|
||||
} egl;
|
||||
} ext;
|
||||
|
||||
struct SwapChainEGL : public Platform::SwapChain {
|
||||
EGLSurface sur = EGL_NO_SURFACE;
|
||||
Config attribs{};
|
||||
EGLNativeWindowType nativeWindow{};
|
||||
EGLConfig config{};
|
||||
uint64_t flags{};
|
||||
};
|
||||
|
||||
void initializeGlExtensions() noexcept;
|
||||
|
||||
protected:
|
||||
EGLConfig findSwapChainConfig(uint64_t flags, bool window, bool pbuffer) const;
|
||||
|
||||
private:
|
||||
class EGL {
|
||||
EGLDisplay& mEGLDisplay;
|
||||
EGLSurface mCurrentDrawSurface = EGL_NO_SURFACE;
|
||||
EGLSurface mCurrentReadSurface = EGL_NO_SURFACE;
|
||||
EGLContext mCurrentContext = EGL_NO_CONTEXT;
|
||||
public:
|
||||
explicit EGL(EGLDisplay& dpy) : mEGLDisplay(dpy) {}
|
||||
EGLBoolean makeCurrent(EGLContext context,
|
||||
EGLSurface drawSurface, EGLSurface readSurface) noexcept;
|
||||
|
||||
EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept {
|
||||
return makeCurrent(mCurrentContext, drawSurface, readSurface);
|
||||
}
|
||||
} egl{ mEGLDisplay };
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -51,7 +51,7 @@ protected:
|
||||
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||
void destroySwapChain(SwapChain* swapChain) noexcept override;
|
||||
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
void commit(SwapChain* swapChain) noexcept override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -53,7 +53,7 @@ protected:
|
||||
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||
void destroySwapChain(SwapChain* swapChain) noexcept override;
|
||||
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
void commit(SwapChain* swapChain) noexcept override;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -46,7 +46,7 @@ protected:
|
||||
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||
void destroySwapChain(SwapChain* swapChain) noexcept override;
|
||||
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
void commit(SwapChain* swapChain) noexcept override;
|
||||
};
|
||||
|
||||
|
||||
@@ -50,13 +50,12 @@ class CommandBufferQueue {
|
||||
size_t mFreeSpace = 0;
|
||||
size_t mHighWatermark = 0;
|
||||
uint32_t mExitRequested = 0;
|
||||
bool mPaused = false;
|
||||
|
||||
static constexpr uint32_t EXIT_REQUESTED = 0x31415926;
|
||||
|
||||
public:
|
||||
// requiredSize: guaranteed available space after flush()
|
||||
CommandBufferQueue(size_t requiredSize, size_t bufferSize, bool paused);
|
||||
CommandBufferQueue(size_t requiredSize, size_t bufferSize);
|
||||
~CommandBufferQueue();
|
||||
|
||||
CircularBuffer& getCircularBuffer() noexcept { return mCircularBuffer; }
|
||||
@@ -81,9 +80,6 @@ public:
|
||||
// returns from waitForCommands() immediately.
|
||||
void requestExit();
|
||||
|
||||
// suspend or unsuspend the queue.
|
||||
void setPaused(bool paused);
|
||||
|
||||
bool isExitRequested() const;
|
||||
};
|
||||
|
||||
|
||||
@@ -167,14 +167,11 @@ DECL_DRIVER_API_0(resetState)
|
||||
* -----------------------
|
||||
*/
|
||||
|
||||
DECL_DRIVER_API_R_N(backend::VertexBufferInfoHandle, createVertexBufferInfo,
|
||||
DECL_DRIVER_API_R_N(backend::VertexBufferHandle, createVertexBuffer,
|
||||
uint8_t, bufferCount,
|
||||
uint8_t, attributeCount,
|
||||
backend::AttributeArray, attributes)
|
||||
|
||||
DECL_DRIVER_API_R_N(backend::VertexBufferHandle, createVertexBuffer,
|
||||
uint32_t, vertexCount,
|
||||
backend::VertexBufferInfoHandle, vbih)
|
||||
backend::AttributeArray, attributes)
|
||||
|
||||
DECL_DRIVER_API_R_N(backend::IndexBufferHandle, createIndexBuffer,
|
||||
backend::ElementType, elementType,
|
||||
@@ -227,7 +224,11 @@ DECL_DRIVER_API_R_N(backend::SamplerGroupHandle, createSamplerGroup,
|
||||
DECL_DRIVER_API_R_N(backend::RenderPrimitiveHandle, createRenderPrimitive,
|
||||
backend::VertexBufferHandle, vbh,
|
||||
backend::IndexBufferHandle, ibh,
|
||||
backend::PrimitiveType, pt)
|
||||
backend::PrimitiveType, pt,
|
||||
uint32_t, offset,
|
||||
uint32_t, minIndex,
|
||||
uint32_t, maxIndex,
|
||||
uint32_t, count)
|
||||
|
||||
DECL_DRIVER_API_R_N(backend::ProgramHandle, createProgram,
|
||||
backend::Program&&, program)
|
||||
@@ -239,7 +240,6 @@ DECL_DRIVER_API_R_N(backend::RenderTargetHandle, createRenderTarget,
|
||||
uint32_t, width,
|
||||
uint32_t, height,
|
||||
uint8_t, samples,
|
||||
uint8_t, layerCount,
|
||||
backend::MRT, color,
|
||||
backend::TargetBufferInfo, depth,
|
||||
backend::TargetBufferInfo, stencil)
|
||||
@@ -264,7 +264,6 @@ DECL_DRIVER_API_R_0(backend::TimerQueryHandle, createTimerQuery)
|
||||
*/
|
||||
|
||||
DECL_DRIVER_API_N(destroyVertexBuffer, backend::VertexBufferHandle, vbh)
|
||||
DECL_DRIVER_API_N(destroyVertexBufferInfo,backend::VertexBufferInfoHandle, vbih)
|
||||
DECL_DRIVER_API_N(destroyIndexBuffer, backend::IndexBufferHandle, ibh)
|
||||
DECL_DRIVER_API_N(destroyBufferObject, backend::BufferObjectHandle, ibh)
|
||||
DECL_DRIVER_API_N(destroyRenderPrimitive, backend::RenderPrimitiveHandle, rph)
|
||||
@@ -299,16 +298,14 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isFrameBufferFetchMultiSampleSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isFrameTimeSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isAutoDepthResolveSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isSRGBSwapChainSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isProtectedContentSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isStereoSupported, backend::StereoscopicType, stereoscopicType)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isStereoSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isParallelShaderCompileSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthStencilResolveSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isProtectedTexturesSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxUniformBufferSize)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(math::float2, getClipSpaceParams)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(void, setupExternalImage, void*, image)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(backend::TimerQueryResult, getTimerQueryValue, backend::TimerQueryHandle, query, uint64_t*, elapsedTime)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(bool, getTimerQueryValue, backend::TimerQueryHandle, query, uint64_t*, elapsedTime)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isWorkaroundNeeded, backend::Workaround, workaround)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(backend::FeatureLevel, getFeatureLevel)
|
||||
|
||||
@@ -494,32 +491,15 @@ DECL_DRIVER_API_N(blit,
|
||||
math::uint2, srcOrigin,
|
||||
math::uint2, size)
|
||||
|
||||
DECL_DRIVER_API_N(bindPipeline,
|
||||
backend::PipelineState, state)
|
||||
|
||||
DECL_DRIVER_API_N(bindRenderPrimitive,
|
||||
backend::RenderPrimitiveHandle, rph)
|
||||
|
||||
DECL_DRIVER_API_N(draw2,
|
||||
uint32_t, indexOffset,
|
||||
uint32_t, indexCount,
|
||||
uint32_t, instanceCount)
|
||||
|
||||
DECL_DRIVER_API_N(draw,
|
||||
backend::PipelineState, state,
|
||||
backend::RenderPrimitiveHandle, rph,
|
||||
uint32_t, indexOffset,
|
||||
uint32_t, indexCount,
|
||||
uint32_t, instanceCount)
|
||||
|
||||
|
||||
DECL_DRIVER_API_N(dispatchCompute,
|
||||
backend::ProgramHandle, program,
|
||||
math::uint3, workGroupCount)
|
||||
|
||||
DECL_DRIVER_API_N(scissor,
|
||||
Viewport, scissor)
|
||||
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
|
||||
@@ -37,9 +37,9 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define HandleAllocatorGL HandleAllocator<32, 64, 136> // ~4520 / pool / MiB
|
||||
#define HandleAllocatorVK HandleAllocator<64, 160, 312> // ~1820 / pool / MiB
|
||||
#define HandleAllocatorMTL HandleAllocator<32, 48, 552> // ~1660 / pool / MiB
|
||||
#define HandleAllocatorGL HandleAllocator<16, 64, 208> // ~3640 / pool / MiB
|
||||
#define HandleAllocatorVK HandleAllocator<80, 176, 320> // ~1820 / pool / MiB
|
||||
#define HandleAllocatorMTL HandleAllocator<48, 160, 592> // ~1310 / pool / MiB
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace filament::backend {
|
||||
template<size_t P0, size_t P1, size_t P2>
|
||||
class HandleAllocator {
|
||||
public:
|
||||
HandleAllocator(const char* name, size_t size, bool disableUseAfterFreeCheck) noexcept;
|
||||
HandleAllocator(const char* name, size_t size) noexcept;
|
||||
HandleAllocator(HandleAllocator const& rhs) = delete;
|
||||
HandleAllocator& operator=(HandleAllocator const& rhs) = delete;
|
||||
~HandleAllocator();
|
||||
@@ -169,13 +169,11 @@ public:
|
||||
|
||||
if (isPoolHandle(handle.getId())) {
|
||||
// check for use after free
|
||||
if (UTILS_UNLIKELY(!mUseAfterFreeCheckDisabled)) {
|
||||
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;
|
||||
ASSERT_POSTCONDITION(expectedAge == age,
|
||||
"use-after-free of Handle with id=%d", handle.getId());
|
||||
}
|
||||
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;
|
||||
ASSERT_POSTCONDITION(expectedAge == age,
|
||||
"use-after-free of Handle with id=%d", handle.getId());
|
||||
}
|
||||
|
||||
return static_cast<Dp>(p);
|
||||
@@ -189,6 +187,7 @@ public:
|
||||
return handle_cast<Dp>(const_cast<Handle<B>&>(handle));
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
template<typename D>
|
||||
@@ -211,9 +210,8 @@ private:
|
||||
Pool<P1> mPool1;
|
||||
Pool<P2> mPool2;
|
||||
UTILS_UNUSED_IN_RELEASE const utils::AreaPolicy::HeapArea& mArea;
|
||||
bool mUseAfterFreeCheckDisabled;
|
||||
public:
|
||||
explicit Allocator(const utils::AreaPolicy::HeapArea& area, bool disableUseAfterFreeCheck);
|
||||
explicit Allocator(const utils::AreaPolicy::HeapArea& area);
|
||||
|
||||
static constexpr size_t getAlignment() noexcept { return MIN_ALIGNMENT; }
|
||||
|
||||
@@ -239,10 +237,8 @@ private:
|
||||
// check for double-free
|
||||
Node* const pNode = static_cast<Node*>(p);
|
||||
uint8_t& expectedAge = pNode[-1].age;
|
||||
if (UTILS_UNLIKELY(!mUseAfterFreeCheckDisabled)) {
|
||||
ASSERT_POSTCONDITION(expectedAge == age,
|
||||
"double-free of Handle of size %d at %p", size, p);
|
||||
}
|
||||
ASSERT_POSTCONDITION(expectedAge == age,
|
||||
"double-free of Handle of size %d at %p", size, p);
|
||||
expectedAge = (expectedAge + 1) & 0xF; // fixme
|
||||
|
||||
if (size <= mPool0.getSize()) { mPool0.free(p); return; }
|
||||
@@ -352,7 +348,6 @@ private:
|
||||
mutable utils::Mutex mLock;
|
||||
tsl::robin_map<HandleBase::HandleId, void*> mOverflowMap;
|
||||
HandleBase::HandleId mId = 0;
|
||||
bool mUseAfterFreeCheckDisabled = false;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -39,11 +39,10 @@ using namespace utils;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
CommandBufferQueue::CommandBufferQueue(size_t requiredSize, size_t bufferSize, bool paused)
|
||||
CommandBufferQueue::CommandBufferQueue(size_t requiredSize, size_t bufferSize)
|
||||
: mRequiredSize((requiredSize + (CircularBuffer::getBlockSize() - 1u)) & ~(CircularBuffer::getBlockSize() -1u)),
|
||||
mCircularBuffer(bufferSize),
|
||||
mFreeSpace(mCircularBuffer.size()),
|
||||
mPaused(paused) {
|
||||
mFreeSpace(mCircularBuffer.size()) {
|
||||
assert_invariant(mCircularBuffer.size() > requiredSize);
|
||||
}
|
||||
|
||||
@@ -57,16 +56,6 @@ void CommandBufferQueue::requestExit() {
|
||||
mCondition.notify_one();
|
||||
}
|
||||
|
||||
void CommandBufferQueue::setPaused(bool paused) {
|
||||
std::lock_guard<utils::Mutex> const lock(mLock);
|
||||
if (paused) {
|
||||
mPaused = true;
|
||||
} else {
|
||||
mPaused = false;
|
||||
mCondition.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
bool CommandBufferQueue::isExitRequested() const {
|
||||
std::lock_guard<utils::Mutex> const lock(mLock);
|
||||
ASSERT_PRECONDITION( mExitRequested == 0 || mExitRequested == EXIT_REQUESTED,
|
||||
@@ -126,9 +115,6 @@ void CommandBufferQueue::flush() noexcept {
|
||||
#endif
|
||||
|
||||
SYSTRACE_NAME("waiting: CircularBuffer::flush()");
|
||||
ASSERT_POSTCONDITION(!mPaused,
|
||||
"CommandStream is full, but since the rendering thread is paused, "
|
||||
"the buffer cannot flush and we will deadlock. Instead, abort.");
|
||||
mCondition.wait(lock, [this, requiredSize]() -> bool {
|
||||
// TODO: on macOS, we need to call pumpEvents from time to time
|
||||
return mFreeSpace >= requiredSize;
|
||||
@@ -141,7 +127,7 @@ std::vector<CommandBufferQueue::Range> CommandBufferQueue::waitForCommands() con
|
||||
return std::move(mCommandBuffersToExecute);
|
||||
}
|
||||
std::unique_lock<utils::Mutex> lock(mLock);
|
||||
while ((mCommandBuffersToExecute.empty() || mPaused) && !mExitRequested) {
|
||||
while (mCommandBuffersToExecute.empty() && !mExitRequested) {
|
||||
mCondition.wait(lock);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,25 +49,21 @@ struct AcquiredImage;
|
||||
struct HwBase {
|
||||
};
|
||||
|
||||
|
||||
struct HwVertexBufferInfo : public HwBase {
|
||||
struct HwVertexBuffer : public HwBase {
|
||||
AttributeArray attributes{}; // 8 * MAX_VERTEX_ATTRIBUTE_COUNT
|
||||
uint32_t vertexCount{}; // 4
|
||||
uint8_t bufferCount{}; // 1
|
||||
uint8_t attributeCount{}; // 1
|
||||
bool padding[2]{}; // 2
|
||||
HwVertexBufferInfo() noexcept = default;
|
||||
HwVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount) noexcept
|
||||
: bufferCount(bufferCount),
|
||||
attributeCount(attributeCount) {
|
||||
}
|
||||
};
|
||||
bool padding{}; // 1
|
||||
uint8_t bufferObjectsVersion{}; // 1 -> total struct is 136 bytes
|
||||
|
||||
struct HwVertexBuffer : public HwBase {
|
||||
uint32_t vertexCount{}; // 4
|
||||
uint8_t bufferObjectsVersion{0xff}; // 1
|
||||
bool padding[3]{}; // 2
|
||||
HwVertexBuffer() noexcept = default;
|
||||
explicit HwVertexBuffer(uint32_t vertextCount) noexcept
|
||||
: vertexCount(vertextCount) {
|
||||
HwVertexBuffer(uint8_t bufferCount, uint8_t attributeCount, uint32_t elementCount,
|
||||
AttributeArray const& attributes) noexcept
|
||||
: attributes(attributes),
|
||||
vertexCount(elementCount),
|
||||
bufferCount(bufferCount),
|
||||
attributeCount(attributeCount) {
|
||||
}
|
||||
};
|
||||
|
||||
@@ -92,6 +88,11 @@ struct HwIndexBuffer : public HwBase {
|
||||
};
|
||||
|
||||
struct HwRenderPrimitive : public HwBase {
|
||||
uint32_t offset{};
|
||||
uint32_t minIndex{};
|
||||
uint32_t maxIndex{};
|
||||
uint32_t count{};
|
||||
uint32_t maxVertexCount{};
|
||||
PrimitiveType type = PrimitiveType::TRIANGLES;
|
||||
};
|
||||
|
||||
@@ -113,9 +114,7 @@ struct HwTexture : public HwBase {
|
||||
uint8_t levels : 4; // This allows up to 15 levels (max texture size of 32768 x 32768)
|
||||
uint8_t samples : 4; // Sample count per pixel (should always be a power of 2)
|
||||
TextureFormat format{};
|
||||
uint8_t reserved0 = 0;
|
||||
TextureUsage usage{};
|
||||
uint16_t reserved1 = 0;
|
||||
HwStream* hwStream = nullptr;
|
||||
|
||||
HwTexture() noexcept : levels{}, samples{} {}
|
||||
|
||||
@@ -39,10 +39,8 @@ using namespace utils;
|
||||
|
||||
template <size_t P0, size_t P1, size_t P2>
|
||||
UTILS_NOINLINE
|
||||
HandleAllocator<P0, P1, P2>::Allocator::Allocator(AreaPolicy::HeapArea const& area,
|
||||
bool disableUseAfterFreeCheck)
|
||||
: mArea(area),
|
||||
mUseAfterFreeCheckDisabled(disableUseAfterFreeCheck) {
|
||||
HandleAllocator<P0, P1, P2>::Allocator::Allocator(AreaPolicy::HeapArea const& area)
|
||||
: mArea(area) {
|
||||
|
||||
// The largest handle this allocator can generate currently depends on the architecture's
|
||||
// min alignment, typically 8 or 16 bytes.
|
||||
@@ -76,10 +74,8 @@ HandleAllocator<P0, P1, P2>::Allocator::Allocator(AreaPolicy::HeapArea const& ar
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
template <size_t P0, size_t P1, size_t P2>
|
||||
HandleAllocator<P0, P1, P2>::HandleAllocator(const char* name, size_t size,
|
||||
bool disableUseAfterFreeCheck) noexcept
|
||||
: mHandleArena(name, size, disableUseAfterFreeCheck),
|
||||
mUseAfterFreeCheckDisabled(disableUseAfterFreeCheck) {
|
||||
HandleAllocator<P0, P1, P2>::HandleAllocator(const char* name, size_t size) noexcept
|
||||
: mHandleArena(name, size) {
|
||||
}
|
||||
|
||||
template <size_t P0, size_t P1, size_t P2>
|
||||
|
||||
@@ -53,18 +53,4 @@ size_t Platform::retrieveBlob(void const* key, size_t keySize, void* value, size
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Platform::setDebugUpdateStatFunc(DebugUpdateStatFunc&& debugUpdateStat) noexcept {
|
||||
mDebugUpdateStat = std::move(debugUpdateStat);
|
||||
}
|
||||
|
||||
bool Platform::hasDebugUpdateStatFunc() const noexcept {
|
||||
return bool(mDebugUpdateStat);
|
||||
}
|
||||
|
||||
void Platform::debugUpdateStat(const char* key, uint64_t value) {
|
||||
if (mDebugUpdateStat) {
|
||||
mDebugUpdateStat(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -91,11 +91,6 @@ Program& Program::cacheId(uint64_t cacheId) noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Program& Program::multiview(bool multiview) noexcept {
|
||||
mMultiview = multiview;
|
||||
return *this;
|
||||
}
|
||||
|
||||
io::ostream& operator<<(io::ostream& out, const Program& builder) {
|
||||
out << "Program{";
|
||||
builder.mLogger(out);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#define TNT_FILAMENT_DRIVER_METALBUFFER_H
|
||||
|
||||
#include "MetalContext.h"
|
||||
#include "MetalBufferPool.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
@@ -27,50 +28,9 @@
|
||||
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class TrackedMetalBuffer {
|
||||
public:
|
||||
TrackedMetalBuffer() noexcept : mBuffer(nil) {}
|
||||
TrackedMetalBuffer(id<MTLBuffer> buffer) noexcept : mBuffer(buffer) {
|
||||
if (buffer) {
|
||||
aliveBuffers++;
|
||||
}
|
||||
}
|
||||
~TrackedMetalBuffer() {
|
||||
if (mBuffer) {
|
||||
aliveBuffers--;
|
||||
}
|
||||
}
|
||||
|
||||
TrackedMetalBuffer(TrackedMetalBuffer&&) = delete;
|
||||
TrackedMetalBuffer(TrackedMetalBuffer const&) = delete;
|
||||
TrackedMetalBuffer& operator=(TrackedMetalBuffer const&) = delete;
|
||||
|
||||
TrackedMetalBuffer& operator=(TrackedMetalBuffer&& rhs) noexcept {
|
||||
swap(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
id<MTLBuffer> get() const noexcept { return mBuffer; }
|
||||
operator bool() const noexcept { return bool(mBuffer); }
|
||||
|
||||
static uint64_t getAliveBuffers() { return aliveBuffers; }
|
||||
|
||||
private:
|
||||
void swap(TrackedMetalBuffer& other) noexcept {
|
||||
id<MTLBuffer> temp = mBuffer;
|
||||
mBuffer = other.mBuffer;
|
||||
other.mBuffer = temp;
|
||||
}
|
||||
|
||||
id<MTLBuffer> mBuffer;
|
||||
|
||||
static std::atomic<uint64_t> aliveBuffers;
|
||||
};
|
||||
|
||||
class MetalBuffer {
|
||||
public:
|
||||
|
||||
@@ -122,7 +82,7 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
TrackedMetalBuffer mBuffer;
|
||||
id<MTLBuffer> mBuffer = nil;
|
||||
size_t mBufferSize = 0;
|
||||
void* mCpuBuffer = nullptr;
|
||||
MetalContext& mContext;
|
||||
@@ -191,7 +151,7 @@ public:
|
||||
// finishes executing.
|
||||
mAuxBuffer = [mDevice newBufferWithLength:mSlotSizeBytes options:mBufferOptions];
|
||||
assert_invariant(mAuxBuffer);
|
||||
return {mAuxBuffer.get(), 0};
|
||||
return {mAuxBuffer, 0};
|
||||
}
|
||||
mCurrentSlot = (mCurrentSlot + 1) % mSlotCount;
|
||||
mOccupiedSlots->fetch_add(1, std::memory_order_relaxed);
|
||||
@@ -220,9 +180,9 @@ public:
|
||||
*/
|
||||
std::pair<id<MTLBuffer>, NSUInteger> getCurrentAllocation() const {
|
||||
if (UTILS_UNLIKELY(mAuxBuffer)) {
|
||||
return { mAuxBuffer.get(), 0 };
|
||||
return { mAuxBuffer, 0 };
|
||||
}
|
||||
return { mBuffer.get(), mCurrentSlot * mSlotSizeBytes };
|
||||
return { mBuffer, mCurrentSlot * mSlotSizeBytes };
|
||||
}
|
||||
|
||||
bool canAccomodateLayout(MTLSizeAndAlign layout) const {
|
||||
@@ -231,8 +191,8 @@ public:
|
||||
|
||||
private:
|
||||
id<MTLDevice> mDevice;
|
||||
TrackedMetalBuffer mBuffer;
|
||||
TrackedMetalBuffer mAuxBuffer;
|
||||
id<MTLBuffer> mBuffer;
|
||||
id<MTLBuffer> mAuxBuffer;
|
||||
|
||||
MTLResourceOptions mBufferOptions;
|
||||
|
||||
|
||||
@@ -15,15 +15,12 @@
|
||||
*/
|
||||
|
||||
#include "MetalBuffer.h"
|
||||
#include "MetalBufferPool.h"
|
||||
|
||||
#include "MetalContext.h"
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
|
||||
std::atomic<uint64_t> TrackedMetalBuffer::aliveBuffers = 0;
|
||||
|
||||
MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType, BufferUsage usage,
|
||||
size_t size, bool forceGpuBuffer) : mBufferSize(size), mContext(context) {
|
||||
// If the buffer is less than 4K in size and is updated frequently, we don't use an explicit
|
||||
@@ -64,7 +61,7 @@ void MetalBuffer::copyIntoBuffer(void* src, size_t size, size_t byteOffset) {
|
||||
// Acquire a staging buffer to hold the contents of this update.
|
||||
MetalBufferPool* bufferPool = mContext.bufferPool;
|
||||
const MetalBufferPoolEntry* const staging = bufferPool->acquireBuffer(size);
|
||||
memcpy(staging->buffer.get().contents, src, size);
|
||||
memcpy(staging->buffer.contents, src, size);
|
||||
|
||||
// The blit below requires that byteOffset be a multiple of 4.
|
||||
ASSERT_PRECONDITION(!(byteOffset & 0x3u), "byteOffset must be a multiple of 4");
|
||||
@@ -73,9 +70,9 @@ void MetalBuffer::copyIntoBuffer(void* src, size_t size, size_t byteOffset) {
|
||||
id<MTLCommandBuffer> cmdBuffer = getPendingCommandBuffer(&mContext);
|
||||
id<MTLBlitCommandEncoder> blitEncoder = [cmdBuffer blitCommandEncoder];
|
||||
blitEncoder.label = @"Buffer upload blit";
|
||||
[blitEncoder copyFromBuffer:staging->buffer.get()
|
||||
[blitEncoder copyFromBuffer:staging->buffer
|
||||
sourceOffset:0
|
||||
toBuffer:mBuffer.get()
|
||||
toBuffer:mBuffer
|
||||
destinationOffset:byteOffset
|
||||
size:size];
|
||||
[blitEncoder endEncoding];
|
||||
@@ -96,7 +93,7 @@ id<MTLBuffer> MetalBuffer::getGpuBufferForDraw(id<MTLCommandBuffer> cmdBuffer) n
|
||||
return nil;
|
||||
}
|
||||
assert_invariant(mBuffer);
|
||||
return mBuffer.get();
|
||||
return mBuffer;
|
||||
}
|
||||
|
||||
void MetalBuffer::bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLCommandEncoder> encoder,
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
|
||||
#include <Metal/Metal.h>
|
||||
|
||||
#include "MetalBuffer.h"
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <unordered_set>
|
||||
@@ -32,7 +30,7 @@ struct MetalContext;
|
||||
|
||||
// Immutable POD representing a shared CPU-GPU buffer.
|
||||
struct MetalBufferPoolEntry {
|
||||
TrackedMetalBuffer buffer;
|
||||
id<MTLBuffer> buffer;
|
||||
size_t capacity;
|
||||
mutable uint64_t lastAccessed;
|
||||
mutable uint32_t referenceCount;
|
||||
|
||||
@@ -45,12 +45,12 @@ MetalBufferPoolEntry const* MetalBufferPool::acquireBuffer(size_t numBytes) {
|
||||
id<MTLBuffer> buffer = [mContext.device newBufferWithLength:numBytes
|
||||
options:MTLResourceStorageModeShared];
|
||||
ASSERT_POSTCONDITION(buffer, "Could not allocate Metal staging buffer of size %zu.", numBytes);
|
||||
MetalBufferPoolEntry* stage = new MetalBufferPoolEntry {
|
||||
MetalBufferPoolEntry* stage = new MetalBufferPoolEntry({
|
||||
.buffer = buffer,
|
||||
.capacity = numBytes,
|
||||
.lastAccessed = mCurrentFrame,
|
||||
.referenceCount = 1
|
||||
};
|
||||
});
|
||||
mUsedStages.insert(stage);
|
||||
|
||||
return stage;
|
||||
|
||||
@@ -99,7 +99,6 @@ struct MetalContext {
|
||||
std::array<BufferState, MAX_SSBO_COUNT> ssboState;
|
||||
CullModeStateTracker cullModeState;
|
||||
WindingStateTracker windingState;
|
||||
Handle<HwRenderPrimitive> currentRenderPrimitive;
|
||||
|
||||
// State caches.
|
||||
DepthStencilStateCache depthStencilStateCache;
|
||||
|
||||
@@ -133,9 +133,12 @@ private:
|
||||
mHandleAllocator.deallocate(handle, p);
|
||||
}
|
||||
|
||||
inline void setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
|
||||
inline void setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph,
|
||||
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh);
|
||||
|
||||
inline void setRenderPrimitiveRange(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
|
||||
uint32_t offset, uint32_t minIndex, uint32_t maxIndex, uint32_t count);
|
||||
|
||||
void finalizeSamplerGroup(MetalSamplerGroup* sg);
|
||||
void enumerateBoundBuffers(BufferObjectBinding bindingType,
|
||||
const std::function<void(const BufferState&, MetalBuffer*, uint32_t)>& f);
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "metal/MetalDriver.h"
|
||||
|
||||
#include "MetalBlitter.h"
|
||||
#include "MetalBufferPool.h"
|
||||
#include "MetalContext.h"
|
||||
#include "MetalDriverFactory.h"
|
||||
#include "MetalEnums.h"
|
||||
@@ -37,7 +36,6 @@
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/sstream.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -50,26 +48,24 @@ Driver* MetalDriverFactory::create(MetalPlatform* const platform, const Platform
|
||||
// For reference on a 64-bits machine in Release mode:
|
||||
// MetalTimerQuery : 16 few
|
||||
// HwStream : 24 few
|
||||
// MetalRenderPrimitive : 24 many
|
||||
// MetalVertexBuffer : 32 moderate
|
||||
// -- less than or equal 32 bytes
|
||||
// MetalIndexBuffer : 40 moderate
|
||||
// MetalFence : 48 few
|
||||
// MetalBufferObject : 48 many
|
||||
// -- less than or equal 48 bytes
|
||||
// MetalSamplerGroup : 112 few
|
||||
// MetalProgram : 152 moderate
|
||||
// MetalProgram : 144 moderate
|
||||
// MetalTexture : 152 moderate
|
||||
// MetalVertexBuffer : 152 moderate
|
||||
// -- less than or equal 160 bytes
|
||||
// MetalSwapChain : 184 few
|
||||
// MetalRenderTarget : 272 few
|
||||
// MetalVertexBufferInfo : 552 moderate
|
||||
// -- less than or equal to 552 bytes
|
||||
// MetalRenderPrimitive : 584 many
|
||||
// -- less than or equal to 592 bytes
|
||||
|
||||
utils::slog.d
|
||||
<< "\nMetalSwapChain: " << sizeof(MetalSwapChain)
|
||||
<< "\nMetalBufferObject: " << sizeof(MetalBufferObject)
|
||||
<< "\nMetalVertexBuffer: " << sizeof(MetalVertexBuffer)
|
||||
<< "\nMetalVertexBufferInfo: " << sizeof(MetalVertexBufferInfo)
|
||||
<< "\nMetalIndexBuffer: " << sizeof(MetalIndexBuffer)
|
||||
<< "\nMetalSamplerGroup: " << sizeof(MetalSamplerGroup)
|
||||
<< "\nMetalRenderPrimitive: " << sizeof(MetalRenderPrimitive)
|
||||
@@ -100,9 +96,7 @@ Dispatcher MetalDriver::getDispatcher() const noexcept {
|
||||
MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig& driverConfig) noexcept
|
||||
: mPlatform(*platform),
|
||||
mContext(new MetalContext(driverConfig.textureUseAfterFreePoolSize)),
|
||||
mHandleAllocator("Handles",
|
||||
driverConfig.handleArenaSize,
|
||||
driverConfig.disableHandleUseAfterFreeCheck) {
|
||||
mHandleAllocator("Handles", driverConfig.handleArenaSize) {
|
||||
mContext->driver = this;
|
||||
|
||||
mContext->device = mPlatform.createDevice();
|
||||
@@ -184,10 +178,7 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
|
||||
mContext->eventListener = [[MTLSharedEventListener alloc] initWithDispatchQueue:queue];
|
||||
}
|
||||
|
||||
const MetalShaderCompiler::Mode compilerMode = driverConfig.disableParallelShaderCompile
|
||||
? MetalShaderCompiler::Mode::SYNCHRONOUS
|
||||
: MetalShaderCompiler::Mode::ASYNCHRONOUS;
|
||||
mContext->shaderCompiler = new MetalShaderCompiler(mContext->device, *this, compilerMode);
|
||||
mContext->shaderCompiler = new MetalShaderCompiler(mContext->device, *this);
|
||||
mContext->shaderCompiler->init();
|
||||
|
||||
#if defined(FILAMENT_METAL_PROFILING)
|
||||
@@ -216,9 +207,6 @@ void MetalDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) {
|
||||
#if defined(FILAMENT_METAL_PROFILING)
|
||||
os_signpost_interval_begin(mContext->log, mContext->signpostId, "Frame encoding", "%{public}d", frameId);
|
||||
#endif
|
||||
if (mPlatform.hasDebugUpdateStatFunc()) {
|
||||
mPlatform.debugUpdateStat("filament.metal.alive_buffers", TrackedMetalBuffer::getAliveBuffers());
|
||||
}
|
||||
}
|
||||
|
||||
void MetalDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
|
||||
@@ -291,16 +279,10 @@ void MetalDriver::finish(int) {
|
||||
[oneOffBuffer waitUntilCompleted];
|
||||
}
|
||||
|
||||
void MetalDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
|
||||
uint8_t attributeCount, AttributeArray attributes) {
|
||||
construct_handle<MetalVertexBufferInfo>(vbih, *mContext,
|
||||
bufferCount, attributeCount, attributes);
|
||||
}
|
||||
|
||||
void MetalDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh,
|
||||
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih) {
|
||||
MetalVertexBufferInfo const* const vbi = handle_cast<const MetalVertexBufferInfo>(vbih);
|
||||
construct_handle<MetalVertexBuffer>(vbh, *mContext, vertexCount, vbi->bufferCount, vbih);
|
||||
void MetalDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint8_t bufferCount,
|
||||
uint8_t attributeCount, uint32_t vertexCount, AttributeArray attributes) {
|
||||
construct_handle<MetalVertexBuffer>(vbh, *mContext, bufferCount,
|
||||
attributeCount, vertexCount, attributes);
|
||||
}
|
||||
|
||||
void MetalDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
|
||||
@@ -368,9 +350,11 @@ void MetalDriver::createSamplerGroupR(
|
||||
|
||||
void MetalDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
|
||||
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
|
||||
PrimitiveType pt) {
|
||||
PrimitiveType pt, uint32_t offset,
|
||||
uint32_t minIndex, uint32_t maxIndex, uint32_t count) {
|
||||
construct_handle<MetalRenderPrimitive>(rph);
|
||||
MetalDriver::setRenderPrimitiveBuffer(rph, pt, vbh, ibh);
|
||||
MetalDriver::setRenderPrimitiveBuffer(rph, vbh, ibh);
|
||||
MetalDriver::setRenderPrimitiveRange(rph, pt, offset, minIndex, maxIndex, count);
|
||||
}
|
||||
|
||||
void MetalDriver::createProgramR(Handle<HwProgram> rph, Program&& program) {
|
||||
@@ -383,7 +367,7 @@ void MetalDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int dum
|
||||
|
||||
void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height,
|
||||
uint8_t samples, uint8_t layerCount, MRT color,
|
||||
uint8_t samples, MRT color,
|
||||
TargetBufferInfo depth, TargetBufferInfo stencil) {
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"createRenderTarget must be called outside of a render pass.");
|
||||
@@ -456,10 +440,6 @@ void MetalDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
|
||||
// nothing to do, timer query was constructed in createTimerQueryS
|
||||
}
|
||||
|
||||
Handle<HwVertexBufferInfo> MetalDriver::createVertexBufferInfoS() noexcept {
|
||||
return alloc_handle<MetalVertexBufferInfo>();
|
||||
}
|
||||
|
||||
Handle<HwVertexBuffer> MetalDriver::createVertexBufferS() noexcept {
|
||||
return alloc_handle<MetalVertexBuffer>();
|
||||
}
|
||||
@@ -524,12 +504,6 @@ Handle<HwTimerQuery> MetalDriver::createTimerQueryS() noexcept {
|
||||
return alloc_and_construct_handle<MetalTimerQuery, HwTimerQuery>();
|
||||
}
|
||||
|
||||
void MetalDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
|
||||
if (vbih) {
|
||||
destruct_handle<MetalVertexBufferInfo>(vbih);
|
||||
}
|
||||
}
|
||||
|
||||
void MetalDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
|
||||
if (vbh) {
|
||||
destruct_handle<MetalVertexBuffer>(vbh);
|
||||
@@ -782,33 +756,18 @@ bool MetalDriver::isSRGBSwapChainSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MetalDriver::isProtectedContentSupported() {
|
||||
// the SWAP_CHAIN_CONFIG_PROTECTED_CONTENT flag is not supported
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MetalDriver::isStereoSupported(backend::StereoscopicType stereoscopicType) {
|
||||
switch (stereoscopicType) {
|
||||
case backend::StereoscopicType::INSTANCED:
|
||||
return true;
|
||||
case backend::StereoscopicType::MULTIVIEW:
|
||||
// TODO: implement multiview feature in Metal.
|
||||
return false;
|
||||
}
|
||||
bool MetalDriver::isStereoSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MetalDriver::isParallelShaderCompileSupported() {
|
||||
return mContext->shaderCompiler->isParallelShaderCompileSupported();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MetalDriver::isDepthStencilResolveSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MetalDriver::isProtectedTexturesSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
|
||||
switch (workaround) {
|
||||
case Workaround::SPLIT_EASU:
|
||||
@@ -935,10 +894,9 @@ void MetalDriver::setExternalImagePlane(Handle<HwTexture> th, void* image, uint3
|
||||
void MetalDriver::setExternalStream(Handle<HwTexture> th, Handle<HwStream> sh) {
|
||||
}
|
||||
|
||||
TimerQueryResult MetalDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
|
||||
bool MetalDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
|
||||
auto* tq = handle_cast<MetalTimerQuery>(tqh);
|
||||
return mContext->timerQueryImpl->getQueryResult(tq, elapsedTime) ?
|
||||
TimerQueryResult::AVAILABLE : TimerQueryResult::NOT_READY;
|
||||
return mContext->timerQueryImpl->getQueryResult(tq, elapsedTime);
|
||||
}
|
||||
|
||||
void MetalDriver::generateMipmaps(Handle<HwTexture> th) {
|
||||
@@ -1105,14 +1063,23 @@ void MetalDriver::endRenderPass(int dummy) {
|
||||
mContext->currentRenderPassEncoder = nil;
|
||||
}
|
||||
|
||||
void MetalDriver::setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
|
||||
void MetalDriver::setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph,
|
||||
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh) {
|
||||
auto primitive = handle_cast<MetalRenderPrimitive>(rph);
|
||||
auto vertexBuffer = handle_cast<MetalVertexBuffer>(vbh);
|
||||
auto indexBuffer = handle_cast<MetalIndexBuffer>(ibh);
|
||||
MetalVertexBufferInfo const* const vbi = handle_cast<MetalVertexBufferInfo>(vertexBuffer->vbih);
|
||||
primitive->setBuffers(vbi, vertexBuffer, indexBuffer);
|
||||
primitive->setBuffers(vertexBuffer, indexBuffer);
|
||||
}
|
||||
|
||||
void MetalDriver::setRenderPrimitiveRange(Handle<HwRenderPrimitive> rph,
|
||||
PrimitiveType pt, uint32_t offset, uint32_t minIndex, uint32_t maxIndex,
|
||||
uint32_t count) {
|
||||
auto primitive = handle_cast<MetalRenderPrimitive>(rph);
|
||||
primitive->type = pt;
|
||||
primitive->offset = offset * primitive->indexBuffer->elementSize;
|
||||
primitive->count = count;
|
||||
primitive->minIndex = minIndex;
|
||||
primitive->maxIndex = maxIndex > minIndex ? maxIndex : primitive->maxVertexCount - 1;
|
||||
}
|
||||
|
||||
void MetalDriver::makeCurrent(Handle<HwSwapChain> schDraw, Handle<HwSwapChain> schRead) {
|
||||
@@ -1614,13 +1581,10 @@ void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) {
|
||||
}
|
||||
}
|
||||
|
||||
void MetalDriver::bindPipeline(PipelineState ps) {
|
||||
void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t instanceCount) {
|
||||
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
|
||||
"bindPipeline() without a valid command encoder.");
|
||||
|
||||
MetalVertexBufferInfo const* const vbi =
|
||||
handle_cast<MetalVertexBufferInfo>(ps.vertexBufferInfo);
|
||||
|
||||
"Attempted to draw without a valid command encoder.");
|
||||
auto primitive = handle_cast<MetalRenderPrimitive>(rph);
|
||||
auto program = handle_cast<MetalProgram>(ps.program);
|
||||
const auto& rs = ps.rasterState;
|
||||
|
||||
@@ -1634,7 +1598,7 @@ void MetalDriver::bindPipeline(PipelineState ps) {
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT_PRECONDITION(bool(functions), "Attempting to bind an invalid Metal program.");
|
||||
ASSERT_PRECONDITION(bool(functions), "Attempting to draw with an invalid Metal program.");
|
||||
|
||||
auto [fragment, vertex] = functions.getRasterFunctions();
|
||||
|
||||
@@ -1661,7 +1625,7 @@ void MetalDriver::bindPipeline(PipelineState ps) {
|
||||
MetalPipelineState const pipelineState {
|
||||
.vertexFunction = vertex,
|
||||
.fragmentFunction = fragment,
|
||||
.vertexDescription = vbi->vertexDescription,
|
||||
.vertexDescription = primitive->vertexDescription,
|
||||
.colorAttachmentPixelFormat = {
|
||||
colorPixelFormat[0],
|
||||
colorPixelFormat[1],
|
||||
@@ -1755,6 +1719,50 @@ void MetalDriver::bindPipeline(PipelineState ps) {
|
||||
mContext->currentPolygonOffset = ps.polygonOffset;
|
||||
}
|
||||
|
||||
// Set scissor-rectangle.
|
||||
// In order to do this, we compute the intersection between:
|
||||
// 1. the scissor rectangle
|
||||
// 2. the render target attachment dimensions (important, as the scissor can't be set larger)
|
||||
// fmax/min are used below to guard against NaN and because the MTLViewport/MTLRegion
|
||||
// coordinates are doubles.
|
||||
MTLRegion scissor = mContext->currentRenderTarget->getRegionFromClientRect(ps.scissor);
|
||||
const float sleft = scissor.origin.x, sright = scissor.origin.x + scissor.size.width;
|
||||
const float stop = scissor.origin.y, sbottom = scissor.origin.y + scissor.size.height;
|
||||
|
||||
// Attachment extent
|
||||
const auto attachmentSize = mContext->currentRenderTarget->getAttachmentSize();
|
||||
const float aleft = 0.0f, atop = 0.0f;
|
||||
const float aright = static_cast<float>(attachmentSize.x);
|
||||
const float abottom = static_cast<float>(attachmentSize.y);
|
||||
|
||||
const auto left = std::fmax(sleft, aleft);
|
||||
const auto right = std::fmin(sright, aright);
|
||||
const auto top = std::fmax(stop, atop);
|
||||
const auto bottom = std::fmin(sbottom, abottom);
|
||||
|
||||
MTLScissorRect scissorRect = {
|
||||
.x = static_cast<NSUInteger>(left),
|
||||
.y = static_cast<NSUInteger>(top),
|
||||
.width = static_cast<NSUInteger>(right - left),
|
||||
.height = static_cast<NSUInteger>(bottom - top)
|
||||
};
|
||||
|
||||
[mContext->currentRenderPassEncoder setScissorRect:scissorRect];
|
||||
|
||||
// Bind uniform buffers.
|
||||
MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil };
|
||||
NSUInteger offsets[Program::UNIFORM_BINDING_COUNT] = { 0 };
|
||||
|
||||
enumerateBoundBuffers(BufferObjectBinding::UNIFORM,
|
||||
[&uniformsToBind, &offsets](const BufferState& state, MetalBuffer* buffer,
|
||||
uint32_t index) {
|
||||
uniformsToBind[index] = buffer;
|
||||
offsets[index] = state.offset;
|
||||
});
|
||||
MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder,
|
||||
UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX | MetalBuffer::Stage::FRAGMENT,
|
||||
uniformsToBind, offsets, Program::UNIFORM_BINDING_COUNT);
|
||||
|
||||
// Bind sampler groups (argument buffers).
|
||||
for (size_t s = 0; s < Program::SAMPLER_BINDING_COUNT; s++) {
|
||||
MetalSamplerGroup* const samplerGroup = mContext->samplerBindings[s];
|
||||
@@ -1785,29 +1793,19 @@ void MetalDriver::bindPipeline(PipelineState ps) {
|
||||
atIndex:(SAMPLER_GROUP_BINDING_START + s)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MetalDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
|
||||
"bindRenderPrimitive() without a valid command encoder.");
|
||||
|
||||
// Bind the user vertex buffers.
|
||||
|
||||
MetalBuffer* vertexBuffers[MAX_VERTEX_BUFFER_COUNT] = {};
|
||||
size_t vertexBufferOffsets[MAX_VERTEX_BUFFER_COUNT] = {};
|
||||
size_t maxBufferIndex = 0;
|
||||
|
||||
MetalRenderPrimitive const* const primitive = handle_cast<MetalRenderPrimitive>(rph);
|
||||
MetalVertexBufferInfo const* const vbi =
|
||||
handle_cast<MetalVertexBufferInfo>(primitive->vertexBuffer->vbih);
|
||||
|
||||
mContext->currentRenderPrimitive = rph;
|
||||
|
||||
auto vb = primitive->vertexBuffer;
|
||||
for (auto m : vbi->bufferMapping) {
|
||||
for (auto m : primitive->bufferMapping) {
|
||||
assert_invariant(
|
||||
m.bufferArgumentIndex >= USER_VERTEX_BUFFER_BINDING_START &&
|
||||
m.bufferArgumentIndex < USER_VERTEX_BUFFER_BINDING_START + MAX_VERTEX_BUFFER_COUNT);
|
||||
size_t const vertexBufferIndex = m.bufferArgumentIndex - USER_VERTEX_BUFFER_BINDING_START;
|
||||
size_t vertexBufferIndex = m.bufferArgumentIndex - USER_VERTEX_BUFFER_BINDING_START;
|
||||
vertexBuffers[vertexBufferIndex] = vb->buffers[m.sourceBufferIndex];
|
||||
maxBufferIndex = std::max(maxBufferIndex, vertexBufferIndex);
|
||||
}
|
||||
@@ -1822,50 +1820,19 @@ void MetalDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
[mContext->currentRenderPassEncoder setVertexBytes:bytes
|
||||
length:16
|
||||
atIndex:ZERO_VERTEX_BUFFER_BINDING];
|
||||
}
|
||||
|
||||
void MetalDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
||||
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
|
||||
"draw() without a valid command encoder.");
|
||||
|
||||
// Bind uniform buffers.
|
||||
MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil };
|
||||
NSUInteger offsets[Program::UNIFORM_BINDING_COUNT] = { 0 };
|
||||
|
||||
enumerateBoundBuffers(BufferObjectBinding::UNIFORM,
|
||||
[&uniformsToBind, &offsets](const BufferState& state, MetalBuffer* buffer,
|
||||
uint32_t index) {
|
||||
uniformsToBind[index] = buffer;
|
||||
offsets[index] = state.offset;
|
||||
});
|
||||
MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder,
|
||||
UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX | MetalBuffer::Stage::FRAGMENT,
|
||||
uniformsToBind, offsets, Program::UNIFORM_BINDING_COUNT);
|
||||
|
||||
auto primitive = handle_cast<MetalRenderPrimitive>(mContext->currentRenderPrimitive);
|
||||
|
||||
MetalIndexBuffer* indexBuffer = primitive->indexBuffer;
|
||||
|
||||
id<MTLCommandBuffer> cmdBuffer = getPendingCommandBuffer(mContext);
|
||||
id<MTLBuffer> metalIndexBuffer = indexBuffer->buffer.getGpuBufferForDraw(cmdBuffer);
|
||||
[mContext->currentRenderPassEncoder drawIndexedPrimitives:getMetalPrimitiveType(primitive->type)
|
||||
indexCount:indexCount
|
||||
indexCount:primitive->count
|
||||
indexType:getIndexType(indexBuffer->elementSize)
|
||||
indexBuffer:metalIndexBuffer
|
||||
indexBufferOffset:indexOffset * primitive->indexBuffer->elementSize
|
||||
indexBufferOffset:primitive->offset
|
||||
instanceCount:instanceCount];
|
||||
}
|
||||
|
||||
void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
|
||||
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
|
||||
MetalRenderPrimitive const* const rp = handle_cast<MetalRenderPrimitive>(rph);
|
||||
ps.primitiveType = rp->type;
|
||||
ps.vertexBufferInfo = rp->vertexBuffer->vbih;
|
||||
bindPipeline(ps);
|
||||
bindRenderPrimitive(rph);
|
||||
draw2(indexOffset, indexCount, instanceCount);
|
||||
}
|
||||
|
||||
void MetalDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"dispatchCompute must be called outside of a render pass.");
|
||||
@@ -1935,38 +1902,6 @@ void MetalDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGro
|
||||
[computeEncoder endEncoding];
|
||||
}
|
||||
|
||||
void MetalDriver::scissor(Viewport scissorBox) {
|
||||
// Set scissor-rectangle.
|
||||
// In order to do this, we compute the intersection between:
|
||||
// 1. the scissor rectangle
|
||||
// 2. the render target attachment dimensions (important, as the scissor can't be set larger)
|
||||
// fmax/min are used below to guard against NaN and because the MTLViewport/MTLRegion
|
||||
// coordinates are doubles.
|
||||
MTLRegion scissor = mContext->currentRenderTarget->getRegionFromClientRect(scissorBox);
|
||||
const float sleft = scissor.origin.x, sright = scissor.origin.x + scissor.size.width;
|
||||
const float stop = scissor.origin.y, sbottom = scissor.origin.y + scissor.size.height;
|
||||
|
||||
// Attachment extent
|
||||
const auto attachmentSize = mContext->currentRenderTarget->getAttachmentSize();
|
||||
const float aleft = 0.0f, atop = 0.0f;
|
||||
const float aright = static_cast<float>(attachmentSize.x);
|
||||
const float abottom = static_cast<float>(attachmentSize.y);
|
||||
|
||||
const auto left = std::fmax(sleft, aleft);
|
||||
const auto right = std::fmin(sright, aright);
|
||||
const auto top = std::fmax(stop, atop);
|
||||
const auto bottom = std::fmin(sbottom, abottom);
|
||||
|
||||
MTLScissorRect scissorRect = {
|
||||
.x = static_cast<NSUInteger>(left),
|
||||
.y = static_cast<NSUInteger>(top),
|
||||
.width = static_cast<NSUInteger>(right - left),
|
||||
.height = static_cast<NSUInteger>(bottom - top)
|
||||
};
|
||||
|
||||
[mContext->currentRenderPassEncoder setScissorRect:scissorRect];
|
||||
}
|
||||
|
||||
void MetalDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"beginTimerQuery must be called outside of a render pass.");
|
||||
|
||||
@@ -145,9 +145,28 @@ private:
|
||||
MetalBuffer buffer;
|
||||
};
|
||||
|
||||
struct MetalVertexBufferInfo : public HwVertexBufferInfo {
|
||||
MetalVertexBufferInfo(MetalContext& context,
|
||||
uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes);
|
||||
struct MetalVertexBuffer : public HwVertexBuffer {
|
||||
MetalVertexBuffer(MetalContext& context, uint8_t bufferCount, uint8_t attributeCount,
|
||||
uint32_t vertexCount, AttributeArray const& attributes);
|
||||
|
||||
utils::FixedCapacityVector<MetalBuffer*> buffers;
|
||||
};
|
||||
|
||||
struct MetalIndexBuffer : public HwIndexBuffer {
|
||||
MetalIndexBuffer(MetalContext& context, BufferUsage usage, uint8_t elementSize,
|
||||
uint32_t indexCount);
|
||||
|
||||
MetalBuffer buffer;
|
||||
};
|
||||
|
||||
struct MetalRenderPrimitive : public HwRenderPrimitive {
|
||||
MetalRenderPrimitive();
|
||||
void setBuffers(MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer);
|
||||
// The pointers to MetalVertexBuffer and MetalIndexBuffer are "weak".
|
||||
// The MetalVertexBuffer and MetalIndexBuffer must outlive the MetalRenderPrimitive.
|
||||
|
||||
MetalVertexBuffer* vertexBuffer = nullptr;
|
||||
MetalIndexBuffer* indexBuffer = nullptr;
|
||||
|
||||
// This struct is used to create the pipeline description to describe vertex assembly.
|
||||
VertexDescription vertexDescription = {};
|
||||
@@ -166,32 +185,6 @@ struct MetalVertexBufferInfo : public HwVertexBufferInfo {
|
||||
utils::FixedCapacityVector<Entry> bufferMapping;
|
||||
};
|
||||
|
||||
struct MetalVertexBuffer : public HwVertexBuffer {
|
||||
MetalVertexBuffer(MetalContext& context,
|
||||
uint32_t vertexCount, uint32_t bufferCount, Handle<HwVertexBufferInfo> vbih);
|
||||
|
||||
Handle<HwVertexBufferInfo> vbih;
|
||||
utils::FixedCapacityVector<MetalBuffer*> buffers;
|
||||
};
|
||||
|
||||
struct MetalIndexBuffer : public HwIndexBuffer {
|
||||
MetalIndexBuffer(MetalContext& context, BufferUsage usage, uint8_t elementSize,
|
||||
uint32_t indexCount);
|
||||
|
||||
MetalBuffer buffer;
|
||||
};
|
||||
|
||||
struct MetalRenderPrimitive : public HwRenderPrimitive {
|
||||
MetalRenderPrimitive();
|
||||
void setBuffers(MetalVertexBufferInfo const* const vbi,
|
||||
MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer);
|
||||
// The pointers to MetalVertexBuffer and MetalIndexBuffer are "weak".
|
||||
// The MetalVertexBuffer and MetalIndexBuffer must outlive the MetalRenderPrimitive.
|
||||
|
||||
MetalVertexBuffer* vertexBuffer = nullptr;
|
||||
MetalIndexBuffer* indexBuffer = nullptr;
|
||||
};
|
||||
|
||||
class MetalProgram : public HwProgram {
|
||||
public:
|
||||
MetalProgram(MetalContext& context, Program&& program) noexcept;
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "MetalBlitter.h"
|
||||
#include "MetalEnums.h"
|
||||
#include "MetalUtils.h"
|
||||
#include "MetalBufferPool.h"
|
||||
|
||||
#include <filament/SwapChain.h>
|
||||
|
||||
@@ -341,12 +340,23 @@ void MetalBufferObject::updateBufferUnsynchronized(void* data, size_t size, uint
|
||||
buffer.copyIntoBufferUnsynchronized(data, size, byteOffset);
|
||||
}
|
||||
|
||||
MetalVertexBufferInfo::MetalVertexBufferInfo(MetalContext& context, uint8_t bufferCount,
|
||||
uint8_t attributeCount, AttributeArray const& attributes)
|
||||
: HwVertexBufferInfo(bufferCount, attributeCount),
|
||||
bufferMapping(utils::FixedCapacityVector<Entry>::with_capacity(MAX_VERTEX_BUFFER_COUNT)) {
|
||||
MetalVertexBuffer::MetalVertexBuffer(MetalContext& context, uint8_t bufferCount,
|
||||
uint8_t attributeCount, uint32_t vertexCount, AttributeArray const& attributes)
|
||||
: HwVertexBuffer(bufferCount, attributeCount, vertexCount, attributes), buffers(bufferCount, nullptr) {}
|
||||
|
||||
const size_t maxAttributeCount = attributes.size();
|
||||
MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uint8_t elementSize,
|
||||
uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount),
|
||||
buffer(context, BufferObjectBinding::VERTEX, usage, elementSize * indexCount, true) { }
|
||||
|
||||
MetalRenderPrimitive::MetalRenderPrimitive()
|
||||
: bufferMapping(utils::FixedCapacityVector<Entry>::with_capacity(MAX_VERTEX_BUFFER_COUNT)) {}
|
||||
|
||||
void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalIndexBuffer*
|
||||
indexBuffer) {
|
||||
this->vertexBuffer = vertexBuffer;
|
||||
this->indexBuffer = indexBuffer;
|
||||
|
||||
const size_t attributeCount = vertexBuffer->attributes.size();
|
||||
|
||||
auto& mapping = bufferMapping;
|
||||
mapping.clear();
|
||||
@@ -386,8 +396,8 @@ MetalVertexBufferInfo::MetalVertexBufferInfo(MetalContext& context, uint8_t buff
|
||||
}
|
||||
};
|
||||
|
||||
for (uint32_t attributeIndex = 0; attributeIndex < maxAttributeCount; attributeIndex++) {
|
||||
const auto& attribute = attributes[attributeIndex];
|
||||
for (uint32_t attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++) {
|
||||
const auto& attribute = vertexBuffer->attributes[attributeIndex];
|
||||
|
||||
// If the attribute is unused, bind it to the zero buffer. It's a Metal error for a shader
|
||||
// to read from missing vertex attributes.
|
||||
@@ -414,24 +424,6 @@ MetalVertexBufferInfo::MetalVertexBufferInfo(MetalContext& context, uint8_t buff
|
||||
}
|
||||
}
|
||||
|
||||
MetalVertexBuffer::MetalVertexBuffer(MetalContext& context,
|
||||
uint32_t vertexCount, uint32_t bufferCount, Handle<HwVertexBufferInfo> vbih)
|
||||
: HwVertexBuffer(vertexCount), vbih(vbih), buffers(bufferCount, nullptr) {
|
||||
}
|
||||
|
||||
MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uint8_t elementSize,
|
||||
uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount),
|
||||
buffer(context, BufferObjectBinding::VERTEX, usage, elementSize * indexCount, true) { }
|
||||
|
||||
MetalRenderPrimitive::MetalRenderPrimitive() {
|
||||
}
|
||||
|
||||
void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi,
|
||||
MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer) {
|
||||
this->vertexBuffer = vertexBuffer;
|
||||
this->indexBuffer = indexBuffer;
|
||||
}
|
||||
|
||||
MetalProgram::MetalProgram(MetalContext& context, Program&& program) noexcept
|
||||
: HwProgram(program.getName()), mContext(context) {
|
||||
|
||||
@@ -771,13 +763,13 @@ void MetalTexture::loadWithCopyBuffer(uint32_t level, uint32_t slice, MTLRegion
|
||||
PixelBufferDescriptor const& data, const PixelBufferShape& shape) {
|
||||
const size_t stagingBufferSize = shape.totalBytes;
|
||||
auto entry = context.bufferPool->acquireBuffer(stagingBufferSize);
|
||||
memcpy(entry->buffer.get().contents,
|
||||
memcpy(entry->buffer.contents,
|
||||
static_cast<uint8_t*>(data.buffer) + shape.sourceOffset,
|
||||
stagingBufferSize);
|
||||
id<MTLCommandBuffer> blitCommandBuffer = getPendingCommandBuffer(&context);
|
||||
id<MTLBlitCommandEncoder> blitCommandEncoder = [blitCommandBuffer blitCommandEncoder];
|
||||
blitCommandEncoder.label = @"Texture upload buffer blit";
|
||||
[blitCommandEncoder copyFromBuffer:entry->buffer.get()
|
||||
[blitCommandEncoder copyFromBuffer:entry->buffer
|
||||
sourceOffset:0
|
||||
sourceBytesPerRow:shape.bytesPerRow
|
||||
sourceBytesPerImage:shape.bytesPerSlice
|
||||
|
||||
@@ -30,8 +30,6 @@
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <variant>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -41,81 +39,43 @@ class MetalShaderCompiler {
|
||||
struct MetalProgramToken;
|
||||
|
||||
public:
|
||||
enum class Mode {
|
||||
SYNCHRONOUS, // synchronous shader compilation
|
||||
ASYNCHRONOUS // asynchronous shader compilation
|
||||
};
|
||||
|
||||
class MetalFunctionBundle {
|
||||
public:
|
||||
using Raster = std::tuple<id<MTLFunction>, id<MTLFunction>>;
|
||||
using Compute = id<MTLFunction>;
|
||||
using Error = std::tuple<NSString*, NSString*>; // error message, Program name
|
||||
struct None {};
|
||||
|
||||
MetalFunctionBundle() : mPrograms{None{}} {}
|
||||
|
||||
explicit operator bool() const { return isValid(); }
|
||||
|
||||
bool isValid() const noexcept {
|
||||
return std::holds_alternative<Raster>(mPrograms) ||
|
||||
std::holds_alternative<Compute>(mPrograms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an NSException if this MetalFunctionBundle either contains an error or is empty.
|
||||
*
|
||||
* If this MetalFunctionBundle contains an error, will throw a MetalCompilationFailure
|
||||
* NSException with the error string and program name passed to
|
||||
* MetalFunctionBundle::error(NSString*, NSString*).
|
||||
*
|
||||
* If this MetalFunctionBundle is empty, will throw a MetalEmptyFunctionBundle NSException.
|
||||
*/
|
||||
void validate() const;
|
||||
|
||||
Raster getRasterFunctions() const {
|
||||
assert_invariant(std::holds_alternative<Raster>(mPrograms));
|
||||
return std::get<Raster>(mPrograms);
|
||||
}
|
||||
|
||||
Compute getComputeFunction() const {
|
||||
assert_invariant(std::holds_alternative<Compute>(mPrograms));
|
||||
return std::get<Compute>(mPrograms);
|
||||
}
|
||||
|
||||
static MetalFunctionBundle none() {
|
||||
return MetalFunctionBundle(None{});
|
||||
}
|
||||
|
||||
static MetalFunctionBundle raster(id<MTLFunction> fragment, id<MTLFunction> vertex) {
|
||||
MetalFunctionBundle() = default;
|
||||
MetalFunctionBundle(id<MTLFunction> fragment, id<MTLFunction> vertex)
|
||||
: functions{fragment, vertex} {
|
||||
assert_invariant(fragment && vertex);
|
||||
assert_invariant(fragment.functionType == MTLFunctionTypeFragment);
|
||||
assert_invariant(vertex.functionType == MTLFunctionTypeVertex);
|
||||
return MetalFunctionBundle(Raster{fragment, vertex});
|
||||
}
|
||||
|
||||
static MetalFunctionBundle compute(id<MTLFunction> compute) {
|
||||
explicit MetalFunctionBundle(id<MTLFunction> compute) : functions{compute, nil} {
|
||||
assert_invariant(compute);
|
||||
assert_invariant(compute.functionType == MTLFunctionTypeKernel);
|
||||
return MetalFunctionBundle(Compute{compute});
|
||||
}
|
||||
|
||||
static MetalFunctionBundle error(NSString* errorMessage, NSString* programName) {
|
||||
return MetalFunctionBundle(Error{errorMessage, programName});
|
||||
std::pair<id<MTLFunction>, id<MTLFunction>> getRasterFunctions() const noexcept {
|
||||
assert_invariant(functions[0].functionType == MTLFunctionTypeFragment);
|
||||
assert_invariant(functions[1].functionType == MTLFunctionTypeVertex);
|
||||
return {functions[0], functions[1]};
|
||||
}
|
||||
|
||||
id<MTLFunction> getComputeFunction() const noexcept {
|
||||
assert_invariant(functions[0].functionType == MTLFunctionTypeKernel);
|
||||
return functions[0];
|
||||
}
|
||||
|
||||
explicit operator bool() const { return functions[0] != nil; }
|
||||
|
||||
private:
|
||||
MetalFunctionBundle(None&& t) : mPrograms(std::move(t)) {}
|
||||
MetalFunctionBundle(Raster&& t) : mPrograms(std::move(t)) {}
|
||||
MetalFunctionBundle(Compute&& t) : mPrograms(std::move(t)) {}
|
||||
MetalFunctionBundle(Error&& t) : mPrograms(std::move(t)) {}
|
||||
|
||||
std::variant<Raster, Compute, None, Error> mPrograms;
|
||||
// Can hold two functions, either:
|
||||
// - fragment and vertex (for rasterization pipelines)
|
||||
// - compute (for compute pipelines)
|
||||
id<MTLFunction> functions[2] = {nil, nil};
|
||||
};
|
||||
|
||||
using program_token_t = std::shared_ptr<MetalProgramToken>;
|
||||
|
||||
explicit MetalShaderCompiler(id<MTLDevice> device, MetalDriver& driver, Mode mode);
|
||||
explicit MetalShaderCompiler(id<MTLDevice> device, MetalDriver& driver);
|
||||
|
||||
MetalShaderCompiler(MetalShaderCompiler const& rhs) = delete;
|
||||
MetalShaderCompiler(MetalShaderCompiler&& rhs) = delete;
|
||||
@@ -125,15 +85,15 @@ public:
|
||||
void init() noexcept;
|
||||
void terminate() noexcept;
|
||||
|
||||
bool isParallelShaderCompileSupported() const noexcept;
|
||||
|
||||
// Creates a program, either synchronously or asynchronously, depending on the Mode
|
||||
// MetalShaderCompiler was constructed with.
|
||||
// Creates a program asynchronously
|
||||
program_token_t createProgram(utils::CString const& name, Program&& program);
|
||||
|
||||
// Returns the functions, blocking if necessary. The Token is destroyed and becomes invalid.
|
||||
MetalFunctionBundle getProgram(program_token_t& token);
|
||||
|
||||
// Destroys a valid token and all associated resources. Used to "cancel" a program compilation.
|
||||
static void terminate(program_token_t& token);
|
||||
|
||||
void notifyWhenAllProgramsAreReady(
|
||||
CallbackHandler* handler, CallbackHandler::Callback callback, void* user);
|
||||
|
||||
@@ -143,7 +103,6 @@ private:
|
||||
CompilerThreadPool mCompilerThreadPool;
|
||||
id<MTLDevice> mDevice;
|
||||
CallbackManager mCallbackManager;
|
||||
Mode mMode;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -70,31 +70,22 @@ struct MetalShaderCompiler::MetalProgramToken : ProgramToken {
|
||||
|
||||
MetalShaderCompiler::MetalProgramToken::~MetalProgramToken() = default;
|
||||
|
||||
MetalShaderCompiler::MetalShaderCompiler(id<MTLDevice> device, MetalDriver& driver, Mode mode)
|
||||
MetalShaderCompiler::MetalShaderCompiler(id<MTLDevice> device, MetalDriver& driver)
|
||||
: mDevice(device),
|
||||
mCallbackManager(driver),
|
||||
mMode(mode) {
|
||||
mCallbackManager(driver) {
|
||||
|
||||
}
|
||||
|
||||
void MetalShaderCompiler::init() noexcept {
|
||||
const uint32_t poolSize = 1;
|
||||
if (mMode == Mode::ASYNCHRONOUS) {
|
||||
mCompilerThreadPool.init(poolSize, []() {}, []() {});
|
||||
}
|
||||
const uint32_t poolSize = 2;
|
||||
mCompilerThreadPool.init(poolSize, []() {}, []() {});
|
||||
}
|
||||
|
||||
void MetalShaderCompiler::terminate() noexcept {
|
||||
if (mMode == Mode::ASYNCHRONOUS) {
|
||||
mCompilerThreadPool.terminate();
|
||||
}
|
||||
mCompilerThreadPool.terminate();
|
||||
mCallbackManager.terminate();
|
||||
}
|
||||
|
||||
bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
|
||||
return mMode == Mode::ASYNCHRONOUS;
|
||||
}
|
||||
|
||||
/* static */ MetalShaderCompiler::MetalFunctionBundle MetalShaderCompiler::compileProgram(
|
||||
const Program& program, id<MTLDevice> device) {
|
||||
std::array<id<MTLFunction>, Program::SHADER_TYPE_COUNT> functions = { nil };
|
||||
@@ -127,16 +118,13 @@ bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
|
||||
options:options
|
||||
error:&error];
|
||||
if (library == nil) {
|
||||
NSString* errorMessage = @"unknown error";
|
||||
if (error) {
|
||||
auto description =
|
||||
[error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
utils::slog.w << description << utils::io::endl;
|
||||
errorMessage = error.localizedDescription;
|
||||
}
|
||||
PANIC_LOG("Failed to compile Metal program.");
|
||||
NSString* programName = [NSString stringWithFormat:@"%s", program.getName().c_str_safe()];
|
||||
return MetalFunctionBundle::error(errorMessage, programName);
|
||||
return {};
|
||||
}
|
||||
|
||||
MTLFunctionConstantValues* constants = [MTLFunctionConstantValues new];
|
||||
@@ -172,15 +160,14 @@ bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
|
||||
assert_invariant(isRasterizationProgram != isComputeProgram);
|
||||
|
||||
if (isRasterizationProgram) {
|
||||
return MetalFunctionBundle::raster(fragmentFunction, vertexFunction);
|
||||
return {fragmentFunction, vertexFunction};
|
||||
}
|
||||
|
||||
if (isComputeProgram) {
|
||||
return MetalFunctionBundle::compute(computeFunction);
|
||||
return MetalFunctionBundle{computeFunction};
|
||||
}
|
||||
|
||||
// Should never reach here.
|
||||
return MetalFunctionBundle::none();
|
||||
return {};
|
||||
}
|
||||
|
||||
MetalShaderCompiler::program_token_t MetalShaderCompiler::createProgram(
|
||||
@@ -189,26 +176,14 @@ MetalShaderCompiler::program_token_t MetalShaderCompiler::createProgram(
|
||||
|
||||
token->handle = mCallbackManager.get();
|
||||
|
||||
switch (mMode) {
|
||||
case Mode::ASYNCHRONOUS: {
|
||||
CompilerPriorityQueue const priorityQueue = program.getPriorityQueue();
|
||||
mCompilerThreadPool.queue(priorityQueue, token,
|
||||
[this, name, device = mDevice, program = std::move(program), token]() {
|
||||
MetalFunctionBundle compiledProgram = compileProgram(program, device);
|
||||
token->set(compiledProgram);
|
||||
mCallbackManager.put(token->handle);
|
||||
});
|
||||
CompilerPriorityQueue const priorityQueue = program.getPriorityQueue();
|
||||
mCompilerThreadPool.queue(priorityQueue, token,
|
||||
[this, name, device = mDevice, program = std::move(program), token]() {
|
||||
MetalFunctionBundle compiledProgram = compileProgram(program, device);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Mode::SYNCHRONOUS: {
|
||||
MetalFunctionBundle compiledProgram = compileProgram(program, mDevice);
|
||||
token->set(compiledProgram);
|
||||
mCallbackManager.put(token->handle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
token->set(compiledProgram);
|
||||
mCallbackManager.put(token->handle);
|
||||
});
|
||||
|
||||
return token;
|
||||
}
|
||||
@@ -216,46 +191,38 @@ MetalShaderCompiler::program_token_t MetalShaderCompiler::createProgram(
|
||||
MetalShaderCompiler::MetalFunctionBundle MetalShaderCompiler::getProgram(program_token_t& token) {
|
||||
assert_invariant(token);
|
||||
|
||||
if (mMode == Mode::ASYNCHRONOUS) {
|
||||
if (!token->isReady()) {
|
||||
auto job = mCompilerThreadPool.dequeue(token);
|
||||
if (job) {
|
||||
job();
|
||||
}
|
||||
if (!token->isReady()) {
|
||||
auto job = mCompilerThreadPool.dequeue(token);
|
||||
if (job) {
|
||||
job();
|
||||
}
|
||||
}
|
||||
|
||||
// The job isn't guaranteed to have finished yet. We may have failed to dequeue it above,
|
||||
// which means it's currently running. In that case get() will block until it finishes.
|
||||
|
||||
MetalShaderCompiler::MetalFunctionBundle program = token->get();
|
||||
|
||||
token = nullptr;
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
/* static */ void MetalShaderCompiler::terminate(program_token_t& token) {
|
||||
assert_invariant(token);
|
||||
|
||||
auto job = token->compiler.mCompilerThreadPool.dequeue(token);
|
||||
if (!job) {
|
||||
// The job is being executed right now (or has already executed).
|
||||
token->wait();
|
||||
} else {
|
||||
// The job has not executed yet.
|
||||
token->compiler.mCallbackManager.put(token->handle);
|
||||
}
|
||||
|
||||
token.reset();
|
||||
}
|
||||
|
||||
void MetalShaderCompiler::notifyWhenAllProgramsAreReady(
|
||||
CallbackHandler* handler, CallbackHandler::Callback callback, void* user) {
|
||||
mCallbackManager.setCallback(handler, callback, user);
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
void MetalShaderCompiler::MetalFunctionBundle::validate() const {
|
||||
if (UTILS_UNLIKELY(std::holds_alternative<Error>(mPrograms))) {
|
||||
auto [errorMessage, programName] = std::get<Error>(mPrograms);
|
||||
NSString* reason =
|
||||
[NSString stringWithFormat:
|
||||
@"Attempting to draw with an id<MTLFunction> that failed to compile.\n"
|
||||
@"Program: %@\n"
|
||||
@"%@", programName, errorMessage];
|
||||
[[NSException exceptionWithName:@"MetalCompilationFailure"
|
||||
reason:reason
|
||||
userInfo:nil] raise];
|
||||
} else if (UTILS_UNLIKELY(std::holds_alternative<None>(mPrograms))) {
|
||||
NSString* reason = @"Attempting to draw with an empty id<MTLFunction>.";
|
||||
[[NSException exceptionWithName:@"MetalEmptyFunctionBundle"
|
||||
reason:reason
|
||||
userInfo:nil] raise];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -77,9 +77,6 @@ void NoopDriver::finish(int) {
|
||||
void NoopDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
}
|
||||
|
||||
void NoopDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
|
||||
}
|
||||
|
||||
void NoopDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
|
||||
}
|
||||
|
||||
@@ -177,11 +174,7 @@ bool NoopDriver::isSRGBSwapChainSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NoopDriver::isProtectedContentSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NoopDriver::isStereoSupported(backend::StereoscopicType) {
|
||||
bool NoopDriver::isStereoSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -193,10 +186,6 @@ bool NoopDriver::isDepthStencilResolveSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NoopDriver::isProtectedTexturesSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NoopDriver::isWorkaroundNeeded(Workaround) {
|
||||
return false;
|
||||
}
|
||||
@@ -252,8 +241,8 @@ void NoopDriver::update3DImage(Handle<HwTexture> th,
|
||||
void NoopDriver::setupExternalImage(void* image) {
|
||||
}
|
||||
|
||||
TimerQueryResult NoopDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
|
||||
return TimerQueryResult::ERROR;
|
||||
bool NoopDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void NoopDriver::setExternalImage(Handle<HwTexture> th, void* image) {
|
||||
@@ -350,26 +339,13 @@ void NoopDriver::blit(
|
||||
math::uint2 size) {
|
||||
}
|
||||
|
||||
void NoopDriver::bindPipeline(PipelineState pipelineState) {
|
||||
}
|
||||
|
||||
void NoopDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
}
|
||||
|
||||
void NoopDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
||||
}
|
||||
|
||||
void NoopDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
|
||||
uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
||||
uint32_t instanceCount) {
|
||||
}
|
||||
|
||||
void NoopDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
|
||||
}
|
||||
|
||||
void NoopDriver::scissor(
|
||||
Viewport scissor) {
|
||||
}
|
||||
|
||||
void NoopDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
}
|
||||
|
||||
|
||||
@@ -16,25 +16,10 @@
|
||||
|
||||
#include "OpenGLContext.h"
|
||||
|
||||
#include "GLUtils.h"
|
||||
#include "OpenGLTimerQuery.h"
|
||||
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
// change to true to display all GL extensions in the console on start-up
|
||||
#define DEBUG_PRINT_EXTENSIONS false
|
||||
|
||||
@@ -63,8 +48,7 @@ bool OpenGLContext::queryOpenGLVersion(GLint* major, GLint* minor) noexcept {
|
||||
#endif
|
||||
}
|
||||
|
||||
OpenGLContext::OpenGLContext(OpenGLPlatform& platform) noexcept
|
||||
: mPlatform(platform) {
|
||||
OpenGLContext::OpenGLContext() noexcept {
|
||||
|
||||
state.vao.p = &mDefaultVAO;
|
||||
|
||||
@@ -247,49 +231,6 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform) noexcept
|
||||
glDebugMessageCallback(cb, nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
mTimerQueryFactory = TimerQueryFactory::init(platform, *this);
|
||||
}
|
||||
|
||||
OpenGLContext::~OpenGLContext() noexcept {
|
||||
delete mTimerQueryFactory;
|
||||
}
|
||||
|
||||
void OpenGLContext::destroyWithContext(
|
||||
size_t index, std::function<void(OpenGLContext&)> const& closure) noexcept {
|
||||
if (index == 0) {
|
||||
// Note: we only need to delay the destruction of objects on the unprotected context
|
||||
// (index 0) because the protected context is always immediately destroyed and all its
|
||||
// active objects and bindings are then automatically destroyed.
|
||||
// TODO: this is only guaranteed for EGLPlatform, but that's the only one we care about.
|
||||
mDestroyWithNormalContext.push_back(closure);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLContext::unbindEverything() noexcept {
|
||||
// TODO: we're supposed to unbind everything here so that resources don't get
|
||||
// stuck in this context (contextIndex) when destroyed in the other context.
|
||||
// However, because EGLPlatform always immediately destroys the protected context (1),
|
||||
// the bindings will automatically be severed when we switch back to the default context.
|
||||
// Since bindings now only exist in one context, we don't have a ref-counting issue to
|
||||
// worry about.
|
||||
}
|
||||
|
||||
void OpenGLContext::synchronizeStateAndCache(size_t index) noexcept {
|
||||
|
||||
// if we're just switching back to context 0, run all the pending destructors
|
||||
if (index == 0) {
|
||||
auto list = std::move(mDestroyWithNormalContext);
|
||||
for (auto&& fn: list) {
|
||||
fn(*this);
|
||||
}
|
||||
}
|
||||
|
||||
// the default FBO could be invalid
|
||||
mDefaultFbo[index].reset();
|
||||
|
||||
contextIndex = index;
|
||||
resetState();
|
||||
}
|
||||
|
||||
void OpenGLContext::setDefaultState() noexcept {
|
||||
@@ -658,7 +599,6 @@ void OpenGLContext::initExtensionsGLES(Extensions* ext, GLint major, GLint minor
|
||||
ext->EXT_disjoint_timer_query = exts.has("GL_EXT_disjoint_timer_query"sv);
|
||||
ext->EXT_multisampled_render_to_texture = exts.has("GL_EXT_multisampled_render_to_texture"sv);
|
||||
ext->EXT_multisampled_render_to_texture2 = exts.has("GL_EXT_multisampled_render_to_texture2"sv);
|
||||
ext->EXT_protected_textures = exts.has("GL_EXT_protected_textures"sv);
|
||||
#endif
|
||||
ext->EXT_shader_framebuffer_fetch = exts.has("GL_EXT_shader_framebuffer_fetch"sv);
|
||||
#ifndef __EMSCRIPTEN__
|
||||
@@ -682,7 +622,6 @@ void OpenGLContext::initExtensionsGLES(Extensions* ext, GLint major, GLint minor
|
||||
ext->OES_standard_derivatives = exts.has("GL_OES_standard_derivatives"sv);
|
||||
ext->OES_texture_npot = exts.has("GL_OES_texture_npot"sv);
|
||||
ext->OES_vertex_array_object = exts.has("GL_OES_vertex_array_object"sv);
|
||||
ext->OVR_multiview2 = exts.has("GL_OVR_multiview2"sv);
|
||||
ext->WEBGL_compressed_texture_etc = exts.has("WEBGL_compressed_texture_etc"sv);
|
||||
ext->WEBGL_compressed_texture_s3tc = exts.has("WEBGL_compressed_texture_s3tc"sv);
|
||||
ext->WEBGL_compressed_texture_s3tc_srgb = exts.has("WEBGL_compressed_texture_s3tc_srgb"sv);
|
||||
@@ -747,7 +686,6 @@ void OpenGLContext::initExtensionsGL(Extensions* ext, GLint major, GLint minor)
|
||||
ext->OES_standard_derivatives = true;
|
||||
ext->OES_texture_npot = true;
|
||||
ext->OES_vertex_array_object = true;
|
||||
ext->OVR_multiview2 = exts.has("GL_OVR_multiview2"sv);
|
||||
ext->WEBGL_compressed_texture_etc = false;
|
||||
ext->WEBGL_compressed_texture_s3tc = false;
|
||||
ext->WEBGL_compressed_texture_s3tc_srgb = false;
|
||||
@@ -769,51 +707,6 @@ void OpenGLContext::initExtensionsGL(Extensions* ext, GLint major, GLint minor)
|
||||
|
||||
#endif // BACKEND_OPENGL_VERSION_GL
|
||||
|
||||
|
||||
GLuint OpenGLContext::bindFramebuffer(GLenum target, GLuint buffer) noexcept {
|
||||
if (UTILS_UNLIKELY(buffer == 0)) {
|
||||
// we're binding the default frame buffer, resolve its actual name
|
||||
auto& defaultFboForThisContext = mDefaultFbo[contextIndex];
|
||||
if (UTILS_UNLIKELY(!defaultFboForThisContext.has_value())) {
|
||||
defaultFboForThisContext = GLuint(mPlatform.getDefaultFramebufferObject());
|
||||
}
|
||||
buffer = defaultFboForThisContext.value();
|
||||
}
|
||||
bindFramebufferResolved(target, buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void OpenGLContext::unbindFramebuffer(GLenum target) noexcept {
|
||||
bindFramebufferResolved(target, 0);
|
||||
}
|
||||
|
||||
void OpenGLContext::bindFramebufferResolved(GLenum target, GLuint buffer) noexcept {
|
||||
switch (target) {
|
||||
case GL_FRAMEBUFFER:
|
||||
if (state.draw_fbo != buffer || state.read_fbo != buffer) {
|
||||
state.draw_fbo = state.read_fbo = buffer;
|
||||
glBindFramebuffer(target, buffer);
|
||||
}
|
||||
break;
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
case GL_DRAW_FRAMEBUFFER:
|
||||
if (state.draw_fbo != buffer) {
|
||||
state.draw_fbo = buffer;
|
||||
glBindFramebuffer(target, buffer);
|
||||
}
|
||||
break;
|
||||
case GL_READ_FRAMEBUFFER:
|
||||
if (state.read_fbo != buffer) {
|
||||
state.read_fbo = buffer;
|
||||
glBindFramebuffer(target, buffer);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLContext::bindBuffer(GLenum target, GLuint buffer) noexcept {
|
||||
if (target == GL_ELEMENT_ARRAY_BUFFER) {
|
||||
constexpr size_t targetIndex = getIndexForBufferTarget(GL_ELEMENT_ARRAY_BUFFER);
|
||||
@@ -924,22 +817,19 @@ void OpenGLContext::deleteBuffers(GLsizei n, const GLuint* buffers, GLenum targe
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLContext::deleteVertexArray(GLuint vao) noexcept {
|
||||
if (UTILS_LIKELY(vao)) {
|
||||
procs.deleteVertexArrays(1, &vao);
|
||||
// if the destroyed VAO is bound, clear the binding.
|
||||
if (state.vao.p->vao[contextIndex] == vao) {
|
||||
void OpenGLContext::deleteVertexArrays(GLsizei n, const GLuint* arrays) noexcept {
|
||||
procs.deleteVertexArrays(n, arrays);
|
||||
// if one of the destroyed VAO is bound, clear the binding.
|
||||
for (GLsizei i = 0; i < n; ++i) {
|
||||
if (state.vao.p->vao == arrays[i]) {
|
||||
bindVertexArray(nullptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLContext::resetState() noexcept {
|
||||
// Force GL state to match the Filament state
|
||||
|
||||
// increase the state version so other parts of the state know to reset
|
||||
state.age++;
|
||||
|
||||
if (state.major > 2) {
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, state.draw_fbo);
|
||||
@@ -956,8 +846,11 @@ void OpenGLContext::resetState() noexcept {
|
||||
glUseProgram(state.program.use);
|
||||
|
||||
// state.vao
|
||||
state.vao.p = nullptr;
|
||||
bindVertexArray(nullptr);
|
||||
if (state.vao.p) {
|
||||
procs.bindVertexArray(state.vao.p->vao);
|
||||
} else {
|
||||
bindVertexArray(nullptr);
|
||||
}
|
||||
|
||||
// state.raster
|
||||
glFrontFace(state.raster.frontFace);
|
||||
@@ -1113,22 +1006,7 @@ void OpenGLContext::resetState() noexcept {
|
||||
state.window.viewport.w
|
||||
);
|
||||
glDepthRangef(state.window.depthRange.x, state.window.depthRange.y);
|
||||
}
|
||||
|
||||
void OpenGLContext::createTimerQuery(GLTimerQuery* query) {
|
||||
mTimerQueryFactory->createTimerQuery(query);
|
||||
}
|
||||
|
||||
void OpenGLContext::destroyTimerQuery(GLTimerQuery* query) {
|
||||
mTimerQueryFactory->destroyTimerQuery(query);
|
||||
}
|
||||
|
||||
void OpenGLContext::beginTimeElapsedQuery(GLTimerQuery* query) {
|
||||
mTimerQueryFactory->beginTimeElapsedQuery(query);
|
||||
}
|
||||
|
||||
void OpenGLContext::endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQuery* query) {
|
||||
mTimerQueryFactory->endTimeElapsedQuery(driver, query);
|
||||
|
||||
}
|
||||
|
||||
} // namesapce filament
|
||||
|
||||
@@ -17,37 +17,25 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_OPENGLCONTEXT_H
|
||||
#define TNT_FILAMENT_BACKEND_OPENGLCONTEXT_H
|
||||
|
||||
|
||||
#include "OpenGLTimerQuery.h"
|
||||
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include "gl_headers.h"
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/bitset.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include "GLUtils.h"
|
||||
|
||||
#include <array>
|
||||
#include <set>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class OpenGLPlatform;
|
||||
|
||||
class OpenGLContext final : public TimerQueryFactoryInterface {
|
||||
class OpenGLContext {
|
||||
public:
|
||||
static constexpr const size_t MAX_TEXTURE_UNIT_COUNT = MAX_SAMPLER_COUNT;
|
||||
static constexpr const size_t DUMMY_TEXTURE_BINDING = 7; // highest binding guaranteed to work with ES2
|
||||
@@ -58,29 +46,19 @@ public:
|
||||
struct RenderPrimitive {
|
||||
static_assert(MAX_VERTEX_ATTRIBUTE_COUNT <= 16);
|
||||
|
||||
GLuint vao[2] = {}; // 4
|
||||
GLuint vao = 0; // 4
|
||||
GLuint elementArray = 0; // 4
|
||||
mutable utils::bitset<uint16_t> vertexAttribArray; // 2
|
||||
utils::bitset<uint16_t> vertexAttribArray; // 2
|
||||
|
||||
// if this differs from vertexBufferWithObjects->bufferObjectsVersion, this VAO needs to
|
||||
// be updated (see OpenGLDriver::updateVertexArrayObject())
|
||||
// If this version number does not match vertexBufferWithObjects->bufferObjectsVersion,
|
||||
// then the VAO needs to be updated.
|
||||
uint8_t vertexBufferVersion = 0; // 1
|
||||
|
||||
// if this differs from OpenGLContext::state.age, this VAO needs to
|
||||
// be updated (see OpenGLDriver::updateVertexArrayObject())
|
||||
uint8_t stateVersion = 0; // 1
|
||||
|
||||
// If this differs from OpenGLContext::state.age, this VAO's name needs to be updated.
|
||||
// See OpenGLContext::bindVertexArray()
|
||||
uint8_t nameVersion = 0; // 1
|
||||
|
||||
// Size in bytes of indices in the index buffer
|
||||
uint8_t indicesSize = 0; // 1
|
||||
|
||||
// The optional 32-bit handle to a GLVertexBuffer is necessary only if the referenced
|
||||
// VertexBuffer supports buffer objects. If this is zero, then the VBO handles array is
|
||||
// immutable.
|
||||
Handle<HwVertexBuffer> vertexBufferWithObjects; // 4
|
||||
Handle<HwVertexBuffer> vertexBufferWithObjects = {}; // 4
|
||||
|
||||
GLenum getIndicesType() const noexcept {
|
||||
return indicesSize == 4 ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT;
|
||||
@@ -89,18 +67,7 @@ public:
|
||||
|
||||
static bool queryOpenGLVersion(GLint* major, GLint* minor) noexcept;
|
||||
|
||||
explicit OpenGLContext(OpenGLPlatform& platform) noexcept;
|
||||
~OpenGLContext() noexcept final;
|
||||
|
||||
// TimerQueryInterface ------------------------------------------------------------------------
|
||||
|
||||
// note: OpenGLContext being final ensures (clang) these are not called through the vtable
|
||||
void createTimerQuery(GLTimerQuery* query) override;
|
||||
void destroyTimerQuery(GLTimerQuery* query) override;
|
||||
void beginTimeElapsedQuery(GLTimerQuery* query) override;
|
||||
void endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQuery* query) override;
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
OpenGLContext() noexcept;
|
||||
|
||||
template<int MAJOR, int MINOR>
|
||||
inline bool isAtLeastGL() const noexcept {
|
||||
@@ -156,11 +123,10 @@ public:
|
||||
inline void bindBufferRange(GLenum target, GLuint index, GLuint buffer,
|
||||
GLintptr offset, GLsizeiptr size) noexcept;
|
||||
|
||||
GLuint bindFramebuffer(GLenum target, GLuint buffer) noexcept;
|
||||
void unbindFramebuffer(GLenum target) noexcept;
|
||||
inline void bindFramebuffer(GLenum target, GLuint buffer) noexcept;
|
||||
|
||||
inline void enableVertexAttribArray(RenderPrimitive const* rp, GLuint index) noexcept;
|
||||
inline void disableVertexAttribArray(RenderPrimitive const* rp, GLuint index) noexcept;
|
||||
inline void enableVertexAttribArray(GLuint index) noexcept;
|
||||
inline void disableVertexAttribArray(GLuint index) noexcept;
|
||||
inline void enable(GLenum cap) noexcept;
|
||||
inline void disable(GLenum cap) noexcept;
|
||||
inline void frontFace(GLenum mode) noexcept;
|
||||
@@ -182,9 +148,7 @@ public:
|
||||
inline void depthRange(GLclampf near, GLclampf far) noexcept;
|
||||
|
||||
void deleteBuffers(GLsizei n, const GLuint* buffers, GLenum target) noexcept;
|
||||
void deleteVertexArray(GLuint vao) noexcept;
|
||||
|
||||
void destroyWithContext(size_t index, std::function<void(OpenGLContext&)> const& closure) noexcept;
|
||||
void deleteVertexArrays(GLsizei n, const GLuint* arrays) noexcept;
|
||||
|
||||
// glGet*() values
|
||||
struct Gets {
|
||||
@@ -219,7 +183,6 @@ public:
|
||||
bool EXT_discard_framebuffer;
|
||||
bool EXT_multisampled_render_to_texture2;
|
||||
bool EXT_multisampled_render_to_texture;
|
||||
bool EXT_protected_textures;
|
||||
bool EXT_shader_framebuffer_fetch;
|
||||
bool EXT_texture_compression_bptc;
|
||||
bool EXT_texture_compression_etc2;
|
||||
@@ -242,7 +205,6 @@ public:
|
||||
bool OES_standard_derivatives;
|
||||
bool OES_texture_npot;
|
||||
bool OES_vertex_array_object;
|
||||
bool OVR_multiview2;
|
||||
bool WEBGL_compressed_texture_etc;
|
||||
bool WEBGL_compressed_texture_s3tc;
|
||||
bool WEBGL_compressed_texture_s3tc_srgb;
|
||||
@@ -328,19 +290,8 @@ public:
|
||||
|
||||
FeatureLevel getFeatureLevel() const noexcept { return mFeatureLevel; }
|
||||
|
||||
// This is the index of the context in use. Must be 0 or 1. This is used to manange the
|
||||
// OpenGL name of ContainerObjects within each context.
|
||||
uint32_t contextIndex = 0;
|
||||
|
||||
// Try to keep the State structure sorted by data-access patterns
|
||||
struct State {
|
||||
State() noexcept = default;
|
||||
// make sure we don't copy this state by accident
|
||||
State(State const& rhs) = delete;
|
||||
State(State&& rhs) noexcept = delete;
|
||||
State& operator=(State const& rhs) = delete;
|
||||
State& operator=(State&& rhs) noexcept = delete;
|
||||
|
||||
GLint major = 0;
|
||||
GLint minor = 0;
|
||||
|
||||
@@ -445,7 +396,6 @@ public:
|
||||
vec4gli viewport { 0 };
|
||||
vec2glf depthRange { 0.0f, 1.0f };
|
||||
} window;
|
||||
uint8_t age = 0;
|
||||
} state;
|
||||
|
||||
struct Procs {
|
||||
@@ -465,19 +415,9 @@ public:
|
||||
void (* maxShaderCompilerThreadsKHR)(GLuint count);
|
||||
} procs{};
|
||||
|
||||
void unbindEverything() noexcept;
|
||||
void synchronizeStateAndCache(size_t index) noexcept;
|
||||
|
||||
private:
|
||||
OpenGLPlatform& mPlatform;
|
||||
ShaderModel mShaderModel = ShaderModel::MOBILE;
|
||||
FeatureLevel mFeatureLevel = FeatureLevel::FEATURE_LEVEL_1;
|
||||
TimerQueryFactoryInterface* mTimerQueryFactory = nullptr;
|
||||
std::vector<std::function<void(OpenGLContext&)>> mDestroyWithNormalContext;
|
||||
RenderPrimitive mDefaultVAO;
|
||||
std::optional<GLuint> mDefaultFbo[2];
|
||||
|
||||
void bindFramebufferResolved(GLenum target, GLuint buffer) noexcept;
|
||||
|
||||
const std::array<std::tuple<bool const&, char const*, char const*>, sizeof(bugs)> mBugDatabase{{
|
||||
{ bugs.disable_glFlush,
|
||||
@@ -530,6 +470,8 @@ private:
|
||||
""},
|
||||
}};
|
||||
|
||||
RenderPrimitive mDefaultVAO;
|
||||
|
||||
// this is chosen to minimize code size
|
||||
#if defined(BACKEND_OPENGL_VERSION_GLES)
|
||||
static void initExtensionsGLES(Extensions* ext, GLint major, GLint minor) noexcept;
|
||||
@@ -687,26 +629,11 @@ void OpenGLContext::depthRange(GLclampf near, GLclampf far) noexcept {
|
||||
void OpenGLContext::bindVertexArray(RenderPrimitive const* p) noexcept {
|
||||
RenderPrimitive* vao = p ? const_cast<RenderPrimitive *>(p) : &mDefaultVAO;
|
||||
update_state(state.vao.p, vao, [&]() {
|
||||
|
||||
// See if we need to create a name for this VAO on the fly, this would happen if:
|
||||
// - we're not the default VAO, because its name is always 0
|
||||
// - our name is 0, this could happen if this VAO was created in the "other" context
|
||||
// - the nameVersion is out of date *and* we're on the protected context, in this case:
|
||||
// - the name must be stale from a previous use of this context because we always
|
||||
// destroy the protected context when we're done with it.
|
||||
bool const recreateVaoName = p != &mDefaultVAO &&
|
||||
((vao->vao[contextIndex] == 0) ||
|
||||
(vao->nameVersion != state.age && contextIndex == 1));
|
||||
if (UTILS_UNLIKELY(recreateVaoName)) {
|
||||
vao->nameVersion = state.age;
|
||||
procs.genVertexArrays(1, &vao->vao[contextIndex]);
|
||||
}
|
||||
|
||||
procs.bindVertexArray(vao->vao[contextIndex]);
|
||||
procs.bindVertexArray(vao->vao);
|
||||
// update GL_ELEMENT_ARRAY_BUFFER, which is updated by glBindVertexArray
|
||||
size_t const targetIndex = getIndexForBufferTarget(GL_ELEMENT_ARRAY_BUFFER);
|
||||
state.buffers.genericBinding[targetIndex] = vao->elementArray;
|
||||
if (UTILS_UNLIKELY(bugs.vao_doesnt_store_element_array_buffer_binding || recreateVaoName)) {
|
||||
if (UTILS_UNLIKELY(bugs.vao_doesnt_store_element_array_buffer_binding)) {
|
||||
// This shouldn't be needed, but it looks like some drivers don't do the implicit
|
||||
// glBindBuffer().
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vao->elementArray);
|
||||
@@ -744,6 +671,33 @@ void OpenGLContext::bindBufferRange(GLenum target, GLuint index, GLuint buffer,
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLContext::bindFramebuffer(GLenum target, GLuint buffer) noexcept {
|
||||
switch (target) {
|
||||
case GL_FRAMEBUFFER:
|
||||
if (state.draw_fbo != buffer || state.read_fbo != buffer) {
|
||||
state.draw_fbo = state.read_fbo = buffer;
|
||||
glBindFramebuffer(target, buffer);
|
||||
}
|
||||
break;
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
case GL_DRAW_FRAMEBUFFER:
|
||||
if (state.draw_fbo != buffer) {
|
||||
state.draw_fbo = buffer;
|
||||
glBindFramebuffer(target, buffer);
|
||||
}
|
||||
break;
|
||||
case GL_READ_FRAMEBUFFER:
|
||||
if (state.read_fbo != buffer) {
|
||||
state.read_fbo = buffer;
|
||||
glBindFramebuffer(target, buffer);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLContext::bindTexture(GLuint unit, GLuint target, GLuint texId, size_t targetIndex) noexcept {
|
||||
assert_invariant(targetIndex == getIndexForTextureTarget(target));
|
||||
assert_invariant(targetIndex < TEXTURE_TARGET_COUNT);
|
||||
@@ -763,22 +717,20 @@ void OpenGLContext::useProgram(GLuint program) noexcept {
|
||||
});
|
||||
}
|
||||
|
||||
void OpenGLContext::enableVertexAttribArray(RenderPrimitive const* rp, GLuint index) noexcept {
|
||||
assert_invariant(rp);
|
||||
assert_invariant(index < rp->vertexAttribArray.size());
|
||||
bool const force = rp->stateVersion != state.age;
|
||||
if (UTILS_UNLIKELY(force || !rp->vertexAttribArray[index])) {
|
||||
rp->vertexAttribArray.set(index);
|
||||
void OpenGLContext::enableVertexAttribArray(GLuint index) noexcept {
|
||||
assert_invariant(state.vao.p);
|
||||
assert_invariant(index < state.vao.p->vertexAttribArray.size());
|
||||
if (UTILS_UNLIKELY(!state.vao.p->vertexAttribArray[index])) {
|
||||
state.vao.p->vertexAttribArray.set(index);
|
||||
glEnableVertexAttribArray(index);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLContext::disableVertexAttribArray(RenderPrimitive const* rp, GLuint index) noexcept {
|
||||
assert_invariant(rp);
|
||||
assert_invariant(index < rp->vertexAttribArray.size());
|
||||
bool const force = rp->stateVersion != state.age;
|
||||
if (UTILS_UNLIKELY(force || rp->vertexAttribArray[index])) {
|
||||
rp->vertexAttribArray.unset(index);
|
||||
void OpenGLContext::disableVertexAttribArray(GLuint index) noexcept {
|
||||
assert_invariant(state.vao.p);
|
||||
assert_invariant(index < state.vao.p->vertexAttribArray.size());
|
||||
if (UTILS_UNLIKELY(state.vao.p->vertexAttribArray[index])) {
|
||||
state.vao.p->vertexAttribArray.unset(index);
|
||||
glDisableVertexAttribArray(index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,54 +16,21 @@
|
||||
|
||||
#include "OpenGLDriver.h"
|
||||
|
||||
#include "private/backend/DriverApi.h"
|
||||
|
||||
#include "CommandStreamDispatcher.h"
|
||||
#include "GLUtils.h"
|
||||
#include "OpenGLContext.h"
|
||||
#include "OpenGLDriverFactory.h"
|
||||
#include "OpenGLProgram.h"
|
||||
#include "OpenGLTimerQuery.h"
|
||||
#include "gl_headers.h"
|
||||
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
|
||||
#include <backend/BufferDescriptor.h>
|
||||
#include <backend/CallbackHandler.h>
|
||||
#include <backend/DriverApiForward.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
#include <backend/PipelineState.h>
|
||||
#include <backend/Platform.h>
|
||||
#include <backend/Program.h>
|
||||
#include <backend/SamplerDescriptor.h>
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
|
||||
#include "private/backend/Dispatcher.h"
|
||||
#include "private/backend/DriverApi.h"
|
||||
|
||||
#include <utils/BitmaskEnum.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Systrace.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec3.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <utility>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
#include <emscripten.h>
|
||||
@@ -127,24 +94,22 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
|
||||
// GLSamplerGroup : 16 few
|
||||
// GLSwapChain : 16 few
|
||||
// GLTimerQuery : 16 few
|
||||
// -- less than or equal 16 bytes
|
||||
// GLFence : 24 few
|
||||
// GLRenderPrimitive : 32 many
|
||||
// GLBufferObject : 32 many
|
||||
// -- less than or equal 32 bytes
|
||||
// GLRenderPrimitive : 40 many
|
||||
// OpenGLProgram : 56 moderate
|
||||
// GLTexture : 64 moderate
|
||||
// -- less than or equal 64 bytes
|
||||
// GLVertexBuffer : 76 moderate
|
||||
// GLStream : 104 few
|
||||
// GLRenderTarget : 112 few
|
||||
// GLVertexBufferInfo : 132 moderate
|
||||
// -- less than or equal to 136 bytes
|
||||
// GLVertexBuffer : 200 moderate
|
||||
// -- less than or equal to 208 bytes
|
||||
|
||||
slog.d
|
||||
<< "\nGLSwapChain: " << sizeof(GLSwapChain)
|
||||
<< "\nGLBufferObject: " << sizeof(GLBufferObject)
|
||||
<< "\nGLVertexBuffer: " << sizeof(GLVertexBuffer)
|
||||
<< "\nGLVertexBufferInfo: " << sizeof(GLVertexBufferInfo)
|
||||
<< "\nGLIndexBuffer: " << sizeof(GLIndexBuffer)
|
||||
<< "\nGLSamplerGroup: " << sizeof(GLSamplerGroup)
|
||||
<< "\nGLRenderPrimitive: " << sizeof(GLRenderPrimitive)
|
||||
@@ -203,11 +168,9 @@ OpenGLDriver::DebugMarker::~DebugMarker() noexcept {
|
||||
|
||||
OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfig& driverConfig) noexcept
|
||||
: mPlatform(*platform),
|
||||
mContext(mPlatform),
|
||||
mContext(),
|
||||
mShaderCompilerService(*this),
|
||||
mHandleAllocator("Handles",
|
||||
driverConfig.handleArenaSize,
|
||||
driverConfig.disableHandleUseAfterFreeCheck),
|
||||
mHandleAllocator("Handles", driverConfig.handleArenaSize),
|
||||
mSamplerMap(32),
|
||||
mDriverConfig(driverConfig) {
|
||||
|
||||
@@ -229,6 +192,8 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfi
|
||||
assert_invariant(mContext.ext.EXT_disjoint_timer_query);
|
||||
#endif
|
||||
|
||||
mTimerQueryImpl = OpenGLTimerQueryFactory::init(mPlatform, *this);
|
||||
|
||||
mShaderCompilerService.init();
|
||||
}
|
||||
|
||||
@@ -273,6 +238,8 @@ void OpenGLDriver::terminate() {
|
||||
}
|
||||
#endif
|
||||
|
||||
delete mTimerQueryImpl;
|
||||
|
||||
mPlatform.terminate();
|
||||
}
|
||||
|
||||
@@ -419,10 +386,6 @@ void OpenGLDriver::setStencilState(StencilState ss) noexcept {
|
||||
// Creating driver objects
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
Handle<HwVertexBufferInfo> OpenGLDriver::createVertexBufferInfoS() noexcept {
|
||||
return initHandle<GLVertexBufferInfo>();
|
||||
}
|
||||
|
||||
Handle<HwVertexBuffer> OpenGLDriver::createVertexBufferS() noexcept {
|
||||
return initHandle<GLVertexBuffer>();
|
||||
}
|
||||
@@ -483,21 +446,14 @@ Handle<HwTimerQuery> OpenGLDriver::createTimerQueryS() noexcept {
|
||||
return initHandle<GLTimerQuery>();
|
||||
}
|
||||
|
||||
void OpenGLDriver::createVertexBufferInfoR(
|
||||
Handle<HwVertexBufferInfo> vbih,
|
||||
uint8_t bufferCount,
|
||||
uint8_t attributeCount,
|
||||
AttributeArray attributes) {
|
||||
DEBUG_MARKER()
|
||||
construct<GLVertexBufferInfo>(vbih, bufferCount, attributeCount, attributes);
|
||||
}
|
||||
|
||||
void OpenGLDriver::createVertexBufferR(
|
||||
Handle<HwVertexBuffer> vbh,
|
||||
uint32_t vertexCount,
|
||||
Handle<HwVertexBufferInfo> vbih) {
|
||||
uint8_t bufferCount,
|
||||
uint8_t attributeCount,
|
||||
uint32_t elementCount,
|
||||
AttributeArray attributes) {
|
||||
DEBUG_MARKER()
|
||||
construct<GLVertexBuffer>(vbh, vertexCount, vbih);
|
||||
construct<GLVertexBuffer>(vbh, bufferCount, attributeCount, elementCount, attributes);
|
||||
}
|
||||
|
||||
void OpenGLDriver::createIndexBufferR(
|
||||
@@ -545,33 +501,31 @@ void OpenGLDriver::createBufferObjectR(Handle<HwBufferObject> boh,
|
||||
|
||||
void OpenGLDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
|
||||
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
|
||||
PrimitiveType pt) {
|
||||
PrimitiveType pt, uint32_t offset,
|
||||
uint32_t minIndex, uint32_t maxIndex, uint32_t count) {
|
||||
DEBUG_MARKER()
|
||||
|
||||
auto& gl = mContext;
|
||||
|
||||
GLVertexBuffer const* const eb = handle_cast<const GLVertexBuffer*>(vbh);
|
||||
GLIndexBuffer const* const ib = handle_cast<const GLIndexBuffer*>(ibh);
|
||||
assert_invariant(ib->elementSize == 2 || ib->elementSize == 4);
|
||||
|
||||
GLVertexBuffer* const vb = handle_cast<GLVertexBuffer*>(vbh);
|
||||
GLRenderPrimitive* const rp = handle_cast<GLRenderPrimitive*>(rph);
|
||||
GLRenderPrimitive* rp = handle_cast<GLRenderPrimitive*>(rph);
|
||||
rp->gl.indicesSize = (ib->elementSize == 4u) ? 4u : 2u;
|
||||
rp->gl.vertexBufferWithObjects = vbh;
|
||||
rp->type = pt;
|
||||
rp->vbih = vb->vbih;
|
||||
rp->offset = offset * rp->gl.indicesSize;
|
||||
rp->count = count;
|
||||
rp->minIndex = minIndex;
|
||||
rp->maxIndex = maxIndex > minIndex ? maxIndex : rp->maxVertexCount - 1; // sanitize max index
|
||||
|
||||
// create a name for this VAO in the current context
|
||||
gl.procs.genVertexArrays(1, &rp->gl.vao[gl.contextIndex]);
|
||||
gl.procs.genVertexArrays(1, &rp->gl.vao);
|
||||
|
||||
// this implies our name is up-to-date
|
||||
rp->gl.nameVersion = gl.state.age;
|
||||
|
||||
// binding the VAO will actually create it
|
||||
gl.bindVertexArray(&rp->gl);
|
||||
|
||||
// Note: we don't update the vertex buffer bindings in the VAO just yet, we will do that
|
||||
// later in draw() or bindRenderPrimitive(). At this point, the HwVertexBuffer might not
|
||||
// have all its buffers set.
|
||||
// update the VBO bindings in the VAO
|
||||
updateVertexArrayObject(rp, eb);
|
||||
|
||||
// this records the index buffer into the currently bound VAO
|
||||
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib->gl.buffer);
|
||||
@@ -618,22 +572,13 @@ void OpenGLDriver::createSamplerGroupR(Handle<HwSamplerGroup> sbh, uint32_t size
|
||||
|
||||
UTILS_NOINLINE
|
||||
void OpenGLDriver::textureStorage(OpenGLDriver::GLTexture* t,
|
||||
uint32_t width, uint32_t height, uint32_t depth, bool useProtectedMemory) noexcept {
|
||||
uint32_t width, uint32_t height, uint32_t depth) noexcept {
|
||||
|
||||
auto& gl = mContext;
|
||||
|
||||
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
|
||||
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
|
||||
|
||||
#ifdef GL_EXT_protected_textures
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
if (UTILS_UNLIKELY(useProtectedMemory)) {
|
||||
assert_invariant(gl.ext.EXT_protected_textures);
|
||||
glTexParameteri(t->gl.target, GL_TEXTURE_PROTECTED_EXT, 1);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
switch (t->gl.target) {
|
||||
case GL_TEXTURE_2D:
|
||||
case GL_TEXTURE_CUBE_MAP:
|
||||
@@ -724,12 +669,6 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
||||
GLenum internalFormat = getInternalFormat(format);
|
||||
assert_invariant(internalFormat);
|
||||
|
||||
if (UTILS_UNLIKELY(usage & TextureUsage::PROTECTED)) {
|
||||
// renderbuffers don't have a protected mode, so we need to use a texture
|
||||
// because protected textures are only supported on GLES 3.2, MSAA will be available.
|
||||
usage |= TextureUsage::SAMPLEABLE;
|
||||
}
|
||||
|
||||
auto& gl = mContext;
|
||||
samples = std::clamp(samples, uint8_t(1u), uint8_t(gl.gets.max_samples));
|
||||
GLTexture* t = construct<GLTexture>(th, target, levels, samples, w, h, depth, format, usage);
|
||||
@@ -765,23 +704,23 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
||||
// we can't be here -- doesn't matter what we do
|
||||
case SamplerType::SAMPLER_2D:
|
||||
t->gl.target = GL_TEXTURE_2D;
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D);
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_2D);
|
||||
break;
|
||||
case SamplerType::SAMPLER_3D:
|
||||
t->gl.target = GL_TEXTURE_3D;
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_3D);
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_3D);
|
||||
break;
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
t->gl.target = GL_TEXTURE_2D_ARRAY;
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D_ARRAY);
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_2D_ARRAY);
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
t->gl.target = GL_TEXTURE_CUBE_MAP;
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP);
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP);
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
t->gl.target = GL_TEXTURE_CUBE_MAP_ARRAY;
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP_ARRAY);
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP_ARRAY);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -793,14 +732,13 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
||||
// multi-sample texture on GL 3.2 / GLES 3.1 and above
|
||||
t->gl.target = GL_TEXTURE_2D_MULTISAMPLE;
|
||||
t->gl.targetIndex = (uint8_t)
|
||||
OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D_MULTISAMPLE);
|
||||
gl.getIndexForTextureTarget(GL_TEXTURE_2D_MULTISAMPLE);
|
||||
} else {
|
||||
// Turn off multi-sampling for that texture. It's just not supported.
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
textureStorage(t, w, h, depth, bool(usage & TextureUsage::PROTECTED));
|
||||
textureStorage(t, w, h, depth);
|
||||
}
|
||||
} else {
|
||||
assert_invariant(any(usage & (
|
||||
@@ -809,7 +747,6 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
||||
TextureUsage::STENCIL_ATTACHMENT)));
|
||||
assert_invariant(levels == 1);
|
||||
assert_invariant(target == SamplerType::SAMPLER_2D);
|
||||
assert_invariant(none(usage & TextureUsage::PROTECTED));
|
||||
t->gl.internalFormat = internalFormat;
|
||||
t->gl.target = GL_RENDERBUFFER;
|
||||
glGenRenderbuffers(1, &t->gl.id);
|
||||
@@ -864,27 +801,27 @@ void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
|
||||
switch (target) {
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
t->gl.target = GL_TEXTURE_EXTERNAL_OES;
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_EXTERNAL_OES);
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_EXTERNAL_OES);
|
||||
break;
|
||||
case SamplerType::SAMPLER_2D:
|
||||
t->gl.target = GL_TEXTURE_2D;
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D);
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_2D);
|
||||
break;
|
||||
case SamplerType::SAMPLER_3D:
|
||||
t->gl.target = GL_TEXTURE_3D;
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_3D);
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_3D);
|
||||
break;
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
t->gl.target = GL_TEXTURE_2D_ARRAY;
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D_ARRAY);
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_2D_ARRAY);
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
t->gl.target = GL_TEXTURE_CUBE_MAP;
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP);
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP);
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
t->gl.target = GL_TEXTURE_CUBE_MAP_ARRAY;
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP_ARRAY);
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_CUBE_MAP_ARRAY);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -895,7 +832,7 @@ void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
|
||||
if (gl.features.multisample_texture) {
|
||||
// multi-sample texture on GL 3.2 / GLES 3.1 and above
|
||||
t->gl.target = GL_TEXTURE_2D_MULTISAMPLE;
|
||||
t->gl.targetIndex = OpenGLContext::getIndexForTextureTarget(GL_TEXTURE_2D_MULTISAMPLE);
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_2D_MULTISAMPLE);
|
||||
} else {
|
||||
// Turn off multi-sampling for that texture. It's just not supported.
|
||||
}
|
||||
@@ -906,65 +843,61 @@ void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
|
||||
}
|
||||
|
||||
void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer const* vb) {
|
||||
// NOTE: this is called from draw() and must be as efficient as possible.
|
||||
|
||||
auto& gl = mContext;
|
||||
|
||||
// NOTE: this is called from draw() and must be as efficient as possible.
|
||||
|
||||
|
||||
if (UTILS_LIKELY(gl.ext.OES_vertex_array_object)) {
|
||||
// The VAO for the given render primitive must already be bound.
|
||||
#ifndef NDEBUG
|
||||
GLint vaoBinding;
|
||||
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &vaoBinding);
|
||||
assert_invariant(vaoBinding == (GLint)rp->gl.vao[gl.contextIndex]);
|
||||
assert_invariant(vaoBinding == (GLint)rp->gl.vao);
|
||||
#endif
|
||||
rp->gl.vertexBufferVersion = vb->bufferObjectsVersion;
|
||||
} else {
|
||||
// if we don't have OES_vertex_array_object, we never update the buffer version so
|
||||
// that it's always reset in draw
|
||||
}
|
||||
|
||||
if (UTILS_LIKELY(rp->gl.vertexBufferVersion == vb->bufferObjectsVersion &&
|
||||
rp->gl.stateVersion == gl.state.age)) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLVertexBufferInfo const* const vbi = handle_cast<const GLVertexBufferInfo*>(vb->vbih);
|
||||
|
||||
for (size_t i = 0, n = vbi->attributes.size(); i < n; i++) {
|
||||
const auto& attribute = vbi->attributes[i];
|
||||
rp->maxVertexCount = vb->vertexCount;
|
||||
for (size_t i = 0, n = vb->attributes.size(); i < n; i++) {
|
||||
const auto& attribute = vb->attributes[i];
|
||||
const uint8_t bi = attribute.buffer;
|
||||
if (bi != Attribute::BUFFER_UNUSED) {
|
||||
// if a buffer is defined it must not be invalid.
|
||||
assert_invariant(vb->gl.buffers[bi]);
|
||||
|
||||
// if w're on ES2, the user shouldn't use FLAG_INTEGER_TARGET
|
||||
// Invoking glVertexAttribPointer without a bound VBO is an invalid operation, so we must
|
||||
// take care to avoid it. This can occur when VertexBuffer is only partially populated with
|
||||
// BufferObject items.
|
||||
if (bi != Attribute::BUFFER_UNUSED && UTILS_LIKELY(vb->gl.buffers[bi] != 0)) {
|
||||
|
||||
assert_invariant(!(gl.isES2() && (attribute.flags & Attribute::FLAG_INTEGER_TARGET)));
|
||||
|
||||
gl.bindBuffer(GL_ARRAY_BUFFER, vb->gl.buffers[bi]);
|
||||
GLuint const index = i;
|
||||
GLint const size = (GLint)getComponentCount(attribute.type);
|
||||
GLenum const type = getComponentType(attribute.type);
|
||||
GLboolean const normalized = getNormalization(attribute.flags & Attribute::FLAG_NORMALIZED);
|
||||
GLsizei const stride = attribute.stride;
|
||||
void const* pointer = reinterpret_cast<void const *>(attribute.offset);
|
||||
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
if (UTILS_UNLIKELY(attribute.flags & Attribute::FLAG_INTEGER_TARGET)) {
|
||||
// integer attributes can't be floats
|
||||
assert_invariant(type == GL_BYTE || type == GL_UNSIGNED_BYTE || type == GL_SHORT ||
|
||||
type == GL_UNSIGNED_SHORT || type == GL_INT || type == GL_UNSIGNED_INT);
|
||||
glVertexAttribIPointer(index, size, type, stride, pointer);
|
||||
glVertexAttribIPointer(GLuint(i),
|
||||
(GLint)getComponentCount(attribute.type),
|
||||
getComponentType(attribute.type),
|
||||
attribute.stride,
|
||||
(void*) uintptr_t(attribute.offset));
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
glVertexAttribPointer(index, size, type, normalized, stride, pointer);
|
||||
glVertexAttribPointer(GLuint(i),
|
||||
(GLint)getComponentCount(attribute.type),
|
||||
getComponentType(attribute.type),
|
||||
getNormalization(attribute.flags & Attribute::FLAG_NORMALIZED),
|
||||
attribute.stride,
|
||||
(void*) uintptr_t(attribute.offset));
|
||||
}
|
||||
|
||||
gl.enableVertexAttribArray(&rp->gl, GLuint(i));
|
||||
gl.enableVertexAttribArray(GLuint(i));
|
||||
} else {
|
||||
// In some OpenGL implementations, we must supply a properly-typed placeholder for
|
||||
// every integer input that is declared in the vertex shader.
|
||||
// Note that the corresponding doesn't have to be enabled and in fact won't be. If it
|
||||
// was enabled, it would indicate a user-error (providing the wrong type of array).
|
||||
// With a disabled array, the vertex shader gets the attribute from glVertexAttrib,
|
||||
// and must have the proper intergerness.
|
||||
// But at this point, we don't know what the shader requirements are, and so we must
|
||||
// rely on the attribute.
|
||||
|
||||
// In some OpenGL implementations, we must supply a properly-typed placeholder for
|
||||
// every integer input that is declared in the vertex shader, even if disabled.
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
if (UTILS_UNLIKELY(attribute.flags & Attribute::FLAG_INTEGER_TARGET)) {
|
||||
if (!gl.isES2()) {
|
||||
@@ -977,21 +910,13 @@ void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer
|
||||
glVertexAttrib4f(GLuint(i), 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
gl.disableVertexAttribArray(&rp->gl, GLuint(i));
|
||||
gl.disableVertexAttribArray(GLuint(i));
|
||||
}
|
||||
}
|
||||
|
||||
rp->gl.stateVersion = gl.state.age;
|
||||
if (UTILS_LIKELY(gl.ext.OES_vertex_array_object)) {
|
||||
rp->gl.vertexBufferVersion = vb->bufferObjectsVersion;
|
||||
} else {
|
||||
// if we don't have OES_vertex_array_object, we never update the buffer version so
|
||||
// that it's always reset in draw
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
|
||||
GLRenderTarget const* rt, GLenum attachment, uint8_t layerCount) noexcept {
|
||||
GLRenderTarget const* rt, GLenum attachment) noexcept {
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
// Only used by assert_invariant() checks below
|
||||
@@ -1129,20 +1054,9 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
|
||||
case GL_TEXTURE_2D_ARRAY:
|
||||
case GL_TEXTURE_CUBE_MAP_ARRAY:
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
|
||||
// TODO: support multiview for iOS and WebGL
|
||||
#if !defined(__EMSCRIPTEN__) && !defined(IOS)
|
||||
if (layerCount > 1) {
|
||||
// if layerCount > 1, it means we use the multiview extension.
|
||||
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, attachment,
|
||||
t->gl.id, 0, binfo.baseViewIndex, layerCount);
|
||||
} else
|
||||
#endif // !defined(__EMSCRIPTEN__) && !defined(IOS)
|
||||
{
|
||||
// GL_TEXTURE_2D_MULTISAMPLE_ARRAY is not supported in GLES
|
||||
glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment,
|
||||
// GL_TEXTURE_2D_MULTISAMPLE_ARRAY is not supported in GLES
|
||||
glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment,
|
||||
t->gl.id, binfo.level, binfo.layer);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
@@ -1303,9 +1217,11 @@ void OpenGLDriver::createDefaultRenderTargetR(
|
||||
|
||||
construct<GLRenderTarget>(rth, 0, 0); // FIXME: we don't know the width/height
|
||||
|
||||
uint32_t const framebuffer = mPlatform.createDefaultRenderTarget();
|
||||
|
||||
GLRenderTarget* rt = handle_cast<GLRenderTarget*>(rth);
|
||||
rt->gl.isDefault = true;
|
||||
rt->gl.fbo = 0; // the actual id is resolved at binding time
|
||||
rt->gl.fbo = framebuffer;
|
||||
rt->gl.samples = 1;
|
||||
// FIXME: these flags should reflect the actual attachments present
|
||||
rt->targets = TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH;
|
||||
@@ -1316,7 +1232,6 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
uint8_t samples,
|
||||
uint8_t layerCount,
|
||||
MRT color,
|
||||
TargetBufferInfo depth,
|
||||
TargetBufferInfo stencil) {
|
||||
@@ -1383,7 +1298,7 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
if (any(targets & getTargetBufferFlagsAt(i))) {
|
||||
assert_invariant(color[i].handle);
|
||||
rt->gl.color[i] = handle_cast<GLTexture*>(color[i].handle);
|
||||
framebufferTexture(color[i], rt, GL_COLOR_ATTACHMENT0 + i, layerCount);
|
||||
framebufferTexture(color[i], rt, GL_COLOR_ATTACHMENT0 + i);
|
||||
bufs[i] = GL_COLOR_ATTACHMENT0 + i;
|
||||
checkDimensions(rt->gl.color[i], color[i].level);
|
||||
}
|
||||
@@ -1405,7 +1320,7 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
// either we supplied only the depth handle or both depth/stencil are identical and not null
|
||||
if (depth.handle && (stencil.handle == depth.handle || !stencil.handle)) {
|
||||
rt->gl.depth = handle_cast<GLTexture*>(depth.handle);
|
||||
framebufferTexture(depth, rt, GL_DEPTH_STENCIL_ATTACHMENT, layerCount);
|
||||
framebufferTexture(depth, rt, GL_DEPTH_STENCIL_ATTACHMENT);
|
||||
specialCased = true;
|
||||
checkDimensions(rt->gl.depth, depth.level);
|
||||
}
|
||||
@@ -1416,13 +1331,13 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
if (any(targets & TargetBufferFlags::DEPTH)) {
|
||||
assert_invariant(depth.handle);
|
||||
rt->gl.depth = handle_cast<GLTexture*>(depth.handle);
|
||||
framebufferTexture(depth, rt, GL_DEPTH_ATTACHMENT, layerCount);
|
||||
framebufferTexture(depth, rt, GL_DEPTH_ATTACHMENT);
|
||||
checkDimensions(rt->gl.depth, depth.level);
|
||||
}
|
||||
if (any(targets & TargetBufferFlags::STENCIL)) {
|
||||
assert_invariant(stencil.handle);
|
||||
rt->gl.stencil = handle_cast<GLTexture*>(stencil.handle);
|
||||
framebufferTexture(stencil, rt, GL_STENCIL_ATTACHMENT, layerCount);
|
||||
framebufferTexture(stencil, rt, GL_STENCIL_ATTACHMENT);
|
||||
checkDimensions(rt->gl.stencil, stencil.level);
|
||||
}
|
||||
}
|
||||
@@ -1464,13 +1379,6 @@ void OpenGLDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
GLSwapChain* sc = handle_cast<GLSwapChain*>(sch);
|
||||
sc->swapChain = mPlatform.createSwapChain(nativeWindow, flags);
|
||||
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
// note: in practice this should never happen on Android
|
||||
ASSERT_POSTCONDITION(sc->swapChain,
|
||||
"createSwapChain(%p, 0x%lx) failed. See logs for details.",
|
||||
nativeWindow, flags);
|
||||
#endif
|
||||
|
||||
// See if we need the emulated rec709 output conversion
|
||||
if (UTILS_UNLIKELY(mContext.isES2())) {
|
||||
sc->rec709 = (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE &&
|
||||
@@ -1485,13 +1393,6 @@ void OpenGLDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
|
||||
GLSwapChain* sc = handle_cast<GLSwapChain*>(sch);
|
||||
sc->swapChain = mPlatform.createSwapChain(width, height, flags);
|
||||
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
// note: in practice this should never happen on Android
|
||||
ASSERT_POSTCONDITION(sc->swapChain,
|
||||
"createSwapChainHeadless(%u, %u, 0x%lx) failed. See logs for details.",
|
||||
width, height, flags);
|
||||
#endif
|
||||
|
||||
// See if we need the emulated rec709 output conversion
|
||||
if (UTILS_UNLIKELY(mContext.isES2())) {
|
||||
sc->rec709 = (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE &&
|
||||
@@ -1502,21 +1403,13 @@ void OpenGLDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
|
||||
void OpenGLDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
|
||||
DEBUG_MARKER()
|
||||
GLTimerQuery* tq = handle_cast<GLTimerQuery*>(tqh);
|
||||
mContext.createTimerQuery(tq);
|
||||
mTimerQueryImpl->createTimerQuery(tq);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Destroying driver objects
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void OpenGLDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
|
||||
DEBUG_MARKER()
|
||||
if (vbih) {
|
||||
GLVertexBufferInfo const* vbi = handle_cast<const GLVertexBufferInfo*>(vbih);
|
||||
destruct(vbih, vbi);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
|
||||
DEBUG_MARKER()
|
||||
if (vbh) {
|
||||
@@ -1556,20 +1449,7 @@ void OpenGLDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
if (rph) {
|
||||
auto& gl = mContext;
|
||||
GLRenderPrimitive const* rp = handle_cast<const GLRenderPrimitive*>(rph);
|
||||
gl.deleteVertexArray(rp->gl.vao[gl.contextIndex]);
|
||||
|
||||
// If we have a name in the "other" context, we need to schedule the destroy for
|
||||
// later, because it can't be done here. VAOs are "container objects" and are not
|
||||
// shared between contexts.
|
||||
size_t const otherContextIndex = 1 - gl.contextIndex;
|
||||
GLuint const nameInOtherContext = rp->gl.vao[otherContextIndex];
|
||||
if (UTILS_UNLIKELY(nameInOtherContext)) {
|
||||
gl.destroyWithContext(otherContextIndex,
|
||||
[name = nameInOtherContext](OpenGLContext& gl) {
|
||||
gl.deleteVertexArray(name);
|
||||
});
|
||||
}
|
||||
|
||||
gl.deleteVertexArrays(1, &rp->gl.vao);
|
||||
destruct(rph, rp);
|
||||
}
|
||||
}
|
||||
@@ -1634,11 +1514,11 @@ void OpenGLDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
|
||||
GLRenderTarget* rt = handle_cast<GLRenderTarget*>(rth);
|
||||
if (rt->gl.fbo) {
|
||||
// first unbind this framebuffer if needed
|
||||
gl.unbindFramebuffer(GL_FRAMEBUFFER);
|
||||
gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
if (rt->gl.fbo_read) {
|
||||
// first unbind this framebuffer if needed
|
||||
gl.unbindFramebuffer(GL_FRAMEBUFFER);
|
||||
gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
@@ -1710,7 +1590,7 @@ void OpenGLDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
|
||||
if (tqh) {
|
||||
GLTimerQuery* tq = handle_cast<GLTimerQuery*>(tqh);
|
||||
mContext.destroyTimerQuery(tq);
|
||||
mTimerQueryImpl->destroyTimerQuery(tq);
|
||||
destruct(tqh, tq);
|
||||
}
|
||||
}
|
||||
@@ -2001,7 +1881,7 @@ bool OpenGLDriver::isFrameBufferFetchMultiSampleSupported() {
|
||||
}
|
||||
|
||||
bool OpenGLDriver::isFrameTimeSupported() {
|
||||
return TimerQueryFactory::isGpuTimeSupported();
|
||||
return OpenGLTimerQueryFactory::isGpuTimeSupported();
|
||||
}
|
||||
|
||||
bool OpenGLDriver::isAutoDepthResolveSupported() {
|
||||
@@ -2019,24 +1899,12 @@ bool OpenGLDriver::isSRGBSwapChainSupported() {
|
||||
return mPlatform.isSRGBSwapChainSupported();
|
||||
}
|
||||
|
||||
bool OpenGLDriver::isProtectedContentSupported() {
|
||||
return mPlatform.isProtectedContextSupported();
|
||||
}
|
||||
|
||||
bool OpenGLDriver::isStereoSupported(backend::StereoscopicType stereoscopicType) {
|
||||
// Instanced-stereo requires instancing and EXT_clip_cull_distance.
|
||||
// Multiview-stereo requires ES 3.0 and OVR_multiview2.
|
||||
bool OpenGLDriver::isStereoSupported() {
|
||||
// Stereo requires instancing and EXT_clip_cull_distance.
|
||||
if (UTILS_UNLIKELY(mContext.isES2())) {
|
||||
return false;
|
||||
}
|
||||
switch (stereoscopicType) {
|
||||
case backend::StereoscopicType::INSTANCED:
|
||||
return mContext.ext.EXT_clip_cull_distance;
|
||||
case backend::StereoscopicType::MULTIVIEW:
|
||||
return mContext.ext.OVR_multiview2;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return mContext.ext.EXT_clip_cull_distance;
|
||||
}
|
||||
|
||||
bool OpenGLDriver::isParallelShaderCompileSupported() {
|
||||
@@ -2047,10 +1915,6 @@ bool OpenGLDriver::isDepthStencilResolveSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenGLDriver::isProtectedTexturesSupported() {
|
||||
return getContext().ext.EXT_protected_textures;
|
||||
}
|
||||
|
||||
bool OpenGLDriver::isWorkaroundNeeded(Workaround workaround) {
|
||||
switch (workaround) {
|
||||
case Workaround::SPLIT_EASU:
|
||||
@@ -2118,18 +1982,7 @@ void OpenGLDriver::makeCurrent(Handle<HwSwapChain> schDraw, Handle<HwSwapChain>
|
||||
|
||||
GLSwapChain* scDraw = handle_cast<GLSwapChain*>(schDraw);
|
||||
GLSwapChain* scRead = handle_cast<GLSwapChain*>(schRead);
|
||||
|
||||
mPlatform.makeCurrent(scDraw->swapChain, scRead->swapChain,
|
||||
[this]() {
|
||||
// OpenGL context is about to change, unbind everything
|
||||
mContext.unbindEverything();
|
||||
},
|
||||
[this](size_t index) {
|
||||
// OpenGL context has changed, resynchronize the state with the cache
|
||||
mContext.synchronizeStateAndCache(index);
|
||||
slog.d << "*** OpenGL context change : " << (index ? "protected" : "default") << io::endl;
|
||||
});
|
||||
|
||||
mPlatform.makeCurrent(scDraw->swapChain, scRead->swapChain);
|
||||
mCurrentDrawSwapChain = scDraw;
|
||||
|
||||
// From the GL spec for glViewport and glScissor:
|
||||
@@ -2472,9 +2325,7 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
|
||||
size_t const stride = p.stride ? p.stride : width;
|
||||
size_t const bpp = PBD::computeDataSize(p.format, p.type, 1, 1, 1);
|
||||
size_t const bpr = PBD::computeDataSize(p.format, p.type, stride, 1, p.alignment);
|
||||
size_t const bpl = bpr * height; // TODO: PBD should have a "layer stride"
|
||||
void const* const buffer = static_cast<char const*>(p.buffer)
|
||||
+ bpp* p.left + bpr * p.top + bpl * 0; // TODO: PBD should have a p.depth
|
||||
void const* const buffer = static_cast<char const*>(p.buffer) + p.left * bpp + bpr * p.top;
|
||||
|
||||
switch (t->target) {
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
@@ -2539,7 +2390,7 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
|
||||
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
if (!gl.isES2()) {
|
||||
// Update the base/max LOD, so we don't access undefined LOD. this allows the app to
|
||||
// update the base/max LOD, so we don't access undefined LOD. this allows the app to
|
||||
// specify levels as they become available.
|
||||
if (int8_t(level) < t->gl.baseLevel) {
|
||||
t->gl.baseLevel = int8_t(level);
|
||||
@@ -2640,7 +2491,7 @@ void OpenGLDriver::setCompressedTextureData(GLTexture* t, uint32_t level,
|
||||
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
if (!gl.isES2()) {
|
||||
// Update the base/max LOD, so we don't access undefined LOD. this allows the app to
|
||||
// update the base/max LOD, so we don't access undefined LOD. this allows the app to
|
||||
// specify levels as they become available.
|
||||
if (int8_t(level) < t->gl.baseLevel) {
|
||||
t->gl.baseLevel = int8_t(level);
|
||||
@@ -2779,21 +2630,21 @@ void OpenGLDriver::replaceStream(GLTexture* texture, GLStream* newStream) noexce
|
||||
void OpenGLDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
DEBUG_MARKER()
|
||||
GLTimerQuery* tq = handle_cast<GLTimerQuery*>(tqh);
|
||||
mContext.beginTimeElapsedQuery(tq);
|
||||
mTimerQueryImpl->beginTimeElapsedQuery(tq);
|
||||
}
|
||||
|
||||
void OpenGLDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
DEBUG_MARKER()
|
||||
GLTimerQuery* tq = handle_cast<GLTimerQuery*>(tqh);
|
||||
mContext.endTimeElapsedQuery(*this, tq);
|
||||
mTimerQueryImpl->endTimeElapsedQuery(tq);
|
||||
}
|
||||
|
||||
TimerQueryResult OpenGLDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
|
||||
bool OpenGLDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
|
||||
GLTimerQuery* tq = handle_cast<GLTimerQuery*>(tqh);
|
||||
return TimerQueryFactoryInterface::getTimerQueryValue(tq, elapsedTime);
|
||||
return OpenGLTimerQueryInterface::getTimerQueryValue(tq, elapsedTime);
|
||||
}
|
||||
|
||||
void OpenGLDriver::compilePrograms(CompilerPriorityQueue,
|
||||
void OpenGLDriver::compilePrograms(CompilerPriorityQueue priority,
|
||||
CallbackHandler* handler, CallbackHandler::Callback callback, void* user) {
|
||||
if (callback) {
|
||||
getShaderCompilerService().notifyWhenAllProgramsAreReady(handler, callback, user);
|
||||
@@ -2821,13 +2672,13 @@ void OpenGLDriver::beginRenderPass(Handle<HwRenderTarget> rth,
|
||||
const TargetBufferFlags clearFlags = params.flags.clear & rt->targets;
|
||||
TargetBufferFlags discardFlags = params.flags.discardStart & rt->targets;
|
||||
|
||||
GLuint const fbo = gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
|
||||
gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_FRAMEBUFFER)
|
||||
|
||||
if (gl.ext.EXT_discard_framebuffer
|
||||
&& !gl.bugs.disable_invalidate_framebuffer) {
|
||||
AttachmentArray attachments; // NOLINT
|
||||
GLsizei const attachmentCount = getAttachments(attachments, discardFlags, !fbo);
|
||||
GLsizei const attachmentCount = getAttachments(attachments, rt, discardFlags);
|
||||
if (attachmentCount) {
|
||||
gl.procs.invalidateFramebuffer(GL_FRAMEBUFFER, attachmentCount, attachments.data());
|
||||
}
|
||||
@@ -2915,9 +2766,9 @@ void OpenGLDriver::endRenderPass(int) {
|
||||
}
|
||||
if (!gl.bugs.disable_invalidate_framebuffer) {
|
||||
// we wouldn't have to bind the framebuffer if we had glInvalidateNamedFramebuffer()
|
||||
GLuint const fbo = gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
|
||||
gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
|
||||
AttachmentArray attachments; // NOLINT
|
||||
GLsizei const attachmentCount = getAttachments(attachments, effectiveDiscardFlags, !fbo);
|
||||
GLsizei const attachmentCount = getAttachments(attachments, rt, effectiveDiscardFlags);
|
||||
if (attachmentCount) {
|
||||
gl.procs.invalidateFramebuffer(GL_FRAMEBUFFER, attachmentCount, attachments.data());
|
||||
}
|
||||
@@ -2979,48 +2830,50 @@ void OpenGLDriver::resolvePass(ResolveAction action, GLRenderTarget const* rt,
|
||||
}
|
||||
|
||||
GLsizei OpenGLDriver::getAttachments(AttachmentArray& attachments,
|
||||
TargetBufferFlags buffers, bool isDefaultFramebuffer) noexcept {
|
||||
GLRenderTarget const* rt, TargetBufferFlags buffers) noexcept {
|
||||
assert_invariant(buffers <= rt->targets);
|
||||
|
||||
GLsizei attachmentCount = 0;
|
||||
// the default framebuffer uses different constants!!!
|
||||
|
||||
const bool defaultFramebuffer = (rt->gl.fbo == 0);
|
||||
if (any(buffers & TargetBufferFlags::COLOR0)) {
|
||||
attachments[attachmentCount++] = isDefaultFramebuffer ? GL_COLOR : GL_COLOR_ATTACHMENT0;
|
||||
attachments[attachmentCount++] = defaultFramebuffer ? GL_COLOR : GL_COLOR_ATTACHMENT0;
|
||||
}
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
if (any(buffers & TargetBufferFlags::COLOR1)) {
|
||||
assert_invariant(!isDefaultFramebuffer);
|
||||
assert_invariant(!defaultFramebuffer);
|
||||
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT1;
|
||||
}
|
||||
if (any(buffers & TargetBufferFlags::COLOR2)) {
|
||||
assert_invariant(!isDefaultFramebuffer);
|
||||
assert_invariant(!defaultFramebuffer);
|
||||
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT2;
|
||||
}
|
||||
if (any(buffers & TargetBufferFlags::COLOR3)) {
|
||||
assert_invariant(!isDefaultFramebuffer);
|
||||
assert_invariant(!defaultFramebuffer);
|
||||
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT3;
|
||||
}
|
||||
if (any(buffers & TargetBufferFlags::COLOR4)) {
|
||||
assert_invariant(!isDefaultFramebuffer);
|
||||
assert_invariant(!defaultFramebuffer);
|
||||
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT4;
|
||||
}
|
||||
if (any(buffers & TargetBufferFlags::COLOR5)) {
|
||||
assert_invariant(!isDefaultFramebuffer);
|
||||
assert_invariant(!defaultFramebuffer);
|
||||
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT5;
|
||||
}
|
||||
if (any(buffers & TargetBufferFlags::COLOR6)) {
|
||||
assert_invariant(!isDefaultFramebuffer);
|
||||
assert_invariant(!defaultFramebuffer);
|
||||
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT6;
|
||||
}
|
||||
if (any(buffers & TargetBufferFlags::COLOR7)) {
|
||||
assert_invariant(!isDefaultFramebuffer);
|
||||
assert_invariant(!defaultFramebuffer);
|
||||
attachments[attachmentCount++] = GL_COLOR_ATTACHMENT7;
|
||||
}
|
||||
#endif
|
||||
if (any(buffers & TargetBufferFlags::DEPTH)) {
|
||||
attachments[attachmentCount++] = isDefaultFramebuffer ? GL_DEPTH : GL_DEPTH_ATTACHMENT;
|
||||
attachments[attachmentCount++] = defaultFramebuffer ? GL_DEPTH : GL_DEPTH_ATTACHMENT;
|
||||
}
|
||||
if (any(buffers & TargetBufferFlags::STENCIL)) {
|
||||
attachments[attachmentCount++] = isDefaultFramebuffer ? GL_STENCIL : GL_STENCIL_ATTACHMENT;
|
||||
attachments[attachmentCount++] = defaultFramebuffer ? GL_STENCIL : GL_STENCIL_ATTACHMENT;
|
||||
}
|
||||
return attachmentCount;
|
||||
}
|
||||
@@ -3740,8 +3593,8 @@ void OpenGLDriver::blit(
|
||||
mask, GL_NEAREST);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
|
||||
gl.unbindFramebuffer(GL_DRAW_FRAMEBUFFER);
|
||||
gl.unbindFramebuffer(GL_READ_FRAMEBUFFER);
|
||||
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
glDeleteFramebuffers(2, fbo);
|
||||
|
||||
if (any(d->usage & TextureUsage::SAMPLEABLE)) {
|
||||
@@ -3845,52 +3698,51 @@ void OpenGLDriver::updateTextureLodRange(GLTexture* texture, int8_t targetLevel)
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLDriver::bindPipeline(PipelineState state) {
|
||||
void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph, uint32_t instanceCount) {
|
||||
DEBUG_MARKER()
|
||||
auto& gl = mContext;
|
||||
setRasterState(state.rasterState);
|
||||
setStencilState(state.stencilState);
|
||||
gl.polygonOffset(state.polygonOffset.slope, state.polygonOffset.constant);
|
||||
OpenGLProgram* const p = handle_cast<OpenGLProgram*>(state.program);
|
||||
mValidProgram = useProgram(p);
|
||||
}
|
||||
|
||||
void OpenGLDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
DEBUG_MARKER()
|
||||
auto& gl = mContext;
|
||||
OpenGLProgram* const p = handle_cast<OpenGLProgram*>(state.program);
|
||||
|
||||
bool const success = useProgram(p);
|
||||
if (UTILS_UNLIKELY(!success)) {
|
||||
// Avoid fatal (or cascading) errors that can occur during the draw call when the program
|
||||
// is invalid. The shader compile error has already been dumped to the console at this
|
||||
// point, so it's fine to simply return early.
|
||||
return;
|
||||
}
|
||||
|
||||
GLRenderPrimitive* const rp = handle_cast<GLRenderPrimitive*>(rph);
|
||||
|
||||
// Gracefully do nothing if the render primitive has not been set up.
|
||||
VertexBufferHandle vb = rp->gl.vertexBufferWithObjects;
|
||||
if (UTILS_UNLIKELY(!vb)) {
|
||||
mBoundRenderPrimitive = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
gl.bindVertexArray(&rp->gl);
|
||||
|
||||
// If necessary, mutate the bindings in the VAO.
|
||||
gl.bindVertexArray(&rp->gl);
|
||||
GLVertexBuffer const* const glvb = handle_cast<GLVertexBuffer*>(vb);
|
||||
updateVertexArrayObject(rp, glvb);
|
||||
|
||||
mBoundRenderPrimitive = rp;
|
||||
}
|
||||
|
||||
void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
||||
GLRenderPrimitive const* const rp = mBoundRenderPrimitive;
|
||||
if (UTILS_UNLIKELY(!rp || !mValidProgram)) {
|
||||
return;
|
||||
if (UTILS_UNLIKELY(rp->gl.vertexBufferVersion != glvb->bufferObjectsVersion)) {
|
||||
updateVertexArrayObject(rp, glvb);
|
||||
}
|
||||
|
||||
setRasterState(state.rasterState);
|
||||
setStencilState(state.stencilState);
|
||||
|
||||
gl.polygonOffset(state.polygonOffset.slope, state.polygonOffset.constant);
|
||||
|
||||
setScissor(state.scissor);
|
||||
|
||||
if (UTILS_LIKELY(instanceCount <= 1)) {
|
||||
glDrawElements(GLenum(rp->type), (GLsizei)indexCount, rp->gl.getIndicesType(),
|
||||
reinterpret_cast<const void*>(indexOffset * rp->gl.indicesSize));
|
||||
glDrawElements(GLenum(rp->type), (GLsizei)rp->count, rp->gl.getIndicesType(),
|
||||
reinterpret_cast<const void*>(rp->offset));
|
||||
} else {
|
||||
assert_invariant(!mContext.isES2());
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
glDrawElementsInstanced(GLenum(rp->type), (GLsizei)indexCount,
|
||||
rp->gl.getIndicesType(),
|
||||
reinterpret_cast<const void*>(indexOffset * rp->gl.indicesSize),
|
||||
glDrawElementsInstanced(GLenum(rp->type), (GLsizei)rp->count,
|
||||
rp->gl.getIndicesType(), reinterpret_cast<const void*>(rp->offset),
|
||||
(GLsizei)instanceCount);
|
||||
#endif
|
||||
}
|
||||
@@ -3902,21 +3754,6 @@ void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLDriver::scissor(Viewport scissor) {
|
||||
setScissor(scissor);
|
||||
}
|
||||
|
||||
void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
|
||||
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
|
||||
DEBUG_MARKER()
|
||||
GLRenderPrimitive* const rp = handle_cast<GLRenderPrimitive*>(rph);
|
||||
state.primitiveType = rp->type;
|
||||
state.vertexBufferInfo = rp->vbih;
|
||||
bindPipeline(state);
|
||||
bindRenderPrimitive(rph);
|
||||
draw2(indexOffset, indexCount, instanceCount);
|
||||
}
|
||||
|
||||
void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
|
||||
getShaderCompilerService().tick();
|
||||
|
||||
|
||||
@@ -18,42 +18,27 @@
|
||||
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGLDRIVER_H
|
||||
|
||||
#include "DriverBase.h"
|
||||
#include "GLUtils.h"
|
||||
#include "OpenGLContext.h"
|
||||
#include "OpenGLTimerQuery.h"
|
||||
#include "ShaderCompilerService.h"
|
||||
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
|
||||
#include <backend/AcquiredImage.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
#include <backend/Platform.h>
|
||||
#include <backend/Program.h>
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
|
||||
#include "private/backend/Driver.h"
|
||||
#include "private/backend/HandleAllocator.h"
|
||||
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
|
||||
#include <backend/AcquiredImage.h>
|
||||
#include <backend/Program.h>
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Allocator.h>
|
||||
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
#include <array>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <set>
|
||||
|
||||
#ifndef FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB
|
||||
# define FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB 4
|
||||
@@ -66,17 +51,15 @@ class PixelBufferDescriptor;
|
||||
struct TargetBufferInfo;
|
||||
|
||||
class OpenGLProgram;
|
||||
class TimerQueryFactoryInterface;
|
||||
class OpenGLTimerQueryInterface;
|
||||
|
||||
class OpenGLDriver final : public DriverBase {
|
||||
inline explicit OpenGLDriver(OpenGLPlatform* platform,
|
||||
const Platform::DriverConfig& driverConfig) noexcept;
|
||||
inline explicit OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfig& driverConfig) noexcept;
|
||||
~OpenGLDriver() noexcept final;
|
||||
Dispatcher getDispatcher() const noexcept final;
|
||||
|
||||
public:
|
||||
static Driver* create(OpenGLPlatform* platform, void* sharedGLContext,
|
||||
const Platform::DriverConfig& driverConfig) noexcept;
|
||||
static Driver* create(OpenGLPlatform* platform, void* sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept;
|
||||
|
||||
class DebugMarker {
|
||||
OpenGLDriver& driver;
|
||||
@@ -111,22 +94,8 @@ public:
|
||||
uint16_t age = 0;
|
||||
};
|
||||
|
||||
struct GLVertexBufferInfo : public HwVertexBufferInfo {
|
||||
GLVertexBufferInfo() noexcept = default;
|
||||
GLVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
|
||||
AttributeArray const& attributes)
|
||||
: HwVertexBufferInfo(bufferCount, attributeCount),
|
||||
attributes(attributes) {
|
||||
}
|
||||
AttributeArray attributes;
|
||||
};
|
||||
|
||||
struct GLVertexBuffer : public HwVertexBuffer {
|
||||
GLVertexBuffer() noexcept = default;
|
||||
GLVertexBuffer(uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih)
|
||||
: HwVertexBuffer(vertexCount), vbih(vbih) {
|
||||
}
|
||||
Handle<HwVertexBufferInfo> vbih;
|
||||
using HwVertexBuffer::HwVertexBuffer;
|
||||
struct {
|
||||
// 4 * MAX_VERTEX_ATTRIBUTE_COUNT bytes
|
||||
std::array<GLuint, MAX_VERTEX_ATTRIBUTE_COUNT> buffers{};
|
||||
@@ -154,7 +123,6 @@ public:
|
||||
struct GLRenderPrimitive : public HwRenderPrimitive {
|
||||
using HwRenderPrimitive::HwRenderPrimitive;
|
||||
OpenGLContext::RenderPrimitive gl;
|
||||
Handle<HwVertexBufferInfo> vbih;
|
||||
};
|
||||
|
||||
struct GLTexture : public HwTexture {
|
||||
@@ -179,7 +147,15 @@ public:
|
||||
OpenGLPlatform::ExternalTexture* externalTexture = nullptr;
|
||||
};
|
||||
|
||||
using GLTimerQuery = filament::backend::GLTimerQuery;
|
||||
struct GLTimerQuery : public HwTimerQuery {
|
||||
struct State {
|
||||
struct {
|
||||
GLuint query;
|
||||
} gl;
|
||||
std::atomic<int64_t> elapsed{};
|
||||
};
|
||||
std::shared_ptr<State> state;
|
||||
};
|
||||
|
||||
struct GLStream : public HwStream {
|
||||
using HwStream::HwStream;
|
||||
@@ -233,8 +209,8 @@ private:
|
||||
OpenGLContext mContext;
|
||||
ShaderCompilerService mShaderCompilerService;
|
||||
|
||||
friend class TimerQueryFactory;
|
||||
friend class TimerQueryNativeFactory;
|
||||
friend class OpenGLTimerQueryFactory;
|
||||
friend class TimerQueryNative;
|
||||
OpenGLContext& getContext() noexcept { return mContext; }
|
||||
|
||||
ShaderCompilerService& getShaderCompilerService() noexcept {
|
||||
@@ -314,7 +290,7 @@ private:
|
||||
void updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer const* vb);
|
||||
|
||||
void framebufferTexture(TargetBufferInfo const& binfo,
|
||||
GLRenderTarget const* rt, GLenum attachment, uint8_t layerCount) noexcept;
|
||||
GLRenderTarget const* rt, GLenum attachment) noexcept;
|
||||
|
||||
void setRasterState(RasterState rs) noexcept;
|
||||
|
||||
@@ -335,8 +311,8 @@ private:
|
||||
void renderBufferStorage(GLuint rbo, GLenum internalformat, uint32_t width,
|
||||
uint32_t height, uint8_t samples) const noexcept;
|
||||
|
||||
void textureStorage(OpenGLDriver::GLTexture* t, uint32_t width, uint32_t height,
|
||||
uint32_t depth, bool useProtectedMemory) noexcept;
|
||||
void textureStorage(GLTexture* t,
|
||||
uint32_t width, uint32_t height, uint32_t depth) noexcept;
|
||||
|
||||
/* State tracking GL wrappers... */
|
||||
|
||||
@@ -369,8 +345,8 @@ private:
|
||||
}
|
||||
|
||||
using AttachmentArray = std::array<GLenum, MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT + 2>;
|
||||
static GLsizei getAttachments(AttachmentArray& attachments, TargetBufferFlags buffers,
|
||||
bool isDefaultFramebuffer) noexcept;
|
||||
static GLsizei getAttachments(AttachmentArray& attachments,
|
||||
GLRenderTarget const* rt, TargetBufferFlags buffers) noexcept;
|
||||
|
||||
// state required to represent the current render pass
|
||||
Handle<HwRenderTarget> mRenderPassTarget;
|
||||
@@ -379,10 +355,6 @@ private:
|
||||
GLboolean mRenderPassDepthWrite{};
|
||||
GLboolean mRenderPassStencilWrite{};
|
||||
|
||||
GLRenderPrimitive const* mBoundRenderPrimitive = nullptr;
|
||||
bool mValidProgram = false;
|
||||
|
||||
|
||||
void clearWithRasterPipe(TargetBufferFlags clearFlags,
|
||||
math::float4 const& linearColor, GLfloat depth, GLint stencil) noexcept;
|
||||
|
||||
@@ -425,6 +397,9 @@ private:
|
||||
void executeEveryNowAndThenOps() noexcept;
|
||||
std::vector<std::function<bool()>> mEveryNowAndThenOps;
|
||||
|
||||
// timer query implementation
|
||||
OpenGLTimerQueryInterface* mTimerQueryImpl = nullptr;
|
||||
|
||||
const Platform::DriverConfig mDriverConfig;
|
||||
Platform::DriverConfig const& getDriverConfig() const noexcept { return mDriverConfig; }
|
||||
|
||||
|
||||
@@ -18,17 +18,6 @@
|
||||
|
||||
#include "OpenGLDriverFactory.h"
|
||||
|
||||
#include <backend/AcquiredImage.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <utils/Invocable.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
Driver* OpenGLPlatform::createDefaultDriver(OpenGLPlatform* platform,
|
||||
@@ -38,27 +27,14 @@ Driver* OpenGLPlatform::createDefaultDriver(OpenGLPlatform* platform,
|
||||
|
||||
OpenGLPlatform::~OpenGLPlatform() noexcept = default;
|
||||
|
||||
void OpenGLPlatform::makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain,
|
||||
utils::Invocable<void()>, utils::Invocable<void(size_t)>) noexcept {
|
||||
makeCurrent(getCurrentContextType(), drawSwapChain, readSwapChain);
|
||||
}
|
||||
|
||||
bool OpenGLPlatform::isProtectedContextSupported() const noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OpenGLPlatform::isSRGBSwapChainSupported() const noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t OpenGLPlatform::getDefaultFramebufferObject() noexcept {
|
||||
uint32_t OpenGLPlatform::createDefaultRenderTarget() noexcept {
|
||||
return 0;
|
||||
}
|
||||
|
||||
OpenGLPlatform::ContextType OpenGLPlatform::getCurrentContextType() const noexcept {
|
||||
return ContextType::UNPROTECTED;
|
||||
}
|
||||
|
||||
void OpenGLPlatform::setPresentationTime(
|
||||
UTILS_UNUSED int64_t presentationTimeInNanosecond) noexcept {
|
||||
}
|
||||
@@ -129,14 +105,10 @@ AcquiredImage OpenGLPlatform::transformAcquiredImage(AcquiredImage source) noexc
|
||||
return source;
|
||||
}
|
||||
|
||||
TargetBufferFlags OpenGLPlatform::getPreservedFlags(UTILS_UNUSED SwapChain*) noexcept {
|
||||
TargetBufferFlags OpenGLPlatform::getPreservedFlags(UTILS_UNUSED SwapChain* swapChain) noexcept {
|
||||
return TargetBufferFlags::NONE;
|
||||
}
|
||||
|
||||
bool OpenGLPlatform::isSwapChainProtected(UTILS_UNUSED SwapChain*) noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OpenGLPlatform::isExtraContextSupported() const noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -16,25 +16,16 @@
|
||||
|
||||
#include "OpenGLProgram.h"
|
||||
|
||||
#include "GLUtils.h"
|
||||
#include "BlobCacheKey.h"
|
||||
#include "OpenGLDriver.h"
|
||||
#include "ShaderCompilerService.h"
|
||||
|
||||
#include <backend/Program.h>
|
||||
|
||||
#include <private/backend/BackendUtils.h>
|
||||
|
||||
#include <utils/debug.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <new>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <private/backend/BackendUtils.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
|
||||
@@ -28,9 +28,6 @@
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <array>
|
||||
#include <limits>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
@@ -16,132 +16,110 @@
|
||||
|
||||
#include "OpenGLTimerQuery.h"
|
||||
|
||||
#include "GLUtils.h"
|
||||
#include "OpenGLDriver.h"
|
||||
|
||||
#include <backend/Platform.h>
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/JobSystem.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Mutex.h>
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <utility>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
using namespace backend;
|
||||
using namespace GLUtils;
|
||||
|
||||
class OpenGLDriver;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
bool TimerQueryFactory::mGpuTimeSupported = false;
|
||||
bool OpenGLTimerQueryFactory::mGpuTimeSupported = false;
|
||||
|
||||
TimerQueryFactoryInterface* TimerQueryFactory::init(
|
||||
OpenGLPlatform& platform, OpenGLContext& context) noexcept {
|
||||
(void)context;
|
||||
OpenGLTimerQueryInterface* OpenGLTimerQueryFactory::init(
|
||||
OpenGLPlatform& platform, OpenGLDriver& driver) noexcept {
|
||||
(void)driver;
|
||||
|
||||
TimerQueryFactoryInterface* impl = nullptr;
|
||||
OpenGLTimerQueryInterface* impl;
|
||||
|
||||
#if defined(BACKEND_OPENGL_VERSION_GL) || defined(GL_EXT_disjoint_timer_query)
|
||||
auto& context = driver.getContext();
|
||||
if (context.ext.EXT_disjoint_timer_query) {
|
||||
// timer queries are available
|
||||
if (context.bugs.dont_use_timer_query && platform.canCreateFence()) {
|
||||
// however, they don't work well, revert to using fences if we can.
|
||||
impl = new(std::nothrow) TimerQueryFenceFactory(platform);
|
||||
impl = new(std::nothrow) OpenGLTimerQueryFence(platform);
|
||||
} else {
|
||||
impl = new(std::nothrow) TimerQueryNativeFactory(context);
|
||||
impl = new(std::nothrow) TimerQueryNative(driver);
|
||||
}
|
||||
mGpuTimeSupported = true;
|
||||
} else
|
||||
#endif
|
||||
if (platform.canCreateFence()) {
|
||||
// no timer queries, but we can use fences
|
||||
impl = new(std::nothrow) TimerQueryFenceFactory(platform);
|
||||
impl = new(std::nothrow) OpenGLTimerQueryFence(platform);
|
||||
mGpuTimeSupported = true;
|
||||
} else {
|
||||
// no queries, no fences -- that's a problem
|
||||
impl = new(std::nothrow) TimerQueryFallbackFactory();
|
||||
impl = new(std::nothrow) TimerQueryFallback();
|
||||
mGpuTimeSupported = false;
|
||||
}
|
||||
assert_invariant(impl);
|
||||
return impl;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
TimerQueryFactoryInterface::~TimerQueryFactoryInterface() = default;
|
||||
OpenGLTimerQueryInterface::~OpenGLTimerQueryInterface() = default;
|
||||
|
||||
// This is a backend synchronous call
|
||||
TimerQueryResult TimerQueryFactoryInterface::getTimerQueryValue(
|
||||
GLTimerQuery* tq, uint64_t* elapsedTime) noexcept {
|
||||
bool OpenGLTimerQueryInterface::getTimerQueryValue(GLTimerQuery* tq, uint64_t* elapsedTime) noexcept {
|
||||
if (UTILS_LIKELY(tq->state)) {
|
||||
int64_t const elapsed = tq->state->elapsed.load(std::memory_order_relaxed);
|
||||
if (elapsed > 0) {
|
||||
bool const available = elapsed > 0;
|
||||
if (available) {
|
||||
*elapsedTime = elapsed;
|
||||
return TimerQueryResult::AVAILABLE;
|
||||
}
|
||||
return TimerQueryResult(elapsed);
|
||||
return available;
|
||||
}
|
||||
return TimerQueryResult::ERROR;
|
||||
return false;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
#if defined(BACKEND_OPENGL_VERSION_GL) || defined(GL_EXT_disjoint_timer_query)
|
||||
|
||||
TimerQueryNativeFactory::TimerQueryNativeFactory(OpenGLContext& context)
|
||||
: mContext(context) {
|
||||
TimerQueryNative::TimerQueryNative(OpenGLDriver& driver)
|
||||
: mDriver(driver) {
|
||||
}
|
||||
|
||||
TimerQueryNativeFactory::~TimerQueryNativeFactory() = default;
|
||||
TimerQueryNative::~TimerQueryNative() = default;
|
||||
|
||||
void TimerQueryNativeFactory::createTimerQuery(GLTimerQuery* tq) {
|
||||
assert_invariant(!tq->state);
|
||||
|
||||
tq->state = std::make_shared<GLTimerQuery::State>();
|
||||
mContext.procs.genQueries(1u, &tq->state->gl.query);
|
||||
void TimerQueryNative::createTimerQuery(GLTimerQuery* tq) {
|
||||
if (UTILS_UNLIKELY(!tq->state)) {
|
||||
tq->state = std::make_shared<GLTimerQuery::State>();
|
||||
}
|
||||
mDriver.getContext().procs.genQueries(1u, &tq->state->gl.query);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
}
|
||||
|
||||
void TimerQueryNativeFactory::destroyTimerQuery(GLTimerQuery* tq) {
|
||||
void TimerQueryNative::destroyTimerQuery(GLTimerQuery* tq) {
|
||||
assert_invariant(tq->state);
|
||||
|
||||
mContext.procs.deleteQueries(1u, &tq->state->gl.query);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
|
||||
tq->state.reset();
|
||||
}
|
||||
|
||||
void TimerQueryNativeFactory::beginTimeElapsedQuery(GLTimerQuery* tq) {
|
||||
assert_invariant(tq->state);
|
||||
|
||||
tq->state->elapsed.store(int64_t(TimerQueryResult::NOT_READY), std::memory_order_relaxed);
|
||||
mContext.procs.beginQuery(GL_TIME_ELAPSED, tq->state->gl.query);
|
||||
mDriver.getContext().procs.deleteQueries(1u, &tq->state->gl.query);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
}
|
||||
|
||||
void TimerQueryNativeFactory::endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQuery* tq) {
|
||||
void TimerQueryNative::beginTimeElapsedQuery(GLTimerQuery* tq) {
|
||||
assert_invariant(tq->state);
|
||||
tq->state->elapsed.store(0);
|
||||
mDriver.getContext().procs.beginQuery(GL_TIME_ELAPSED, tq->state->gl.query);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
}
|
||||
|
||||
mContext.procs.endQuery(GL_TIME_ELAPSED);
|
||||
void TimerQueryNative::endTimeElapsedQuery(GLTimerQuery* tq) {
|
||||
assert_invariant(tq->state);
|
||||
mDriver.getContext().procs.endQuery(GL_TIME_ELAPSED);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
|
||||
std::weak_ptr<GLTimerQuery::State> const weak = tq->state;
|
||||
|
||||
driver.runEveryNowAndThen([&context = mContext, weak]() -> bool {
|
||||
mDriver.runEveryNowAndThen([context = mDriver.getContext(), weak]() -> bool {
|
||||
auto state = weak.lock();
|
||||
if (state) {
|
||||
GLuint available = 0;
|
||||
@@ -155,8 +133,6 @@ void TimerQueryNativeFactory::endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQ
|
||||
// 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);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
@@ -166,7 +142,7 @@ void TimerQueryNativeFactory::endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQ
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
TimerQueryFenceFactory::TimerQueryFenceFactory(OpenGLPlatform& platform)
|
||||
OpenGLTimerQueryFence::OpenGLTimerQueryFence(OpenGLPlatform& platform)
|
||||
: mPlatform(platform) {
|
||||
mQueue.reserve(2);
|
||||
mThread = std::thread([this]() {
|
||||
@@ -190,8 +166,7 @@ TimerQueryFenceFactory::TimerQueryFenceFactory(OpenGLPlatform& platform)
|
||||
});
|
||||
}
|
||||
|
||||
TimerQueryFenceFactory::~TimerQueryFenceFactory() {
|
||||
assert_invariant(mQueue.empty());
|
||||
OpenGLTimerQueryFence::~OpenGLTimerQueryFence() {
|
||||
if (mThread.joinable()) {
|
||||
std::unique_lock<utils::Mutex> lock(mLock);
|
||||
mExitRequested = true;
|
||||
@@ -203,26 +178,27 @@ TimerQueryFenceFactory::~TimerQueryFenceFactory() {
|
||||
}
|
||||
}
|
||||
|
||||
void TimerQueryFenceFactory::push(TimerQueryFenceFactory::Job&& job) {
|
||||
void OpenGLTimerQueryFence::enqueue(OpenGLTimerQueryFence::Job&& job) {
|
||||
std::unique_lock<utils::Mutex> const lock(mLock);
|
||||
mQueue.push_back(std::move(job));
|
||||
mQueue.push_back(std::forward<Job>(job));
|
||||
mCondition.notify_one();
|
||||
}
|
||||
|
||||
void TimerQueryFenceFactory::createTimerQuery(GLTimerQuery* tq) {
|
||||
assert_invariant(!tq->state);
|
||||
tq->state = std::make_shared<GLTimerQuery::State>();
|
||||
void OpenGLTimerQueryFence::createTimerQuery(GLTimerQuery* tq) {
|
||||
if (UTILS_UNLIKELY(!tq->state)) {
|
||||
tq->state = std::make_shared<GLTimerQuery::State>();
|
||||
}
|
||||
}
|
||||
|
||||
void TimerQueryFenceFactory::destroyTimerQuery(GLTimerQuery* tq) {
|
||||
void OpenGLTimerQueryFence::destroyTimerQuery(GLTimerQuery* tq) {
|
||||
assert_invariant(tq->state);
|
||||
tq->state.reset();
|
||||
}
|
||||
|
||||
void TimerQueryFenceFactory::beginTimeElapsedQuery(GLTimerQuery* tq) {
|
||||
void OpenGLTimerQueryFence::beginTimeElapsedQuery(GLTimerQuery* tq) {
|
||||
assert_invariant(tq->state);
|
||||
tq->state->elapsed.store(int64_t(TimerQueryResult::NOT_READY), std::memory_order_relaxed);
|
||||
tq->state->elapsed.store(0);
|
||||
|
||||
Platform::Fence* fence = mPlatform.createFence();
|
||||
std::weak_ptr<GLTimerQuery::State> const weak = tq->state;
|
||||
|
||||
// FIXME: this implementation of beginTimeElapsedQuery is usually wrong; it ends up
|
||||
@@ -231,11 +207,12 @@ void TimerQueryFenceFactory::beginTimeElapsedQuery(GLTimerQuery* tq) {
|
||||
// on a dummy target for instance, or somehow latch the begin time at the next renderpass
|
||||
// start.
|
||||
|
||||
push([&platform = mPlatform, fence = mPlatform.createFence(), weak]() {
|
||||
push([&platform = mPlatform, fence, weak]() {
|
||||
auto state = weak.lock();
|
||||
if (state) {
|
||||
platform.waitFence(fence, FENCE_WAIT_FOR_EVER);
|
||||
state->then = clock::now().time_since_epoch().count();
|
||||
int64_t const then = clock::now().time_since_epoch().count();
|
||||
state->elapsed.store(-then, std::memory_order_relaxed);
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_ASYNC_BEGIN("OpenGLTimerQueryFence", intptr_t(state.get()));
|
||||
}
|
||||
@@ -243,16 +220,19 @@ void TimerQueryFenceFactory::beginTimeElapsedQuery(GLTimerQuery* tq) {
|
||||
});
|
||||
}
|
||||
|
||||
void TimerQueryFenceFactory::endTimeElapsedQuery(OpenGLDriver&, GLTimerQuery* tq) {
|
||||
void OpenGLTimerQueryFence::endTimeElapsedQuery(GLTimerQuery* tq) {
|
||||
assert_invariant(tq->state);
|
||||
Platform::Fence* fence = mPlatform.createFence();
|
||||
std::weak_ptr<GLTimerQuery::State> const weak = tq->state;
|
||||
|
||||
push([&platform = mPlatform, fence = mPlatform.createFence(), weak]() {
|
||||
push([&platform = mPlatform, fence, weak]() {
|
||||
auto state = weak.lock();
|
||||
if (state) {
|
||||
platform.waitFence(fence, FENCE_WAIT_FOR_EVER);
|
||||
int64_t const now = clock::now().time_since_epoch().count();
|
||||
state->elapsed.store(now - state->then, std::memory_order_relaxed);
|
||||
int64_t const then = state->elapsed.load(std::memory_order_relaxed);
|
||||
assert_invariant(then < 0);
|
||||
state->elapsed.store(now + then, std::memory_order_relaxed);
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_ASYNC_END("OpenGLTimerQueryFence", intptr_t(state.get()));
|
||||
}
|
||||
@@ -262,32 +242,34 @@ void TimerQueryFenceFactory::endTimeElapsedQuery(OpenGLDriver&, GLTimerQuery* tq
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
TimerQueryFallbackFactory::TimerQueryFallbackFactory() = default;
|
||||
TimerQueryFallback::TimerQueryFallback() = default;
|
||||
|
||||
TimerQueryFallbackFactory::~TimerQueryFallbackFactory() = default;
|
||||
TimerQueryFallback::~TimerQueryFallback() = default;
|
||||
|
||||
void TimerQueryFallbackFactory::createTimerQuery(GLTimerQuery* tq) {
|
||||
assert_invariant(!tq->state);
|
||||
tq->state = std::make_shared<GLTimerQuery::State>();
|
||||
void TimerQueryFallback::createTimerQuery(GLTimerQuery* tq) {
|
||||
if (UTILS_UNLIKELY(!tq->state)) {
|
||||
tq->state = std::make_shared<GLTimerQuery::State>();
|
||||
}
|
||||
}
|
||||
|
||||
void TimerQueryFallbackFactory::destroyTimerQuery(GLTimerQuery* tq) {
|
||||
void TimerQueryFallback::destroyTimerQuery(GLTimerQuery* tq) {
|
||||
assert_invariant(tq->state);
|
||||
tq->state.reset();
|
||||
}
|
||||
|
||||
void TimerQueryFallbackFactory::beginTimeElapsedQuery(GLTimerQuery* tq) {
|
||||
void TimerQueryFallback::beginTimeElapsedQuery(OpenGLTimerQueryInterface::GLTimerQuery* tq) {
|
||||
assert_invariant(tq->state);
|
||||
// this implementation measures the CPU time, but we have no h/w support
|
||||
tq->state->then = clock::now().time_since_epoch().count();
|
||||
tq->state->elapsed.store(int64_t(TimerQueryResult::NOT_READY), std::memory_order_relaxed);
|
||||
int64_t const then = clock::now().time_since_epoch().count();
|
||||
tq->state->elapsed.store(-then, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void TimerQueryFallbackFactory::endTimeElapsedQuery(OpenGLDriver&, GLTimerQuery* tq) {
|
||||
void TimerQueryFallback::endTimeElapsedQuery(OpenGLTimerQueryInterface::GLTimerQuery* tq) {
|
||||
assert_invariant(tq->state);
|
||||
// this implementation measures the CPU time, but we have no h/w support
|
||||
int64_t const now = clock::now().time_since_epoch().count();
|
||||
tq->state->elapsed.store(now - tq->state->then, std::memory_order_relaxed);
|
||||
int64_t const then = tq->state->elapsed.load(std::memory_order_relaxed);
|
||||
assert_invariant(then < 0);
|
||||
tq->state->elapsed.store(now + then, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -17,41 +17,18 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_OPENGL_TIMERQUERY_H
|
||||
#define TNT_FILAMENT_BACKEND_OPENGL_TIMERQUERY_H
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include "DriverBase.h"
|
||||
#include "OpenGLDriver.h"
|
||||
|
||||
#include <utils/Condition.h>
|
||||
#include <utils/Mutex.h>
|
||||
|
||||
#include "gl_headers.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class OpenGLPlatform;
|
||||
class OpenGLContext;
|
||||
class OpenGLDriver;
|
||||
class TimerQueryFactoryInterface;
|
||||
|
||||
struct GLTimerQuery : public HwTimerQuery {
|
||||
struct State {
|
||||
struct {
|
||||
GLuint query;
|
||||
} gl;
|
||||
int64_t then{};
|
||||
std::atomic<int64_t> elapsed{};
|
||||
};
|
||||
std::shared_ptr<State> state;
|
||||
};
|
||||
class OpenGLTimerQueryInterface;
|
||||
|
||||
/*
|
||||
* We need two implementation of timer queries (only elapsed time), because
|
||||
@@ -61,80 +38,83 @@ struct GLTimerQuery : public HwTimerQuery {
|
||||
* These classes implement the various strategies...
|
||||
*/
|
||||
|
||||
class TimerQueryFactory {
|
||||
|
||||
class OpenGLTimerQueryFactory {
|
||||
static bool mGpuTimeSupported;
|
||||
public:
|
||||
static TimerQueryFactoryInterface* init(
|
||||
OpenGLPlatform& platform, OpenGLContext& context) noexcept;
|
||||
static OpenGLTimerQueryInterface* init(
|
||||
OpenGLPlatform& platform, OpenGLDriver& driver) noexcept;
|
||||
|
||||
static bool isGpuTimeSupported() noexcept {
|
||||
return mGpuTimeSupported;
|
||||
}
|
||||
};
|
||||
|
||||
class TimerQueryFactoryInterface {
|
||||
class OpenGLTimerQueryInterface {
|
||||
protected:
|
||||
using GLTimerQuery = filament::backend::GLTimerQuery;
|
||||
using GLTimerQuery = OpenGLDriver::GLTimerQuery;
|
||||
using clock = std::chrono::steady_clock;
|
||||
|
||||
public:
|
||||
virtual ~TimerQueryFactoryInterface();
|
||||
virtual ~OpenGLTimerQueryInterface();
|
||||
virtual void createTimerQuery(GLTimerQuery* query) = 0;
|
||||
virtual void destroyTimerQuery(GLTimerQuery* query) = 0;
|
||||
virtual void beginTimeElapsedQuery(GLTimerQuery* query) = 0;
|
||||
virtual void endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQuery* query) = 0;
|
||||
virtual void endTimeElapsedQuery(GLTimerQuery* query) = 0;
|
||||
|
||||
static TimerQueryResult getTimerQueryValue(GLTimerQuery* tq, uint64_t* elapsedTime) noexcept;
|
||||
static bool getTimerQueryValue(GLTimerQuery* tq, uint64_t* elapsedTime) noexcept;
|
||||
};
|
||||
|
||||
#if defined(BACKEND_OPENGL_VERSION_GL) || defined(GL_EXT_disjoint_timer_query)
|
||||
|
||||
class TimerQueryNativeFactory final : public TimerQueryFactoryInterface {
|
||||
class TimerQueryNative : public OpenGLTimerQueryInterface {
|
||||
public:
|
||||
explicit TimerQueryNativeFactory(OpenGLContext& context);
|
||||
~TimerQueryNativeFactory() override;
|
||||
explicit TimerQueryNative(OpenGLDriver& driver);
|
||||
~TimerQueryNative() override;
|
||||
private:
|
||||
void createTimerQuery(GLTimerQuery* query) override;
|
||||
void destroyTimerQuery(GLTimerQuery* query) override;
|
||||
void beginTimeElapsedQuery(GLTimerQuery* query) override;
|
||||
void endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQuery* query) override;
|
||||
OpenGLContext& mContext;
|
||||
void endTimeElapsedQuery(GLTimerQuery* query) override;
|
||||
OpenGLDriver& mDriver;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
class TimerQueryFenceFactory final : public TimerQueryFactoryInterface {
|
||||
class OpenGLTimerQueryFence : public OpenGLTimerQueryInterface {
|
||||
public:
|
||||
explicit TimerQueryFenceFactory(OpenGLPlatform& platform);
|
||||
~TimerQueryFenceFactory() override;
|
||||
explicit OpenGLTimerQueryFence(OpenGLPlatform& platform);
|
||||
~OpenGLTimerQueryFence() override;
|
||||
private:
|
||||
using Job = std::function<void()>;
|
||||
using Container = std::vector<Job>;
|
||||
|
||||
void createTimerQuery(GLTimerQuery* query) override;
|
||||
void destroyTimerQuery(GLTimerQuery* query) override;
|
||||
void beginTimeElapsedQuery(GLTimerQuery* tq) override;
|
||||
void endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQuery* tq) override;
|
||||
void endTimeElapsedQuery(GLTimerQuery* tq) override;
|
||||
|
||||
void push(Job&& job);
|
||||
void enqueue(Job&& job);
|
||||
template<typename CALLABLE, typename ... ARGS>
|
||||
void push(CALLABLE&& func, ARGS&& ... args) {
|
||||
enqueue(Job(std::bind(std::forward<CALLABLE>(func), std::forward<ARGS>(args)...)));
|
||||
}
|
||||
|
||||
OpenGLPlatform& mPlatform;
|
||||
std::thread mThread;
|
||||
mutable utils::Mutex mLock;
|
||||
mutable utils::Condition mCondition;
|
||||
Container mQueue;
|
||||
std::vector<Job> mQueue;
|
||||
bool mExitRequested = false;
|
||||
};
|
||||
|
||||
class TimerQueryFallbackFactory final : public TimerQueryFactoryInterface {
|
||||
class TimerQueryFallback : public OpenGLTimerQueryInterface {
|
||||
public:
|
||||
explicit TimerQueryFallbackFactory();
|
||||
~TimerQueryFallbackFactory() override;
|
||||
explicit TimerQueryFallback();
|
||||
~TimerQueryFallback() override;
|
||||
private:
|
||||
void createTimerQuery(GLTimerQuery* query) override;
|
||||
void destroyTimerQuery(GLTimerQuery* query) override;
|
||||
void beginTimeElapsedQuery(GLTimerQuery* query) override;
|
||||
void endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQuery* query) override;
|
||||
void endTimeElapsedQuery(GLTimerQuery* query) override;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
#include <cctype>
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
@@ -262,12 +261,12 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
|
||||
[this, &gl, program = std::move(program), token]() mutable {
|
||||
// compile the shaders
|
||||
std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders{};
|
||||
std::array<utils::CString, Program::SHADER_TYPE_COUNT> shaderSourceCode;
|
||||
compileShaders(gl,
|
||||
std::move(program.getShadersSource()),
|
||||
program.getSpecializationConstants(),
|
||||
program.isMultiview(),
|
||||
shaders,
|
||||
token->shaderSourceCode);
|
||||
shaderSourceCode);
|
||||
|
||||
// link the program
|
||||
GLuint const glProgram = linkProgram(gl, shaders, token->attributes);
|
||||
@@ -301,7 +300,6 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
|
||||
compileShaders(gl,
|
||||
std::move(program.getShadersSource()),
|
||||
program.getSpecializationConstants(),
|
||||
program.isMultiview(),
|
||||
token->gl.shaders,
|
||||
token->shaderSourceCode);
|
||||
|
||||
@@ -504,7 +502,6 @@ GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept {
|
||||
void ShaderCompilerService::compileShaders(OpenGLContext& context,
|
||||
Program::ShaderSource shadersSource,
|
||||
utils::FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
|
||||
bool multiview,
|
||||
std::array<GLuint, Program::SHADER_TYPE_COUNT>& outShaders,
|
||||
UTILS_UNUSED_IN_RELEASE std::array<CString, Program::SHADER_TYPE_COUNT>& outShaderSourceCode) noexcept {
|
||||
|
||||
@@ -518,16 +515,8 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
|
||||
};
|
||||
|
||||
std::string specializationConstantString;
|
||||
int32_t numViews = 2;
|
||||
for (auto const& sc : specializationConstants) {
|
||||
appendSpecConstantString(specializationConstantString, sc);
|
||||
if (sc.id == 8) {
|
||||
// This constant must match
|
||||
// ReservedSpecializationConstants::CONFIG_STEREO_EYE_COUNT
|
||||
// which we can't use here because it's defined in EngineEnums.h.
|
||||
// (we're breaking layering here, but it's for the good cause).
|
||||
numViews = std::get<int32_t>(sc.value);
|
||||
}
|
||||
}
|
||||
if (!specializationConstantString.empty()) {
|
||||
specializationConstantString += '\n';
|
||||
@@ -556,23 +545,17 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
|
||||
|
||||
if (UTILS_LIKELY(!shadersSource[i].empty())) {
|
||||
Program::ShaderBlob& shader = shadersSource[i];
|
||||
char* shader_src = reinterpret_cast<char*>(shader.data());
|
||||
size_t shader_len = shader.size();
|
||||
|
||||
// remove GOOGLE_cpp_style_line_directive
|
||||
process_GOOGLE_cpp_style_line_directive(context, shader_src, shader_len);
|
||||
|
||||
// replace the value of layout(num_views = X) for multiview extension
|
||||
if (multiview && stage == ShaderStage::VERTEX) {
|
||||
process_OVR_multiview2(context, numViews, shader_src, shader_len);
|
||||
}
|
||||
std::string_view const source = process_GOOGLE_cpp_style_line_directive(context,
|
||||
reinterpret_cast<char*>(shader.data()), shader.size());
|
||||
|
||||
// add support for ARB_shading_language_packing if needed
|
||||
auto const packingFunctions = process_ARB_shading_language_packing(context);
|
||||
|
||||
// split shader source, so we can insert the specialization constants and the packing
|
||||
// functions
|
||||
auto const [prolog, body] = splitShaderSource({ shader_src, shader_len });
|
||||
auto const [prolog, body] = splitShaderSource(source);
|
||||
|
||||
const std::array<const char*, 4> sources = {
|
||||
prolog.data(),
|
||||
@@ -595,7 +578,7 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
|
||||
#ifndef NDEBUG
|
||||
// for debugging we return the original shader source (without the modifications we
|
||||
// made here), otherwise the line numbers wouldn't match.
|
||||
outShaderSourceCode[i] = { shader_src, shader_len };
|
||||
outShaderSourceCode[i] = { source.data(), source.length() };
|
||||
#endif
|
||||
|
||||
outShaders[i] = shaderId;
|
||||
@@ -604,59 +587,15 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
|
||||
}
|
||||
|
||||
// If usages of the Google-style line directive are present, remove them, as some
|
||||
// drivers don't allow the quotation marks. This source modification happens in-place.
|
||||
void ShaderCompilerService::process_GOOGLE_cpp_style_line_directive(OpenGLContext& context,
|
||||
// drivers don't allow the quotation marks. This happens in-place.
|
||||
std::string_view ShaderCompilerService::process_GOOGLE_cpp_style_line_directive(OpenGLContext& context,
|
||||
char* source, size_t len) noexcept {
|
||||
if (!context.ext.GOOGLE_cpp_style_line_directive) {
|
||||
if (UTILS_UNLIKELY(requestsGoogleLineDirectivesExtension({ source, len }))) {
|
||||
removeGoogleLineDirectives(source, len); // length is unaffected
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look up the `source` to replace the number of eyes for multiview with the given number. This is
|
||||
// necessary for OpenGL because OpenGL relies on the number specified in shader files to determine
|
||||
// the number of views, which is assumed as a single digit, for multiview.
|
||||
// This source modification happens in-place.
|
||||
void ShaderCompilerService::process_OVR_multiview2(OpenGLContext& context,
|
||||
int32_t eyeCount, char* source, size_t len) noexcept {
|
||||
// We don't use regular expression in favor of performance.
|
||||
if (context.ext.OVR_multiview2) {
|
||||
const std::string_view shader{ source, len };
|
||||
const std::string_view layout = "layout";
|
||||
const std::string_view num_views = "num_views";
|
||||
size_t found = 0;
|
||||
while (true) {
|
||||
found = shader.find(layout, found);
|
||||
if (found == std::string_view::npos) {
|
||||
break;
|
||||
}
|
||||
found = shader.find_first_not_of(' ', found + layout.size());
|
||||
if (found == std::string_view::npos || shader[found] != '(') {
|
||||
continue;
|
||||
}
|
||||
found = shader.find_first_not_of(' ', found + 1);
|
||||
if (found == std::string_view::npos) {
|
||||
continue;
|
||||
}
|
||||
if (shader.compare(found, num_views.size(), num_views) != 0) {
|
||||
continue;
|
||||
}
|
||||
found = shader.find_first_not_of(' ', found + num_views.size());
|
||||
if (found == std::string_view::npos || shader[found] != '=') {
|
||||
continue;
|
||||
}
|
||||
found = shader.find_first_not_of(' ', found + 1);
|
||||
if (found == std::string_view::npos) {
|
||||
continue;
|
||||
}
|
||||
// We assume the value should be one-digit number.
|
||||
assert_invariant(eyeCount < 10);
|
||||
assert_invariant(!::isdigit(source[found + 1]));
|
||||
source[found] = '0' + eyeCount;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return { source, len };
|
||||
}
|
||||
|
||||
// Tragically, OpenGL 4.1 doesn't support unpackHalf2x16 (appeared in 4.2) and
|
||||
|
||||
@@ -134,14 +134,10 @@ private:
|
||||
OpenGLContext& context,
|
||||
Program::ShaderSource shadersSource,
|
||||
utils::FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
|
||||
bool multiview,
|
||||
std::array<GLuint, Program::SHADER_TYPE_COUNT>& outShaders,
|
||||
std::array<utils::CString, Program::SHADER_TYPE_COUNT>& outShaderSourceCode) noexcept;
|
||||
|
||||
static void process_GOOGLE_cpp_style_line_directive(OpenGLContext& context,
|
||||
char* source, size_t len) noexcept;
|
||||
|
||||
static void process_OVR_multiview2(OpenGLContext& context, int32_t eyeCount,
|
||||
static std::string_view process_GOOGLE_cpp_style_line_directive(OpenGLContext& context,
|
||||
char* source, size_t len) noexcept;
|
||||
|
||||
static std::string_view process_ARB_shading_language_packing(OpenGLContext& context) noexcept;
|
||||
|
||||
@@ -67,9 +67,6 @@ PFNGLDISCARDFRAMEBUFFEREXTPROC glDiscardFramebufferEXT;
|
||||
#ifdef GL_KHR_parallel_shader_compile
|
||||
PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glMaxShaderCompilerThreadsKHR;
|
||||
#endif
|
||||
#ifdef GL_OVR_multiview
|
||||
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR;
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
|
||||
// On Android, If we want to support a build system less than ANDROID_API 21, we need to
|
||||
@@ -120,9 +117,6 @@ void importGLESExtensionsEntryPoints() {
|
||||
#ifdef GL_KHR_parallel_shader_compile
|
||||
getProcAddress(glMaxShaderCompilerThreadsKHR, "glMaxShaderCompilerThreadsKHR");
|
||||
#endif
|
||||
#ifdef GL_OVR_multiview
|
||||
getProcAddress(glFramebufferTextureMultiviewOVR, "glFramebufferTextureMultiviewOVR");
|
||||
#endif
|
||||
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
|
||||
getProcAddress(glDispatchCompute, "glDispatchCompute");
|
||||
#endif
|
||||
|
||||
@@ -151,9 +151,6 @@ extern PFNGLDISCARDFRAMEBUFFEREXTPROC glDiscardFramebufferEXT;
|
||||
#ifdef GL_KHR_parallel_shader_compile
|
||||
extern PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glMaxShaderCompilerThreadsKHR;
|
||||
#endif
|
||||
#ifdef GL_OVR_multiview
|
||||
extern PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR;
|
||||
#endif
|
||||
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
|
||||
extern PFNGLDISPATCHCOMPUTEPROC glDispatchCompute;
|
||||
#endif
|
||||
|
||||
@@ -247,8 +247,8 @@ void PlatformCocoaGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept
|
||||
delete cocoaSwapChain;
|
||||
}
|
||||
|
||||
bool PlatformCocoaGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain) noexcept {
|
||||
void PlatformCocoaGL::makeCurrent(Platform::SwapChain* drawSwapChain,
|
||||
Platform::SwapChain* readSwapChain) noexcept {
|
||||
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
|
||||
"ContextManagerCocoa does not support using distinct draw/read swap chains.");
|
||||
CocoaGLSwapChain* swapChain = (CocoaGLSwapChain*)drawSwapChain;
|
||||
@@ -287,7 +287,6 @@ bool PlatformCocoaGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||
|
||||
swapChain->previousBounds = currentBounds;
|
||||
swapChain->previousWindowFrame = currentWindowFrame;
|
||||
return true;
|
||||
}
|
||||
|
||||
void PlatformCocoaGL::commit(Platform::SwapChain* swapChain) noexcept {
|
||||
|
||||
@@ -143,12 +143,11 @@ void PlatformCocoaTouchGL::destroySwapChain(Platform::SwapChain* swapChain) noex
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t PlatformCocoaTouchGL::getDefaultFramebufferObject() noexcept {
|
||||
uint32_t PlatformCocoaTouchGL::createDefaultRenderTarget() noexcept {
|
||||
return pImpl->mDefaultFramebuffer;
|
||||
}
|
||||
|
||||
bool PlatformCocoaTouchGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain) noexcept {
|
||||
void PlatformCocoaTouchGL::makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept {
|
||||
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
|
||||
"PlatformCocoaTouchGL does not support using distinct draw/read swap chains.");
|
||||
CAEAGLLayer* const glLayer = (__bridge CAEAGLLayer*) drawSwapChain;
|
||||
@@ -183,7 +182,6 @@ bool PlatformCocoaTouchGL::makeCurrent(ContextType type, SwapChain* drawSwapChai
|
||||
ASSERT_POSTCONDITION(status == GL_FRAMEBUFFER_COMPLETE, "Incomplete framebuffer.");
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, oldFramebuffer);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PlatformCocoaTouchGL::commit(Platform::SwapChain* swapChain) noexcept {
|
||||
|
||||
@@ -18,33 +18,16 @@
|
||||
|
||||
#include "opengl/GLUtils.h"
|
||||
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
|
||||
#include <backend/Platform.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <EGL/eglplatform.h>
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <sys/system_properties.h>
|
||||
#endif
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Invocable.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <new>
|
||||
#include <initializer_list>
|
||||
#include <utility>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#ifndef EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE
|
||||
# define EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE 0x3483
|
||||
@@ -65,6 +48,7 @@ UTILS_PRIVATE PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR = {};
|
||||
}
|
||||
using namespace glext;
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Utilities
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
@@ -153,8 +137,7 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const Platform::DriverCon
|
||||
ext.egl.KHR_gl_colorspace = extensions.has("EGL_KHR_gl_colorspace");
|
||||
ext.egl.KHR_create_context = extensions.has("EGL_KHR_create_context");
|
||||
ext.egl.KHR_no_config_context = extensions.has("EGL_KHR_no_config_context");
|
||||
ext.egl.KHR_surfaceless_context = extensions.has("EGL_KHR_surfaceless_context");
|
||||
ext.egl.EXT_protected_content = extensions.has("EGL_EXT_protected_content");
|
||||
ext.egl.KHR_surfaceless_context = extensions.has("KHR_surfaceless_context");
|
||||
if (ext.egl.KHR_create_context) {
|
||||
// KHR_create_context implies KHR_surfaceless_context for ES3.x contexts
|
||||
ext.egl.KHR_surfaceless_context = true;
|
||||
@@ -283,14 +266,12 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const Platform::DriverCon
|
||||
}
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY(
|
||||
egl.makeCurrent(mEGLContext, mEGLDummySurface, mEGLDummySurface) == EGL_FALSE)) {
|
||||
if (UTILS_UNLIKELY(!makeCurrent(mEGLDummySurface, mEGLDummySurface))) {
|
||||
// eglMakeCurrent failed
|
||||
logEglError("eglMakeCurrent");
|
||||
goto error;
|
||||
}
|
||||
|
||||
mCurrentContextType = ContextType::UNPROTECTED;
|
||||
mContextAttribs = std::move(contextAttribs);
|
||||
|
||||
initializeGlExtensions();
|
||||
@@ -309,13 +290,9 @@ error:
|
||||
if (mEGLContext) {
|
||||
eglDestroyContext(mEGLDisplay, mEGLContext);
|
||||
}
|
||||
if (mEGLContextProtected) {
|
||||
eglDestroyContext(mEGLDisplay, mEGLContextProtected);
|
||||
}
|
||||
|
||||
mEGLDummySurface = EGL_NO_SURFACE;
|
||||
mEGLContext = EGL_NO_CONTEXT;
|
||||
mEGLContextProtected = EGL_NO_CONTEXT;
|
||||
|
||||
eglTerminate(mEGLDisplay);
|
||||
eglReleaseThread();
|
||||
@@ -327,10 +304,6 @@ bool PlatformEGL::isExtraContextSupported() const noexcept {
|
||||
return ext.egl.KHR_surfaceless_context;
|
||||
}
|
||||
|
||||
bool PlatformEGL::isProtectedContextSupported() const noexcept {
|
||||
return ext.egl.EXT_protected_content;
|
||||
}
|
||||
|
||||
void PlatformEGL::createContext(bool shared) {
|
||||
EGLConfig config = ext.egl.KHR_no_config_context ? EGL_NO_CONFIG_KHR : mEGLConfig;
|
||||
|
||||
@@ -365,6 +338,15 @@ void PlatformEGL::releaseContext() noexcept {
|
||||
eglReleaseThread();
|
||||
}
|
||||
|
||||
EGLBoolean PlatformEGL::makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept {
|
||||
if (UTILS_UNLIKELY((drawSurface != mCurrentDrawSurface || readSurface != mCurrentReadSurface))) {
|
||||
mCurrentDrawSurface = drawSurface;
|
||||
mCurrentReadSurface = readSurface;
|
||||
return eglMakeCurrent(mEGLDisplay, drawSurface, readSurface, mEGLContext);
|
||||
}
|
||||
return EGL_TRUE;
|
||||
}
|
||||
|
||||
void PlatformEGL::terminate() noexcept {
|
||||
// it's always allowed to use EGL_NO_SURFACE, EGL_NO_CONTEXT
|
||||
eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
@@ -372,9 +354,6 @@ void PlatformEGL::terminate() noexcept {
|
||||
eglDestroySurface(mEGLDisplay, mEGLDummySurface);
|
||||
}
|
||||
eglDestroyContext(mEGLDisplay, mEGLContext);
|
||||
if (mEGLContextProtected != EGL_NO_CONTEXT) {
|
||||
eglDestroyContext(mEGLDisplay, mEGLContextProtected);
|
||||
}
|
||||
for (auto context : mAdditionalContexts) {
|
||||
eglDestroyContext(mEGLDisplay, context);
|
||||
}
|
||||
@@ -445,8 +424,6 @@ EGLConfig PlatformEGL::findSwapChainConfig(uint64_t flags, bool window, bool pbu
|
||||
return config;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
|
||||
bool PlatformEGL::isSRGBSwapChainSupported() const noexcept {
|
||||
return ext.egl.KHR_gl_colorspace;
|
||||
}
|
||||
@@ -454,23 +431,6 @@ bool PlatformEGL::isSRGBSwapChainSupported() const noexcept {
|
||||
Platform::SwapChain* PlatformEGL::createSwapChain(
|
||||
void* nativeWindow, uint64_t flags) noexcept {
|
||||
|
||||
Config attribs;
|
||||
if (ext.egl.KHR_gl_colorspace) {
|
||||
if (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) {
|
||||
attribs[EGL_GL_COLORSPACE_KHR] = EGL_GL_COLORSPACE_SRGB_KHR;
|
||||
}
|
||||
} else {
|
||||
flags &= ~SWAP_CHAIN_CONFIG_SRGB_COLORSPACE;
|
||||
}
|
||||
|
||||
if (ext.egl.EXT_protected_content) {
|
||||
if (flags & SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) {
|
||||
attribs[EGL_PROTECTED_CONTENT_EXT] = EGL_TRUE;
|
||||
}
|
||||
} else {
|
||||
flags &= ~SWAP_CHAIN_CONFIG_PROTECTED_CONTENT;
|
||||
}
|
||||
|
||||
EGLConfig config = EGL_NO_CONFIG_KHR;
|
||||
if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) {
|
||||
config = findSwapChainConfig(flags, true, false);
|
||||
@@ -478,34 +438,46 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
|
||||
config = mEGLConfig;
|
||||
}
|
||||
|
||||
EGLSurface sur = EGL_NO_SURFACE;
|
||||
if (UTILS_LIKELY(config != EGL_NO_CONFIG_KHR)) {
|
||||
sur = eglCreateWindowSurface(mEGLDisplay, config,
|
||||
(EGLNativeWindowType)nativeWindow, attribs.data());
|
||||
|
||||
if (UTILS_LIKELY(sur != EGL_NO_SURFACE)) {
|
||||
// this is not fatal
|
||||
eglSurfaceAttrib(mEGLDisplay, sur, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED);
|
||||
} else {
|
||||
logEglError("PlatformEGL::createSwapChain: eglCreateWindowSurface");
|
||||
}
|
||||
} else {
|
||||
// error already logged
|
||||
if (UTILS_UNLIKELY(config == EGL_NO_CONFIG_KHR)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SwapChainEGL* const sc = new(std::nothrow) SwapChainEGL({
|
||||
.sur = sur,
|
||||
.attribs = std::move(attribs),
|
||||
.nativeWindow = (EGLNativeWindowType)nativeWindow,
|
||||
.config = config,
|
||||
.flags = flags
|
||||
});
|
||||
return sc;
|
||||
Config attribs;
|
||||
|
||||
if (ext.egl.KHR_gl_colorspace) {
|
||||
if (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) {
|
||||
attribs[EGL_GL_COLORSPACE_KHR] = EGL_GL_COLORSPACE_SRGB_KHR;
|
||||
}
|
||||
}
|
||||
|
||||
EGLSurface sur = eglCreateWindowSurface(mEGLDisplay, config,
|
||||
(EGLNativeWindowType)nativeWindow, attribs.data());
|
||||
|
||||
if (UTILS_UNLIKELY(sur == EGL_NO_SURFACE)) {
|
||||
logEglError("eglCreateWindowSurface");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// this is not fatal
|
||||
eglSurfaceAttrib(mEGLDisplay, sur, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED);
|
||||
|
||||
return (SwapChain*)sur;
|
||||
}
|
||||
|
||||
Platform::SwapChain* PlatformEGL::createSwapChain(
|
||||
uint32_t width, uint32_t height, uint64_t flags) noexcept {
|
||||
|
||||
EGLConfig config = EGL_NO_CONFIG_KHR;
|
||||
if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) {
|
||||
config = findSwapChainConfig(flags, false, true);
|
||||
} else {
|
||||
config = mEGLConfig;
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY(config == EGL_NO_CONFIG_KHR)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Config attribs = {
|
||||
{ EGL_WIDTH, EGLint(width) },
|
||||
{ EGL_HEIGHT, EGLint(height) },
|
||||
@@ -515,149 +487,41 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
|
||||
if (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) {
|
||||
attribs[EGL_GL_COLORSPACE_KHR] = EGL_GL_COLORSPACE_SRGB_KHR;
|
||||
}
|
||||
} else {
|
||||
flags &= ~SWAP_CHAIN_CONFIG_SRGB_COLORSPACE;
|
||||
}
|
||||
|
||||
if (ext.egl.EXT_protected_content) {
|
||||
if (flags & SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) {
|
||||
attribs[EGL_PROTECTED_CONTENT_EXT] = EGL_TRUE;
|
||||
}
|
||||
} else {
|
||||
flags &= ~SWAP_CHAIN_CONFIG_PROTECTED_CONTENT;
|
||||
}
|
||||
EGLSurface sur = eglCreatePbufferSurface(mEGLDisplay, config, attribs.data());
|
||||
|
||||
EGLConfig config = EGL_NO_CONFIG_KHR;
|
||||
if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) {
|
||||
config = findSwapChainConfig(flags, true, false);
|
||||
} else {
|
||||
config = mEGLConfig;
|
||||
if (UTILS_UNLIKELY(sur == EGL_NO_SURFACE)) {
|
||||
logEglError("eglCreatePbufferSurface");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
EGLSurface sur = EGL_NO_SURFACE;
|
||||
if (UTILS_LIKELY(config != EGL_NO_CONFIG_KHR)) {
|
||||
sur = eglCreatePbufferSurface(mEGLDisplay, config, attribs.data());
|
||||
if (UTILS_UNLIKELY(sur == EGL_NO_SURFACE)) {
|
||||
logEglError("PlatformEGL::createSwapChain: eglCreatePbufferSurface");
|
||||
}
|
||||
} else {
|
||||
// error already logged
|
||||
}
|
||||
|
||||
SwapChainEGL* const sc = new(std::nothrow) SwapChainEGL({
|
||||
.sur = sur,
|
||||
.attribs = std::move(attribs),
|
||||
.config = config,
|
||||
.flags = flags
|
||||
});
|
||||
return sc;
|
||||
return (SwapChain*)sur;
|
||||
}
|
||||
|
||||
void PlatformEGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
|
||||
if (swapChain) {
|
||||
SwapChainEGL const* const sc = static_cast<SwapChainEGL const*>(swapChain);
|
||||
if (sc->sur != EGL_NO_SURFACE) {
|
||||
egl.makeCurrent(mEGLDummySurface, mEGLDummySurface);
|
||||
eglDestroySurface(mEGLDisplay, sc->sur);
|
||||
delete sc;
|
||||
}
|
||||
EGLSurface sur = (EGLSurface) swapChain;
|
||||
if (sur != EGL_NO_SURFACE) {
|
||||
makeCurrent(mEGLDummySurface, mEGLDummySurface);
|
||||
eglDestroySurface(mEGLDisplay, sur);
|
||||
}
|
||||
}
|
||||
|
||||
bool PlatformEGL::isSwapChainProtected(Platform::SwapChain* swapChain) noexcept {
|
||||
if (swapChain) {
|
||||
SwapChainEGL const* const sc = static_cast<SwapChainEGL const*>(swapChain);
|
||||
return bool(sc->flags & SWAP_CHAIN_CONFIG_PROTECTED_CONTENT);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
OpenGLPlatform::ContextType PlatformEGL::getCurrentContextType() const noexcept {
|
||||
return mCurrentContextType;
|
||||
}
|
||||
|
||||
bool PlatformEGL::makeCurrent(ContextType type,
|
||||
SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept {
|
||||
SwapChainEGL const* const dsc = static_cast<SwapChainEGL const*>(drawSwapChain);
|
||||
SwapChainEGL const* const rsc = static_cast<SwapChainEGL const*>(readSwapChain);
|
||||
EGLContext context = getContextForType(type);
|
||||
EGLBoolean const success = egl.makeCurrent(context, dsc->sur, rsc->sur);
|
||||
return success == EGL_TRUE ? true : false;
|
||||
}
|
||||
|
||||
void PlatformEGL::makeCurrent(Platform::SwapChain* drawSwapChain,
|
||||
Platform::SwapChain* readSwapChain,
|
||||
utils::Invocable<void()> preContextChange,
|
||||
utils::Invocable<void(size_t index)> postContextChange) noexcept {
|
||||
|
||||
assert_invariant(drawSwapChain);
|
||||
assert_invariant(readSwapChain);
|
||||
|
||||
ContextType type = ContextType::UNPROTECTED;
|
||||
if (ext.egl.EXT_protected_content) {
|
||||
bool const swapChainProtected = isSwapChainProtected(drawSwapChain);
|
||||
if (UTILS_UNLIKELY(swapChainProtected)) {
|
||||
// we need a protected context
|
||||
if (UTILS_UNLIKELY(mEGLContextProtected == EGL_NO_CONTEXT)) {
|
||||
// we don't have one, create it!
|
||||
EGLConfig config = ext.egl.KHR_no_config_context ? EGL_NO_CONFIG_KHR : mEGLConfig;
|
||||
Config protectedContextAttribs{ mContextAttribs };
|
||||
protectedContextAttribs[EGL_PROTECTED_CONTENT_EXT] = EGL_TRUE;
|
||||
mEGLContextProtected = eglCreateContext(mEGLDisplay, config, mEGLContext,
|
||||
protectedContextAttribs.data());
|
||||
if (UTILS_UNLIKELY(mEGLContextProtected == EGL_NO_CONTEXT)) {
|
||||
// couldn't create the protected context
|
||||
logEglError("eglCreateContext[EGL_PROTECTED_CONTENT_EXT]");
|
||||
ext.egl.EXT_protected_content = false;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
type = ContextType::PROTECTED;
|
||||
error: ;
|
||||
}
|
||||
|
||||
bool const contextChange = type != mCurrentContextType;
|
||||
mCurrentContextType = type;
|
||||
|
||||
if (UTILS_UNLIKELY(contextChange)) {
|
||||
preContextChange();
|
||||
bool const success = makeCurrent(mCurrentContextType, drawSwapChain, readSwapChain);
|
||||
if (UTILS_UNLIKELY(!success)) {
|
||||
logEglError("PlatformEGL::makeCurrent");
|
||||
if (mEGLContextProtected != EGL_NO_CONTEXT) {
|
||||
eglDestroyContext(mEGLDisplay, mEGLContextProtected);
|
||||
mEGLContextProtected = EGL_NO_CONTEXT;
|
||||
}
|
||||
mCurrentContextType = ContextType::UNPROTECTED;
|
||||
}
|
||||
if (UTILS_LIKELY(!swapChainProtected && mEGLContextProtected != EGL_NO_CONTEXT)) {
|
||||
// We don't need the protected context anymore, unbind and destroy right away.
|
||||
eglDestroyContext(mEGLDisplay, mEGLContextProtected);
|
||||
mEGLContextProtected = EGL_NO_CONTEXT;
|
||||
}
|
||||
size_t const contextIndex = (mCurrentContextType == ContextType::PROTECTED) ? 1 : 0;
|
||||
postContextChange(contextIndex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool const success = makeCurrent(mCurrentContextType, drawSwapChain, readSwapChain);
|
||||
if (UTILS_UNLIKELY(!success)) {
|
||||
logEglError("PlatformEGL::makeCurrent");
|
||||
Platform::SwapChain* readSwapChain) noexcept {
|
||||
EGLSurface drawSur = (EGLSurface) drawSwapChain;
|
||||
EGLSurface readSur = (EGLSurface) readSwapChain;
|
||||
if (drawSur != EGL_NO_SURFACE || readSur != EGL_NO_SURFACE) {
|
||||
makeCurrent(drawSur, readSur);
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformEGL::commit(Platform::SwapChain* swapChain) noexcept {
|
||||
if (swapChain) {
|
||||
SwapChainEGL const* const sc = static_cast<SwapChainEGL const*>(swapChain);
|
||||
if (sc->sur != EGL_NO_SURFACE) {
|
||||
eglSwapBuffers(mEGLDisplay, sc->sur);
|
||||
}
|
||||
EGLSurface sur = (EGLSurface) swapChain;
|
||||
if (sur != EGL_NO_SURFACE) {
|
||||
eglSwapBuffers(mEGLDisplay, sur);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
|
||||
bool PlatformEGL::canCreateFence() noexcept {
|
||||
return true;
|
||||
}
|
||||
@@ -696,10 +560,8 @@ FenceStatus PlatformEGL::waitFence(
|
||||
return FenceStatus::ERROR;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
|
||||
OpenGLPlatform::ExternalTexture* PlatformEGL::createExternalImageTexture() noexcept {
|
||||
ExternalTexture* outTexture = new(std::nothrow) ExternalTexture{};
|
||||
ExternalTexture* outTexture = new ExternalTexture{};
|
||||
glGenTextures(1, &outTexture->id);
|
||||
if (UTILS_LIKELY(ext.gl.OES_EGL_image_external_essl3)) {
|
||||
outTexture->target = GL_TEXTURE_EXTERNAL_OES;
|
||||
@@ -728,8 +590,6 @@ bool PlatformEGL::setExternalImage(void* externalImage,
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
|
||||
void PlatformEGL::initializeGlExtensions() noexcept {
|
||||
// We're guaranteed to be on an ES platform, since we're using EGL
|
||||
GLUtils::unordered_string_set glExtensions;
|
||||
@@ -738,17 +598,6 @@ void PlatformEGL::initializeGlExtensions() noexcept {
|
||||
ext.gl.OES_EGL_image_external_essl3 = glExtensions.has("GL_OES_EGL_image_external_essl3");
|
||||
}
|
||||
|
||||
EGLContext PlatformEGL::getContextForType(OpenGLPlatform::ContextType type) const noexcept {
|
||||
switch (type) {
|
||||
case ContextType::NONE:
|
||||
return EGL_NO_CONTEXT;
|
||||
case ContextType::UNPROTECTED:
|
||||
return mEGLContext;
|
||||
case ContextType::PROTECTED:
|
||||
return mEGLContextProtected;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
PlatformEGL::Config::Config() = default;
|
||||
@@ -785,23 +634,6 @@ void PlatformEGL::Config::erase(EGLint name) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
EGLBoolean PlatformEGL::EGL::makeCurrent(EGLContext context, EGLSurface drawSurface,
|
||||
EGLSurface readSurface) noexcept {
|
||||
if (UTILS_UNLIKELY((
|
||||
mCurrentContext != context ||
|
||||
drawSurface != mCurrentDrawSurface || readSurface != mCurrentReadSurface))) {
|
||||
EGLBoolean const success = eglMakeCurrent(
|
||||
mEGLDisplay, drawSurface, readSurface, context);
|
||||
if (success) {
|
||||
mCurrentDrawSurface = drawSurface;
|
||||
mCurrentReadSurface = readSurface;
|
||||
mCurrentContext = context;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
return EGL_TRUE;
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -14,34 +14,21 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <backend/AcquiredImage.h>
|
||||
#include <backend/Platform.h>
|
||||
#include <backend/platforms/PlatformEGL.h>
|
||||
#include <backend/platforms/PlatformEGLAndroid.h>
|
||||
|
||||
#include "opengl/GLUtils.h"
|
||||
#include "ExternalStreamManagerAndroid.h"
|
||||
|
||||
#include <android/api-level.h>
|
||||
#include <android/hardware_buffer.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <sys/system_properties.h>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <new>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// We require filament to be built with an API 19 toolchain, before that, OpenGLES 3.0 didn't exist
|
||||
// Actually, OpenGL ES 3.0 was added to API 18, but API 19 is the better target and
|
||||
// the minimum for Jetpack at the time of this comment.
|
||||
@@ -142,12 +129,11 @@ Driver* PlatformEGLAndroid::createDriver(void* sharedContext,
|
||||
}
|
||||
|
||||
void PlatformEGLAndroid::setPresentationTime(int64_t presentationTimeInNanosecond) noexcept {
|
||||
EGLSurface currentDrawSurface = eglGetCurrentSurface(EGL_DRAW);
|
||||
if (currentDrawSurface != EGL_NO_SURFACE) {
|
||||
if (mCurrentDrawSurface != EGL_NO_SURFACE) {
|
||||
if (eglPresentationTimeANDROID) {
|
||||
eglPresentationTimeANDROID(
|
||||
mEGLDisplay,
|
||||
currentDrawSurface,
|
||||
mCurrentDrawSurface,
|
||||
presentationTimeInNanosecond);
|
||||
}
|
||||
}
|
||||
@@ -179,28 +165,14 @@ int PlatformEGLAndroid::getOSVersion() const noexcept {
|
||||
|
||||
AcquiredImage PlatformEGLAndroid::transformAcquiredImage(AcquiredImage source) noexcept {
|
||||
// Convert the AHardwareBuffer to EGLImage.
|
||||
AHardwareBuffer const* const pHardwareBuffer = (const AHardwareBuffer*)source.image;
|
||||
|
||||
EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(pHardwareBuffer);
|
||||
EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID((const AHardwareBuffer*)source.image);
|
||||
if (!clientBuffer) {
|
||||
slog.e << "Unable to get EGLClientBuffer from AHardwareBuffer." << io::endl;
|
||||
return {};
|
||||
}
|
||||
|
||||
PlatformEGL::Config attributes;
|
||||
|
||||
if (__builtin_available(android 26, *)) {
|
||||
AHardwareBuffer_Desc desc;
|
||||
AHardwareBuffer_describe(pHardwareBuffer, &desc);
|
||||
bool const isProtectedContent =
|
||||
desc.usage & AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT;
|
||||
if (isProtectedContent) {
|
||||
attributes[EGL_PROTECTED_CONTENT_EXT] = EGL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
EGLImageKHR eglImage = eglCreateImageKHR(mEGLDisplay,
|
||||
EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attributes.data());
|
||||
// Note that this cannot be used to stream protected video (for now) because we do not set EGL_PROTECTED_CONTENT_EXT.
|
||||
EGLint attrs[] = { EGL_NONE, EGL_NONE };
|
||||
EGLImageKHR eglImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs);
|
||||
if (eglImage == EGL_NO_IMAGE_KHR) {
|
||||
slog.e << "eglCreateImageKHR returned no image." << io::endl;
|
||||
return {};
|
||||
|
||||
@@ -265,11 +265,10 @@ void PlatformGLX::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
bool PlatformGLX::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain) noexcept {
|
||||
void PlatformGLX::makeCurrent(
|
||||
Platform::SwapChain* drawSwapChain, Platform::SwapChain* readSwapChain) noexcept {
|
||||
g_glx.setCurrentContext(mGLXDisplay,
|
||||
(GLXDrawable)drawSwapChain, (GLXDrawable)readSwapChain, mGLXContext);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PlatformGLX::commit(Platform::SwapChain* swapChain) noexcept {
|
||||
|
||||
@@ -255,8 +255,8 @@ void PlatformWGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
|
||||
wglMakeCurrent(mWhdc, mContext);
|
||||
}
|
||||
|
||||
bool PlatformWGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain) noexcept {
|
||||
void PlatformWGL::makeCurrent(Platform::SwapChain* drawSwapChain,
|
||||
Platform::SwapChain* readSwapChain) noexcept {
|
||||
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
|
||||
"PlatformWGL does not support distinct draw/read swap chains.");
|
||||
|
||||
@@ -269,7 +269,6 @@ bool PlatformWGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||
wglMakeCurrent(0, NULL);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PlatformWGL::commit(Platform::SwapChain* swapChain) noexcept {
|
||||
|
||||
@@ -46,9 +46,8 @@ Platform::SwapChain* PlatformWebGL::createSwapChain(
|
||||
void PlatformWebGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
|
||||
}
|
||||
|
||||
bool PlatformWebGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain) noexcept {
|
||||
return true;
|
||||
void PlatformWebGL::makeCurrent(Platform::SwapChain* drawSwapChain,
|
||||
Platform::SwapChain* readSwapChain) noexcept {
|
||||
}
|
||||
|
||||
void PlatformWebGL::commit(Platform::SwapChain* swapChain) noexcept {
|
||||
|
||||
@@ -154,9 +154,15 @@ struct BlitterUniforms {
|
||||
|
||||
}// anonymous namespace
|
||||
|
||||
VulkanBlitter::VulkanBlitter(VkPhysicalDevice physicalDevice, VulkanCommands* commands) noexcept
|
||||
: mPhysicalDevice(physicalDevice),
|
||||
mCommands(commands) {}
|
||||
VulkanBlitter::VulkanBlitter() noexcept = default;
|
||||
|
||||
void VulkanBlitter::initialize(VkPhysicalDevice physicalDevice, VkDevice device,
|
||||
VmaAllocator allocator, VulkanCommands* commands) noexcept {
|
||||
mPhysicalDevice = physicalDevice;
|
||||
mDevice = device;
|
||||
mAllocator = allocator;
|
||||
mCommands = commands;
|
||||
}
|
||||
|
||||
void VulkanBlitter::resolve(VulkanAttachment dst, VulkanAttachment src) {
|
||||
|
||||
|
||||
@@ -32,7 +32,10 @@ struct VulkanProgram;
|
||||
|
||||
class VulkanBlitter {
|
||||
public:
|
||||
VulkanBlitter(VkPhysicalDevice physicalDevice, VulkanCommands* commands) noexcept;
|
||||
VulkanBlitter() noexcept;
|
||||
|
||||
void initialize(VkPhysicalDevice physicalDevice, VkDevice device,
|
||||
VmaAllocator allocator, VulkanCommands* commands) noexcept;
|
||||
|
||||
void blit(VkFilter filter,
|
||||
VulkanAttachment dst, const VkOffset3D* dstRectPair,
|
||||
@@ -44,6 +47,8 @@ public:
|
||||
|
||||
private:
|
||||
UTILS_UNUSED VkPhysicalDevice mPhysicalDevice;
|
||||
VkDevice mDevice;
|
||||
VmaAllocator mAllocator;
|
||||
VulkanCommands* mCommands;
|
||||
};
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ VulkanCommands::VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFam
|
||||
#endif
|
||||
}
|
||||
|
||||
void VulkanCommands::terminate() {
|
||||
VulkanCommands::~VulkanCommands() {
|
||||
wait();
|
||||
gc();
|
||||
vkDestroyCommandPool(mDevice, mPool, VKALLOC);
|
||||
@@ -435,8 +435,8 @@ void VulkanCommands::popGroupMarker() {
|
||||
auto const [marker, startTime] = mGroupMarkers->pop();
|
||||
auto const endTime = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::duration<double> diff = endTime - startTime;
|
||||
utils::slog.d << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms"
|
||||
<< utils::io::endl;
|
||||
utils::slog.d << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms\n"
|
||||
<< utils::io::flush;
|
||||
#else
|
||||
mGroupMarkers->pop();
|
||||
#endif
|
||||
|
||||
@@ -139,72 +139,71 @@ public:
|
||||
// - We do this because vkGetFenceStatus must be called from the rendering thread.
|
||||
//
|
||||
class VulkanCommands {
|
||||
public:
|
||||
VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
|
||||
VulkanContext* context, VulkanResourceAllocator* allocator);
|
||||
public:
|
||||
VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
|
||||
VulkanContext* context, VulkanResourceAllocator* allocator);
|
||||
~VulkanCommands();
|
||||
|
||||
void terminate();
|
||||
// Creates a "current" command buffer if none exists, otherwise returns the current one.
|
||||
VulkanCommandBuffer& get();
|
||||
|
||||
// Creates a "current" command buffer if none exists, otherwise returns the current one.
|
||||
VulkanCommandBuffer& get();
|
||||
// Submits the current command buffer if it exists, then sets "current" to null.
|
||||
// If there are no outstanding commands then nothing happens and this returns false.
|
||||
bool flush();
|
||||
|
||||
// Submits the current command buffer if it exists, then sets "current" to null.
|
||||
// If there are no outstanding commands then nothing happens and this returns false.
|
||||
bool flush();
|
||||
// Returns the "rendering finished" semaphore for the most recent flush and removes
|
||||
// it from the existing dependency chain. This is especially useful for setting up
|
||||
// vkQueuePresentKHR.
|
||||
VkSemaphore acquireFinishedSignal();
|
||||
|
||||
// Returns the "rendering finished" semaphore for the most recent flush and removes
|
||||
// it from the existing dependency chain. This is especially useful for setting up
|
||||
// vkQueuePresentKHR.
|
||||
VkSemaphore acquireFinishedSignal();
|
||||
// Takes a semaphore that signals when the next flush can occur. Only one injected
|
||||
// semaphore is allowed per flush. Useful after calling vkAcquireNextImageKHR.
|
||||
void injectDependency(VkSemaphore next);
|
||||
|
||||
// Takes a semaphore that signals when the next flush can occur. Only one injected
|
||||
// semaphore is allowed per flush. Useful after calling vkAcquireNextImageKHR.
|
||||
void injectDependency(VkSemaphore next);
|
||||
// Destroys all command buffers that are no longer in use.
|
||||
void gc();
|
||||
|
||||
// Destroys all command buffers that are no longer in use.
|
||||
void gc();
|
||||
// Waits for all outstanding command buffers to finish.
|
||||
void wait();
|
||||
|
||||
// Waits for all outstanding command buffers to finish.
|
||||
void wait();
|
||||
// Updates the atomic "status" variable in every extant fence.
|
||||
void updateFences();
|
||||
|
||||
// Updates the atomic "status" variable in every extant fence.
|
||||
void updateFences();
|
||||
|
||||
// Sets an observer who is notified every time a new command buffer has been made "current".
|
||||
// The observer's event handler can only be called during get().
|
||||
void setObserver(CommandBufferObserver* observer) { mObserver = observer; }
|
||||
// Sets an observer who is notified every time a new command buffer has been made "current".
|
||||
// The observer's event handler can only be called during get().
|
||||
void setObserver(CommandBufferObserver* observer) { mObserver = observer; }
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
void pushGroupMarker(char const* str, VulkanGroupMarkers::Timestamp timestamp = {});
|
||||
void pushGroupMarker(char const* str, VulkanGroupMarkers::Timestamp timestamp = {});
|
||||
|
||||
void popGroupMarker();
|
||||
void popGroupMarker();
|
||||
|
||||
void insertEventMarker(char const* string, uint32_t len);
|
||||
void insertEventMarker(char const* string, uint32_t len);
|
||||
|
||||
std::string getTopGroupMarker() const;
|
||||
std::string getTopGroupMarker() const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
static constexpr int CAPACITY = FVK_MAX_COMMAND_BUFFERS;
|
||||
VkDevice const mDevice;
|
||||
VkQueue const mQueue;
|
||||
VkCommandPool const mPool;
|
||||
VulkanContext const* mContext;
|
||||
private:
|
||||
static constexpr int CAPACITY = FVK_MAX_COMMAND_BUFFERS;
|
||||
VkDevice const mDevice;
|
||||
VkQueue const mQueue;
|
||||
VkCommandPool const mPool;
|
||||
VulkanContext const* mContext;
|
||||
|
||||
// int8 only goes up to 127, therefore capacity must be less than that.
|
||||
static_assert(CAPACITY < 128);
|
||||
int8_t mCurrentCommandBufferIndex = -1;
|
||||
VkSemaphore mSubmissionSignal = {};
|
||||
VkSemaphore mInjectedSignal = {};
|
||||
utils::FixedCapacityVector<std::unique_ptr<VulkanCommandBuffer>> mStorage;
|
||||
VkFence mFences[CAPACITY] = {};
|
||||
VkSemaphore mSubmissionSignals[CAPACITY] = {};
|
||||
uint8_t mAvailableBufferCount = CAPACITY;
|
||||
CommandBufferObserver* mObserver = nullptr;
|
||||
// int8 only goes up to 127, therefore capacity must be less than that.
|
||||
static_assert(CAPACITY < 128);
|
||||
int8_t mCurrentCommandBufferIndex = -1;
|
||||
VkSemaphore mSubmissionSignal = {};
|
||||
VkSemaphore mInjectedSignal = {};
|
||||
utils::FixedCapacityVector<std::unique_ptr<VulkanCommandBuffer>> mStorage;
|
||||
VkFence mFences[CAPACITY] = {};
|
||||
VkSemaphore mSubmissionSignals[CAPACITY] = {};
|
||||
uint8_t mAvailableBufferCount = CAPACITY;
|
||||
CommandBufferObserver* mObserver = nullptr;
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
std::unique_ptr<VulkanGroupMarkers> mGroupMarkers;
|
||||
std::unique_ptr<VulkanGroupMarkers> mCarriedOverMarkers;
|
||||
std::unique_ptr<VulkanGroupMarkers> mGroupMarkers;
|
||||
std::unique_ptr<VulkanGroupMarkers> mCarriedOverMarkers;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -39,16 +39,8 @@
|
||||
// not required for validation.
|
||||
|
||||
// FVK is short for Filament Vulkan
|
||||
|
||||
// Enables Android systrace
|
||||
#define FVK_DEBUG_SYSTRACE 0x00000001
|
||||
|
||||
// Group markers are used to denote collections of GPU commands. It is typically at the
|
||||
// granualarity of a renderpass. You can enable this along with FVK_DEBUG_DEBUG_UTILS to take
|
||||
// advantage of vkCmdBegin/EndDebugUtilsLabelEXT. You can also just enable this with
|
||||
// FVK_DEBUG_PRINT_GROUP_MARKERS to print the current marker to stdout.
|
||||
#define FVK_DEBUG_GROUP_MARKERS 0x00000002
|
||||
|
||||
#define FVK_DEBUG_GROUP_MARKERS 0x00000002
|
||||
#define FVK_DEBUG_TEXTURE 0x00000004
|
||||
#define FVK_DEBUG_LAYOUT_TRANSITION 0x00000008
|
||||
#define FVK_DEBUG_COMMAND_BUFFER 0x00000010
|
||||
@@ -63,17 +55,11 @@
|
||||
#define FVK_DEBUG_PIPELINE_CACHE 0x00002000
|
||||
#define FVK_DEBUG_ALLOCATION 0x00004000
|
||||
|
||||
// Enable the debug utils extension if it is available.
|
||||
#define FVK_DEBUG_DEBUG_UTILS 0x00008000
|
||||
|
||||
// Use this to debug potential Handle/Resource leakage. It will print out reference counts for all
|
||||
// the currently active resources.
|
||||
#define FVK_DEBUG_RESOURCE_LEAK 0x00010000
|
||||
|
||||
// Useful default combinations
|
||||
// Usefaul default combinations
|
||||
#define FVK_DEBUG_EVERYTHING 0xFFFFFFFF
|
||||
#define FVK_DEBUG_PERFORMANCE \
|
||||
FVK_DEBUG_SYSTRACE
|
||||
FVK_DEBUG_SYSTRACE | \
|
||||
FVK_DEBUG_GROUP_MARKERS
|
||||
|
||||
#define FVK_DEBUG_CORRECTNESS \
|
||||
FVK_DEBUG_VALIDATION | \
|
||||
@@ -86,40 +72,21 @@
|
||||
FVK_DEBUG_PRINT_GROUP_MARKERS
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE)
|
||||
#define FVK_DEBUG_FLAGS FVK_DEBUG_PERFORMANCE
|
||||
#else
|
||||
#define FVK_DEBUG_FLAGS 0
|
||||
#endif
|
||||
|
||||
#define FVK_ENABLED(flags) (((FVK_DEBUG_FLAGS) & (flags)) == (flags))
|
||||
|
||||
// Group marker only works only if validation or debug utils is enabled since it uses
|
||||
// vkCmd(Begin/End)DebugUtilsLabelEXT or vkCmdDebugMarker(Begin/End)EXT
|
||||
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
|
||||
static_assert(FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) || FVK_ENABLED(FVK_DEBUG_VALIDATION));
|
||||
#endif
|
||||
#define FVK_ENABLED(flags) ((FVK_DEBUG_FLAGS) & (flags))
|
||||
#define FVK_ENABLED_BOOL(flags) ((bool) FVK_ENABLED(flags))
|
||||
|
||||
// Ensure dependencies are met between debug options
|
||||
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
|
||||
static_assert(FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS));
|
||||
#endif
|
||||
|
||||
// Only enable debug utils if validation is enabled.
|
||||
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
|
||||
#endif
|
||||
|
||||
// end dependcy checks
|
||||
|
||||
// Shorthand for combination of enabled debug flags
|
||||
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) || FVK_ENABLED(FVK_DEBUG_TEXTURE)
|
||||
#define FVK_ENABLED_DEBUG_SAMPLER_NAME 1
|
||||
#else
|
||||
#define FVK_ENABLED_DEBUG_SAMPLER_NAME 0
|
||||
#endif
|
||||
|
||||
// end shorthands
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_SYSTRACE)
|
||||
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "VulkanHandles.h"
|
||||
#include "VulkanImageUtility.h"
|
||||
#include "VulkanMemory.h"
|
||||
#include "VulkanTexture.h"
|
||||
|
||||
#include <backend/platforms/VulkanPlatform.h>
|
||||
|
||||
@@ -118,9 +117,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugReportCallback(VkDebugReportFlagsEXT flags,
|
||||
utils::slog.e << utils::io::endl;
|
||||
return VK_FALSE;
|
||||
}
|
||||
#endif // FVK_EANBLED(FVK_DEBUG_VALIDATION)
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT types, const VkDebugUtilsMessengerCallbackDataEXT* cbdata,
|
||||
void* pUserData) {
|
||||
@@ -139,63 +136,9 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFla
|
||||
utils::slog.e << utils::io::endl;
|
||||
return VK_FALSE;
|
||||
}
|
||||
#endif // FVK_EANBLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
|
||||
}// anonymous namespace
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
using DebugUtils = VulkanDriver::DebugUtils;
|
||||
DebugUtils* DebugUtils::mSingleton = nullptr;
|
||||
|
||||
DebugUtils::DebugUtils(VkInstance instance, VkDevice device, VulkanContext const* context)
|
||||
: mInstance(instance), mDevice(device), mEnabled(context->isDebugUtilsSupported()) {
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
|
||||
// Also initialize debug utils messenger here
|
||||
if (mEnabled) {
|
||||
VkDebugUtilsMessengerCreateInfoEXT const createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
|
||||
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
|
||||
.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
|
||||
| VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
|
||||
.pfnUserCallback = debugUtilsCallback,
|
||||
};
|
||||
VkResult result = vkCreateDebugUtilsMessengerEXT(instance, &createInfo,
|
||||
VKALLOC, &mDebugMessenger);
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan debug messenger.");
|
||||
}
|
||||
#endif // FVK_EANBLED(FVK_DEBUG_VALIDATION)
|
||||
}
|
||||
|
||||
DebugUtils* DebugUtils::get() {
|
||||
assert_invariant(DebugUtils::mSingleton);
|
||||
return DebugUtils::mSingleton;
|
||||
}
|
||||
|
||||
DebugUtils::~DebugUtils() {
|
||||
if (mDebugMessenger) {
|
||||
vkDestroyDebugUtilsMessengerEXT(mInstance, mDebugMessenger, VKALLOC);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugUtils::setName(VkObjectType type, uint64_t handle, char const* name) {
|
||||
auto impl = DebugUtils::get();
|
||||
if (!impl->mEnabled) {
|
||||
return;
|
||||
}
|
||||
VkDebugUtilsObjectNameInfoEXT const info = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||
.pNext = nullptr,
|
||||
.objectType = type,
|
||||
.objectHandle = handle,
|
||||
.pObjectName = name,
|
||||
};
|
||||
vkSetDebugUtilsObjectNameEXT(impl->mDevice, &info);
|
||||
}
|
||||
#endif // FVK_EANBLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
} // anonymous namespace
|
||||
|
||||
using ImgUtil = VulkanImageUtility;
|
||||
|
||||
@@ -209,47 +152,60 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
|
||||
mAllocator(createAllocator(mPlatform->getInstance(), mPlatform->getPhysicalDevice(),
|
||||
mPlatform->getDevice())),
|
||||
mContext(context),
|
||||
mResourceAllocator(driverConfig.handleArenaSize, driverConfig.disableHandleUseAfterFreeCheck),
|
||||
mResourceAllocator(driverConfig.handleArenaSize),
|
||||
mResourceManager(&mResourceAllocator),
|
||||
mThreadSafeResourceManager(&mResourceAllocator),
|
||||
mCommands(mPlatform->getDevice(), mPlatform->getGraphicsQueue(),
|
||||
mPlatform->getGraphicsQueueFamilyIndex(), &mContext, &mResourceAllocator),
|
||||
mPipelineCache(&mResourceAllocator),
|
||||
mStagePool(mAllocator, &mCommands),
|
||||
mFramebufferCache(mPlatform->getDevice()),
|
||||
mSamplerCache(mPlatform->getDevice()),
|
||||
mBlitter(mPlatform->getPhysicalDevice(), &mCommands),
|
||||
mReadPixels(mPlatform->getDevice()),
|
||||
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported) {
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
DebugUtils::mSingleton =
|
||||
new DebugUtils(mPlatform->getInstance(), mPlatform->getDevice(), &context);
|
||||
#endif
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
|
||||
UTILS_UNUSED const PFN_vkCreateDebugReportCallbackEXT createDebugReportCallback
|
||||
= vkCreateDebugReportCallbackEXT;
|
||||
if (!context.isDebugUtilsSupported() && createDebugReportCallback) {
|
||||
VkResult result;
|
||||
if (mContext.isDebugUtilsSupported()) {
|
||||
VkDebugUtilsMessengerCreateInfoEXT const createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
|
||||
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
|
||||
.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
|
||||
| VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
|
||||
.pfnUserCallback = debugUtilsCallback,
|
||||
};
|
||||
result = vkCreateDebugUtilsMessengerEXT(mPlatform->getInstance(), &createInfo, VKALLOC,
|
||||
&mDebugMessenger);
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan debug messenger.");
|
||||
} else if (createDebugReportCallback) {
|
||||
VkDebugReportCallbackCreateInfoEXT const cbinfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT,
|
||||
.flags = VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT,
|
||||
.pfnCallback = debugReportCallback,
|
||||
};
|
||||
VkResult result = createDebugReportCallback(mPlatform->getInstance(), &cbinfo, VKALLOC,
|
||||
result = createDebugReportCallback(mPlatform->getInstance(), &cbinfo, VKALLOC,
|
||||
&mDebugCallback);
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan debug callback.");
|
||||
}
|
||||
#endif
|
||||
|
||||
mTimestamps = std::make_unique<VulkanTimestamps>(mPlatform->getDevice());
|
||||
mCommands.setObserver(&mPipelineCache);
|
||||
mCommands = std::make_unique<VulkanCommands>(mPlatform->getDevice(),
|
||||
mPlatform->getGraphicsQueue(), mPlatform->getGraphicsQueueFamilyIndex(), &mContext,
|
||||
&mResourceAllocator);
|
||||
mCommands->setObserver(&mPipelineCache);
|
||||
mPipelineCache.setDevice(mPlatform->getDevice(), mAllocator);
|
||||
|
||||
mEmptyTexture = createEmptyTexture(mPlatform->getDevice(), mPlatform->getPhysicalDevice(),
|
||||
mContext, mAllocator, &mCommands, mStagePool);
|
||||
// TOOD: move them all to be initialized by constructor
|
||||
mStagePool.initialize(mAllocator, mCommands.get());
|
||||
mFramebufferCache.initialize(mPlatform->getDevice());
|
||||
mSamplerCache.initialize(mPlatform->getDevice());
|
||||
|
||||
mEmptyTexture.reset(createEmptyTexture(mPlatform->getDevice(), mPlatform->getPhysicalDevice(),
|
||||
mContext, mAllocator, mCommands.get(), mStagePool));
|
||||
|
||||
mPipelineCache.setDummyTexture(mEmptyTexture->getPrimaryImageView());
|
||||
mBlitter.initialize(mPlatform->getPhysicalDevice(), mPlatform->getDevice(), mAllocator,
|
||||
mCommands.get());
|
||||
}
|
||||
|
||||
VulkanDriver::~VulkanDriver() noexcept = default;
|
||||
@@ -260,28 +216,26 @@ Driver* VulkanDriver::create(VulkanPlatform* platform, VulkanContext const& cont
|
||||
#if 0
|
||||
// this is useful for development, but too verbose even for debug builds
|
||||
// For reference on a 64-bits machine in Release mode:
|
||||
// VulkanSamplerGroup : 16 few
|
||||
// VulkanSamplerGroup : 24 few
|
||||
// HwStream : 24 few
|
||||
// VulkanFence : 32 few
|
||||
// VulkanProgram : 32 moderate
|
||||
// VulkanIndexBuffer : 64 moderate
|
||||
// VulkanBufferObject : 64 many
|
||||
// -- less than or equal 64 bytes
|
||||
// VulkanRenderPrimitive : 72 many
|
||||
// VulkanVertexBufferInfo : 96 moderate
|
||||
// VulkanSwapChain : 104 few
|
||||
// VulkanTimerQuery : 160 few
|
||||
// -- less than or equal 160 bytes
|
||||
// VulkanVertexBuffer : 192 moderate
|
||||
// VulkanTexture : 224 moderate
|
||||
// VulkanRenderTarget : 312 few
|
||||
// -- less than or equal to 312 bytes
|
||||
// VulkanFence : 40 few
|
||||
// VulkanProgram : 40 moderate
|
||||
// VulkanIndexBuffer : 72 moderate
|
||||
// VulkanBufferObject : 72 many
|
||||
// -- less than or equal 80 bytes
|
||||
// VulkanRenderPrimitive : 104 many
|
||||
// VulkanSwapChain : 112 few
|
||||
// VulkanTimerQuery : 168 few
|
||||
// -- less than or equal 176 bytes
|
||||
// VulkanTexture : 232 moderate
|
||||
// VulkanVertexBuffer : 312 moderate
|
||||
// VulkanRenderTarget : 320 few
|
||||
// -- less than or equal to 320 bytes
|
||||
|
||||
utils::slog.d
|
||||
<< "\nVulkanSwapChain: " << sizeof(VulkanSwapChain)
|
||||
<< "\nVulkanBufferObject: " << sizeof(VulkanBufferObject)
|
||||
<< "\nVulkanVertexBuffer: " << sizeof(VulkanVertexBuffer)
|
||||
<< "\nVulkanVertexBufferInfo: " << sizeof(VulkanVertexBufferInfo)
|
||||
<< "\nVulkanIndexBuffer: " << sizeof(VulkanIndexBuffer)
|
||||
<< "\nVulkanSamplerGroup: " << sizeof(VulkanSamplerGroup)
|
||||
<< "\nVulkanRenderPrimitive: " << sizeof(VulkanRenderPrimitive)
|
||||
@@ -312,11 +266,8 @@ ShaderModel VulkanDriver::getShaderModel() const noexcept {
|
||||
void VulkanDriver::terminate() {
|
||||
// Command buffers should come first since it might have commands depending on resources that
|
||||
// are about to be destroyed.
|
||||
mCommands.terminate();
|
||||
|
||||
delete mEmptyTexture;
|
||||
mResourceManager.clear();
|
||||
|
||||
mCommands.reset();
|
||||
mEmptyTexture.reset();
|
||||
mTimestamps.reset();
|
||||
|
||||
mBlitter.terminate();
|
||||
@@ -335,17 +286,14 @@ void VulkanDriver::terminate() {
|
||||
if (mDebugCallback) {
|
||||
vkDestroyDebugReportCallbackEXT(mPlatform->getInstance(), mDebugCallback, VKALLOC);
|
||||
}
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
assert_invariant(DebugUtils::mSingleton);
|
||||
delete DebugUtils::mSingleton;
|
||||
#endif
|
||||
|
||||
if (mDebugMessenger) {
|
||||
vkDestroyDebugUtilsMessengerEXT(mPlatform->getInstance(), mDebugMessenger, VKALLOC);
|
||||
}
|
||||
mPlatform->terminate();
|
||||
}
|
||||
|
||||
void VulkanDriver::tick(int) {
|
||||
mCommands.updateFences();
|
||||
mCommands->updateFences();
|
||||
}
|
||||
|
||||
// Garbage collection should not occur too frequently, only about once per frame. Internally, the
|
||||
@@ -357,14 +305,9 @@ void VulkanDriver::collectGarbage() {
|
||||
FVK_SYSTRACE_START("gc");
|
||||
// Command buffers need to be submitted and completed before other resources can be gc'd. And
|
||||
// its gc() function carrys out the *wait*.
|
||||
mCommands.gc();
|
||||
mCommands->gc();
|
||||
mStagePool.gc();
|
||||
mFramebufferCache.gc();
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
|
||||
mResourceAllocator.print();
|
||||
#endif
|
||||
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
void VulkanDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) {
|
||||
@@ -385,7 +328,7 @@ void VulkanDriver::setPresentationTime(int64_t monotonic_clock_ns) {
|
||||
void VulkanDriver::endFrame(uint32_t frameId) {
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("endframe");
|
||||
mCommands.flush();
|
||||
mCommands->flush();
|
||||
collectGarbage();
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
@@ -393,7 +336,7 @@ void VulkanDriver::endFrame(uint32_t frameId) {
|
||||
void VulkanDriver::flush(int) {
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("flush");
|
||||
mCommands.flush();
|
||||
mCommands->flush();
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
@@ -401,8 +344,8 @@ void VulkanDriver::finish(int dummy) {
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("finish");
|
||||
|
||||
mCommands.flush();
|
||||
mCommands.wait();
|
||||
mCommands->flush();
|
||||
mCommands->wait();
|
||||
|
||||
mReadPixels.runUntilComplete();
|
||||
FVK_SYSTRACE_END();
|
||||
@@ -416,10 +359,12 @@ void VulkanDriver::createSamplerGroupR(Handle<HwSamplerGroup> sbh, uint32_t coun
|
||||
|
||||
void VulkanDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
|
||||
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
|
||||
PrimitiveType pt) {
|
||||
auto rp = mResourceAllocator.construct<VulkanRenderPrimitive>(rph,
|
||||
&mResourceAllocator, pt, vbh, ibh);
|
||||
PrimitiveType pt, uint32_t offset,
|
||||
uint32_t minIndex, uint32_t maxIndex, uint32_t count) {
|
||||
auto rp = mResourceAllocator.construct<VulkanRenderPrimitive>(rph, &mResourceAllocator);
|
||||
mResourceManager.acquire(rp);
|
||||
VulkanDriver::setRenderPrimitiveBuffer(rph, vbh, ibh);
|
||||
VulkanDriver::setRenderPrimitiveRange(rph, pt, offset, minIndex, maxIndex, count);
|
||||
}
|
||||
|
||||
void VulkanDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
@@ -430,26 +375,10 @@ void VulkanDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
mResourceManager.release(rp);
|
||||
}
|
||||
|
||||
void VulkanDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
|
||||
uint8_t attributeCount, AttributeArray attributes) {
|
||||
auto vbi = mResourceAllocator.construct<VulkanVertexBufferInfo>(vbih,
|
||||
bufferCount, attributeCount, attributes);
|
||||
mResourceManager.acquire(vbi);
|
||||
}
|
||||
|
||||
void VulkanDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
|
||||
if (!vbih) {
|
||||
return;
|
||||
}
|
||||
auto vbi = mResourceAllocator.handle_cast<VulkanVertexBufferInfo*>(vbih);
|
||||
mResourceManager.release(vbi);
|
||||
}
|
||||
|
||||
|
||||
void VulkanDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh,
|
||||
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih) {
|
||||
auto vertexBuffer = mResourceAllocator.construct<VulkanVertexBuffer>(vbh,
|
||||
mContext, mStagePool, &mResourceAllocator, vertexCount, vbih);
|
||||
void VulkanDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint8_t bufferCount,
|
||||
uint8_t attributeCount, uint32_t elementCount, AttributeArray attributes) {
|
||||
auto vertexBuffer = mResourceAllocator.construct<VulkanVertexBuffer>(vbh, mContext, mStagePool,
|
||||
&mResourceAllocator, bufferCount, attributeCount, elementCount, attributes);
|
||||
mResourceManager.acquire(vertexBuffer);
|
||||
}
|
||||
|
||||
@@ -499,7 +428,7 @@ void VulkanDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
||||
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
|
||||
TextureUsage usage) {
|
||||
auto vktexture = mResourceAllocator.construct<VulkanTexture>(th, mPlatform->getDevice(),
|
||||
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, target, levels,
|
||||
mPlatform->getPhysicalDevice(), mContext, mAllocator, mCommands.get(), target, levels,
|
||||
format, samples, w, h, depth, usage, mStagePool);
|
||||
mResourceManager.acquire(vktexture);
|
||||
}
|
||||
@@ -511,7 +440,7 @@ void VulkanDriver::createTextureSwizzledR(Handle<HwTexture> th, SamplerType targ
|
||||
TextureSwizzle swizzleArray[] = {r, g, b, a};
|
||||
const VkComponentMapping swizzleMap = getSwizzleMap(swizzleArray);
|
||||
auto vktexture = mResourceAllocator.construct<VulkanTexture>(th, mPlatform->getDevice(),
|
||||
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, target, levels,
|
||||
mPlatform->getPhysicalDevice(), mContext, mAllocator, mCommands.get(), target, levels,
|
||||
format, samples, w, h, depth, usage, mStagePool, false /*heap allocated */, swizzleMap);
|
||||
mResourceManager.acquire(vktexture);
|
||||
}
|
||||
@@ -554,7 +483,7 @@ void VulkanDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {
|
||||
|
||||
void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
TargetBufferFlags targets, uint32_t width, uint32_t height, uint8_t samples,
|
||||
uint8_t layerCount, MRT color, TargetBufferInfo depth, TargetBufferInfo stencil) {
|
||||
MRT color, TargetBufferInfo depth, TargetBufferInfo stencil) {
|
||||
UTILS_UNUSED_IN_RELEASE math::vec2<uint32_t> tmin = {std::numeric_limits<uint32_t>::max()};
|
||||
UTILS_UNUSED_IN_RELEASE math::vec2<uint32_t> tmax = {0};
|
||||
UTILS_UNUSED_IN_RELEASE size_t attachmentCount = 0;
|
||||
@@ -606,7 +535,7 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
assert_invariant(tmin.x >= width && tmin.y >= height);
|
||||
|
||||
auto renderTarget = mResourceAllocator.construct<VulkanRenderTarget>(rth, mPlatform->getDevice(),
|
||||
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, width, height,
|
||||
mPlatform->getPhysicalDevice(), mContext, mAllocator, mCommands.get(), width, height,
|
||||
samples, colorTargets, depthStencil, mStagePool);
|
||||
mResourceManager.acquire(renderTarget);
|
||||
}
|
||||
@@ -624,7 +553,7 @@ void VulkanDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
|
||||
}
|
||||
|
||||
void VulkanDriver::createFenceR(Handle<HwFence> fh, int) {
|
||||
VulkanCommandBuffer const& commandBuffer = mCommands.get();
|
||||
VulkanCommandBuffer const& commandBuffer = mCommands->get();
|
||||
mResourceAllocator.construct<VulkanFence>(fh, commandBuffer.fence);
|
||||
}
|
||||
|
||||
@@ -635,7 +564,7 @@ void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE);
|
||||
}
|
||||
auto swapChain = mResourceAllocator.construct<VulkanSwapChain>(sch, mPlatform, mContext,
|
||||
mAllocator, &mCommands, mStagePool, nativeWindow, flags);
|
||||
mAllocator, mCommands.get(), mStagePool, nativeWindow, flags);
|
||||
mResourceManager.acquire(swapChain);
|
||||
}
|
||||
|
||||
@@ -648,7 +577,7 @@ void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t wi
|
||||
}
|
||||
assert_invariant(width > 0 && height > 0 && "Vulkan requires non-zero swap chain dimensions.");
|
||||
auto swapChain = mResourceAllocator.construct<VulkanSwapChain>(sch, mPlatform, mContext,
|
||||
mAllocator, &mCommands, mStagePool, nullptr, flags, VkExtent2D{width, height});
|
||||
mAllocator, mCommands.get(), mStagePool, nullptr, flags, VkExtent2D{width, height});
|
||||
mResourceManager.acquire(swapChain);
|
||||
}
|
||||
|
||||
@@ -656,10 +585,6 @@ void VulkanDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
|
||||
// nothing to do, timer query was constructed in createTimerQueryS
|
||||
}
|
||||
|
||||
Handle<HwVertexBufferInfo> VulkanDriver::createVertexBufferInfoS() noexcept {
|
||||
return mResourceAllocator.allocHandle<VulkanVertexBufferInfo>();
|
||||
}
|
||||
|
||||
Handle<HwVertexBuffer> VulkanDriver::createVertexBufferS() noexcept {
|
||||
return mResourceAllocator.allocHandle<VulkanVertexBuffer>();
|
||||
}
|
||||
@@ -874,19 +799,8 @@ bool VulkanDriver::isSRGBSwapChainSupported() {
|
||||
return mIsSRGBSwapChainSupported;
|
||||
}
|
||||
|
||||
bool VulkanDriver::isProtectedContentSupported() {
|
||||
// the SWAP_CHAIN_CONFIG_PROTECTED_CONTENT flag is not supported
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VulkanDriver::isStereoSupported(backend::StereoscopicType stereoscopicType) {
|
||||
switch (stereoscopicType) {
|
||||
case backend::StereoscopicType::INSTANCED:
|
||||
return true;
|
||||
case backend::StereoscopicType::MULTIVIEW:
|
||||
// TODO: implement multiview feature in Vulkan.
|
||||
return false;
|
||||
}
|
||||
bool VulkanDriver::isStereoSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VulkanDriver::isParallelShaderCompileSupported() {
|
||||
@@ -898,10 +812,6 @@ bool VulkanDriver::isDepthStencilResolveSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VulkanDriver::isProtectedTexturesSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VulkanDriver::isWorkaroundNeeded(Workaround workaround) {
|
||||
switch (workaround) {
|
||||
case Workaround::SPLIT_EASU: {
|
||||
@@ -988,12 +898,13 @@ void VulkanDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t in
|
||||
auto vb = mResourceAllocator.handle_cast<VulkanVertexBuffer*>(vbh);
|
||||
auto bo = mResourceAllocator.handle_cast<VulkanBufferObject*>(boh);
|
||||
assert_invariant(bo->bindingType == BufferObjectBinding::VERTEX);
|
||||
vb->setBuffer(mResourceAllocator, bo, index);
|
||||
|
||||
vb->setBuffer(bo, index);
|
||||
}
|
||||
|
||||
void VulkanDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
|
||||
uint32_t byteOffset) {
|
||||
VulkanCommandBuffer& commands = mCommands.get();
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
auto ib = mResourceAllocator.handle_cast<VulkanIndexBuffer*>(ibh);
|
||||
commands.acquire(ib);
|
||||
ib->buffer.loadFromCpu(commands.buffer(), p.buffer, byteOffset, p.size);
|
||||
@@ -1003,7 +914,7 @@ void VulkanDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor
|
||||
|
||||
void VulkanDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescriptor&& bd,
|
||||
uint32_t byteOffset) {
|
||||
VulkanCommandBuffer& commands = mCommands.get();
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
|
||||
auto bo = mResourceAllocator.handle_cast<VulkanBufferObject*>(boh);
|
||||
commands.acquire(bo);
|
||||
@@ -1014,7 +925,7 @@ void VulkanDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescript
|
||||
|
||||
void VulkanDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> boh,
|
||||
BufferDescriptor&& bd, uint32_t byteOffset) {
|
||||
VulkanCommandBuffer& commands = mCommands.get();
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
auto bo = mResourceAllocator.handle_cast<VulkanBufferObject*>(boh);
|
||||
commands.acquire(bo);
|
||||
// TODO: implement unsynchronized version
|
||||
@@ -1047,10 +958,10 @@ void VulkanDriver::update3DImage(Handle<HwTexture> th, uint32_t level, uint32_t
|
||||
void VulkanDriver::setupExternalImage(void* image) {
|
||||
}
|
||||
|
||||
TimerQueryResult VulkanDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
|
||||
bool VulkanDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
|
||||
VulkanTimerQuery* vtq = mResourceAllocator.handle_cast<VulkanTimerQuery*>(tqh);
|
||||
if (!vtq->isCompleted()) {
|
||||
return TimerQueryResult::NOT_READY;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto results = mTimestamps->getResult(vtq);
|
||||
@@ -1060,7 +971,7 @@ TimerQueryResult VulkanDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint
|
||||
uint64_t available1 = results[3];
|
||||
|
||||
if (available0 == 0 || available1 == 0) {
|
||||
return TimerQueryResult::NOT_READY;
|
||||
return false;
|
||||
}
|
||||
|
||||
ASSERT_POSTCONDITION(timestamp1 >= timestamp0, "Timestamps are not monotonically increasing.");
|
||||
@@ -1072,7 +983,7 @@ TimerQueryResult VulkanDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint
|
||||
float const period = mContext.getPhysicalDeviceLimits().timestampPeriod;
|
||||
uint64_t delta = uint64_t(float(timestamp1 - timestamp0) * period);
|
||||
*elapsedTime = delta;
|
||||
return TimerQueryResult::AVAILABLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
void VulkanDriver::setExternalImage(Handle<HwTexture> th, void* image) {
|
||||
@@ -1152,7 +1063,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
|
||||
VulkanRenderTarget* const rt = mResourceAllocator.handle_cast<VulkanRenderTarget*>(rth);
|
||||
const VkExtent2D extent = rt->getExtent();
|
||||
assert_invariant(rt == mDefaultRenderTarget || extent.width > 0 && extent.height > 0);
|
||||
assert_invariant(extent.width > 0 && extent.height > 0);
|
||||
|
||||
// Filament has the expectation that the contents of the swap chain are not preserved on the
|
||||
// first render pass. Note however that its contents are often preserved on subsequent render
|
||||
@@ -1179,7 +1090,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
// If that's the case, we need to change the layout of the texture to DEPTH_SAMPLER, which is a
|
||||
// more general layout. Otherwise, we prefer the DEPTH_ATTACHMENT layout, which is optimal for
|
||||
// the non-sampling case.
|
||||
VulkanCommandBuffer& commands = mCommands.get();
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
VkCommandBuffer const cmdbuffer = commands.buffer();
|
||||
|
||||
UTILS_NOUNROLL
|
||||
@@ -1304,14 +1215,22 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
}
|
||||
VkFramebuffer vkfb = mFramebufferCache.getFramebuffer(fbkey);
|
||||
|
||||
// Assign a label to the framebuffer for debugging purposes.
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS | FVK_DEBUG_DEBUG_UTILS)
|
||||
auto const topMarker = mCommands.getTopGroupMarker();
|
||||
if (!topMarker.empty()) {
|
||||
DebugUtils::setName(VK_OBJECT_TYPE_FRAMEBUFFER, reinterpret_cast<uint64_t>(vkfb),
|
||||
topMarker.c_str());
|
||||
// Assign a label to the framebuffer for debugging purposes.
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
if (UTILS_UNLIKELY(mContext.isDebugUtilsSupported())) {
|
||||
auto const topMarker = mCommands->getTopGroupMarker();
|
||||
if (!topMarker.empty()) {
|
||||
const VkDebugUtilsObjectNameInfoEXT info = {
|
||||
VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
|
||||
nullptr,
|
||||
VK_OBJECT_TYPE_FRAMEBUFFER,
|
||||
reinterpret_cast<uint64_t>(vkfb),
|
||||
topMarker.c_str(),
|
||||
};
|
||||
vkSetDebugUtilsObjectNameEXT(mPlatform->getDevice(), &info);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// The current command buffer now owns a reference to the render target and its attachments.
|
||||
// Note that we must acquire parent textures, not sidecars.
|
||||
@@ -1395,7 +1314,7 @@ void VulkanDriver::endRenderPass(int) {
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("endRenderPass");
|
||||
|
||||
VulkanCommandBuffer& commands = mCommands.get();
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
VkCommandBuffer cmdbuffer = commands.buffer();
|
||||
vkCmdEndRenderPass(cmdbuffer);
|
||||
|
||||
@@ -1448,7 +1367,7 @@ void VulkanDriver::nextSubpass(int) {
|
||||
assert_invariant(renderTarget);
|
||||
assert_invariant(mCurrentRenderPass.params.subpassMask);
|
||||
|
||||
vkCmdNextSubpass(mCommands.get().buffer(), VK_SUBPASS_CONTENTS_INLINE);
|
||||
vkCmdNextSubpass(mCommands->get().buffer(), VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
mPipelineCache.bindRenderPass(mCurrentRenderPass.renderPass,
|
||||
++mCurrentRenderPass.currentSubpass);
|
||||
@@ -1465,6 +1384,24 @@ void VulkanDriver::nextSubpass(int) {
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanDriver::setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph,
|
||||
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh) {
|
||||
auto primitive = mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
|
||||
primitive->setBuffers(mResourceAllocator.handle_cast<VulkanVertexBuffer*>(vbh),
|
||||
mResourceAllocator.handle_cast<VulkanIndexBuffer*>(ibh));
|
||||
}
|
||||
|
||||
void VulkanDriver::setRenderPrimitiveRange(Handle<HwRenderPrimitive> rph,
|
||||
PrimitiveType pt, uint32_t offset,
|
||||
uint32_t minIndex, uint32_t maxIndex, uint32_t count) {
|
||||
auto& primitive = *mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
|
||||
primitive.setPrimitiveType(pt);
|
||||
primitive.offset = offset * primitive.indexBuffer->elementSize;
|
||||
primitive.count = count;
|
||||
primitive.minIndex = minIndex;
|
||||
primitive.maxIndex = maxIndex > minIndex ? maxIndex : primitive.maxVertexCount - 1;
|
||||
}
|
||||
|
||||
void VulkanDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain> readSch) {
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("makeCurrent");
|
||||
@@ -1529,14 +1466,14 @@ void VulkanDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
|
||||
|
||||
void VulkanDriver::insertEventMarker(char const* string, uint32_t len) {
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
mCommands.insertEventMarker(string, len);
|
||||
mCommands->insertEventMarker(string, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
void VulkanDriver::pushGroupMarker(char const* string, uint32_t) {
|
||||
// Turns out all the markers are 0-terminated, so we can just pass it without len.
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
mCommands.pushGroupMarker(string);
|
||||
mCommands->pushGroupMarker(string);
|
||||
#endif
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START(string);
|
||||
@@ -1544,7 +1481,7 @@ void VulkanDriver::pushGroupMarker(char const* string, uint32_t) {
|
||||
|
||||
void VulkanDriver::popGroupMarker(int) {
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
mCommands.popGroupMarker();
|
||||
mCommands->popGroupMarker();
|
||||
#endif
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_END();
|
||||
@@ -1557,7 +1494,7 @@ void VulkanDriver::stopCapture(int) {}
|
||||
void VulkanDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y, uint32_t width,
|
||||
uint32_t height, PixelBufferDescriptor&& pbd) {
|
||||
VulkanRenderTarget* srcTarget = mResourceAllocator.handle_cast<VulkanRenderTarget*>(src);
|
||||
mCommands.flush();
|
||||
mCommands->flush();
|
||||
mReadPixels.run(
|
||||
srcTarget, x, y, width, height, mPlatform->getGraphicsQueueFamilyIndex(),
|
||||
std::move(pbd),
|
||||
@@ -1710,20 +1647,31 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void VulkanDriver::bindPipeline(PipelineState pipelineState) {
|
||||
void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
|
||||
const uint32_t instanceCount) {
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("draw");
|
||||
|
||||
VulkanCommandBuffer* commands = &mCommands.get();
|
||||
const VulkanVertexBufferInfo& vbi =
|
||||
*mResourceAllocator.handle_cast<VulkanVertexBufferInfo*>(pipelineState.vertexBufferInfo);
|
||||
VulkanCommandBuffer* commands = &mCommands->get();
|
||||
VkCommandBuffer cmdbuffer = commands->buffer();
|
||||
const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
|
||||
|
||||
Handle<HwProgram> programHandle = pipelineState.program;
|
||||
RasterState rasterState = pipelineState.rasterState;
|
||||
PolygonOffset depthOffset = pipelineState.polygonOffset;
|
||||
Viewport const& scissorBox = pipelineState.scissor;
|
||||
|
||||
auto* program = mResourceAllocator.handle_cast<VulkanProgram*>(programHandle);
|
||||
commands->acquire(program);
|
||||
commands->acquire(prim.indexBuffer);
|
||||
commands->acquire(prim.vertexBuffer);
|
||||
|
||||
// If this is a debug build, validate the current shader.
|
||||
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
|
||||
if (program->bundle.vertex == VK_NULL_HANDLE || program->bundle.fragment == VK_NULL_HANDLE) {
|
||||
utils::slog.e << "Binding missing shader: " << program->name.c_str() << utils::io::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Update the VK raster state.
|
||||
const VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget;
|
||||
@@ -1749,19 +1697,18 @@ void VulkanDriver::bindPipeline(PipelineState pipelineState) {
|
||||
.depthBiasSlopeFactor = depthOffset.slope
|
||||
};
|
||||
|
||||
// unfortunately in Vulkan the topology is per pipeline
|
||||
VkPrimitiveTopology const topology =
|
||||
VulkanPipelineCache::getPrimitiveTopology(pipelineState.primitiveType);
|
||||
|
||||
// Declare fixed-size arrays that get passed to the pipeCache and to vkCmdBindVertexBuffers.
|
||||
VkVertexInputAttributeDescription const* attribDesc = vbi.getAttribDescriptions();
|
||||
VkVertexInputBindingDescription const* bufferDesc = vbi.getBufferDescriptions();
|
||||
uint32_t const bufferCount = prim.vertexBuffer->attributes.size();
|
||||
VkVertexInputAttributeDescription const* attribDesc = prim.vertexBuffer->getAttribDescriptions();
|
||||
VkVertexInputBindingDescription const* bufferDesc = prim.vertexBuffer->getBufferDescriptions();
|
||||
VkBuffer const* buffers = prim.vertexBuffer->getVkBuffers();
|
||||
VkDeviceSize const* offsets = prim.vertexBuffer->getOffsets();
|
||||
|
||||
// Push state changes to the VulkanPipelineCache instance. This is fast and does not make VK calls.
|
||||
mPipelineCache.bindProgram(program);
|
||||
mPipelineCache.bindRasterState(vulkanRasterState);
|
||||
mPipelineCache.bindPrimitiveTopology(topology);
|
||||
mPipelineCache.bindVertexArray(attribDesc, bufferDesc, vbi.getAttributeCount());
|
||||
mPipelineCache.bindPrimitiveTopology(prim.primitiveTopology);
|
||||
mPipelineCache.bindVertexArray(attribDesc, bufferDesc, bufferCount);
|
||||
|
||||
// Query the program for the mapping from (SamplerGroupBinding,Offset) to (SamplerBinding),
|
||||
// where "SamplerBinding" is the integer in the GLSL, and SamplerGroupBinding is the abstract
|
||||
@@ -1771,11 +1718,7 @@ void VulkanDriver::bindPipeline(PipelineState pipelineState) {
|
||||
VulkanTexture* samplerTextures[VulkanPipelineCache::SAMPLER_BINDING_COUNT] = {nullptr};
|
||||
|
||||
auto const& bindingToSamplerIndex = program->getBindingToSamplerIndex();
|
||||
VulkanPipelineCache::UsageFlags usage = program->getUsage();
|
||||
|
||||
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
|
||||
auto const& bindingToName = program->getBindingToName();
|
||||
#endif
|
||||
VulkanPipelineCache::UsageFlags usage = program->getUsage();
|
||||
|
||||
UTILS_NOUNROLL
|
||||
for (uint8_t binding = 0; binding < VulkanPipelineCache::SAMPLER_BINDING_COUNT; binding++) {
|
||||
@@ -1809,21 +1752,15 @@ void VulkanDriver::bindPipeline(PipelineState pipelineState) {
|
||||
// matching characteristics. (e.g. if the missing texture is a 3D texture)
|
||||
if (UTILS_UNLIKELY(texture->getPrimaryImageLayout() == VulkanLayout::UNDEFINED)) {
|
||||
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
|
||||
utils::slog.w << "Uninitialized texture bound to '" << bindingToName[binding] << "'";
|
||||
utils::slog.w << "Uninitialized texture bound to '" << sampler.name.c_str() << "'";
|
||||
utils::slog.w << " in material '" << program->name.c_str() << "'";
|
||||
utils::slog.w << " at binding point " << +binding << utils::io::endl;
|
||||
utils::slog.w << " at binding point " << +sampler.binding << utils::io::endl;
|
||||
#endif
|
||||
texture = mEmptyTexture;
|
||||
texture = mEmptyTexture.get();
|
||||
}
|
||||
|
||||
SamplerParams const& samplerParams = boundSampler->s;
|
||||
VkSampler const vksampler = mSamplerCache.getSampler(samplerParams);
|
||||
|
||||
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
|
||||
VulkanDriver::DebugUtils::setName(VK_OBJECT_TYPE_SAMPLER,
|
||||
reinterpret_cast<uint64_t>(vksampler), bindingToName[binding].c_str());
|
||||
#endif
|
||||
|
||||
VkImageView imageView = VK_NULL_HANDLE;
|
||||
VkImageSubresourceRange const range = texture->getPrimaryViewRange();
|
||||
if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) &&
|
||||
@@ -1847,51 +1784,6 @@ void VulkanDriver::bindPipeline(PipelineState pipelineState) {
|
||||
|
||||
mPipelineCache.bindSamplers(samplerInfo, samplerTextures, usage);
|
||||
|
||||
// Bind a new pipeline if the pipeline state changed.
|
||||
// If allocation failed, skip the draw call and bail. We do not emit an error since the
|
||||
// validation layer will already do so.
|
||||
if (!mPipelineCache.bindPipeline(commands)) {
|
||||
return;
|
||||
}
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void VulkanDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("bindRenderPrimitive");
|
||||
|
||||
VulkanCommandBuffer* commands = &mCommands.get();
|
||||
VkCommandBuffer cmdbuffer = commands->buffer();
|
||||
const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
|
||||
commands->acquire(prim.indexBuffer);
|
||||
commands->acquire(prim.vertexBuffer);
|
||||
|
||||
// This *must* match the VulkanVertexBufferInfo that was bound in bindPipeline(). But we want
|
||||
// to allow to call this before bindPipeline(), so the validation can only happen in draw()
|
||||
VulkanVertexBufferInfo const* const vbi =
|
||||
mResourceAllocator.handle_cast<VulkanVertexBufferInfo*>(prim.vertexBuffer->vbih);
|
||||
|
||||
uint32_t const bufferCount = vbi->getAttributeCount();
|
||||
VkDeviceSize const* offsets = vbi->getOffsets();
|
||||
VkBuffer const* buffers = prim.vertexBuffer->getVkBuffers();
|
||||
|
||||
// Next bind the vertex buffers and index buffer. One potential performance improvement is to
|
||||
// avoid rebinding these if they are already bound, but since we do not (yet) support subranges
|
||||
// it would be rare for a client to make consecutive draw calls with the same render primitive.
|
||||
vkCmdBindVertexBuffers(cmdbuffer, 0, bufferCount, buffers, offsets);
|
||||
vkCmdBindIndexBuffer(cmdbuffer, prim.indexBuffer->buffer.getGpuBuffer(), 0,
|
||||
prim.indexBuffer->indexType);
|
||||
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("draw2");
|
||||
|
||||
VulkanCommandBuffer& commands = mCommands.get();
|
||||
VkCommandBuffer cmdbuffer = commands.buffer();
|
||||
|
||||
// Bind new descriptor sets if they need to change.
|
||||
// If descriptor set allocation failed, skip the draw call and bail. No need to emit an error
|
||||
// message since the validation layers already do so.
|
||||
@@ -1899,34 +1791,6 @@ void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins
|
||||
return;
|
||||
}
|
||||
|
||||
// Finally, make the actual draw call. TODO: support subranges
|
||||
const uint32_t firstIndex = indexOffset;
|
||||
const int32_t vertexOffset = 0;
|
||||
const uint32_t firstInstId = 0;
|
||||
|
||||
vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId);
|
||||
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void VulkanDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
|
||||
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
|
||||
VulkanRenderPrimitive* const rp = mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
|
||||
state.primitiveType = rp->type;
|
||||
state.vertexBufferInfo = rp->vertexBuffer->vbih;
|
||||
bindPipeline(state);
|
||||
bindRenderPrimitive(rph);
|
||||
draw2(indexOffset, indexCount, instanceCount);
|
||||
}
|
||||
|
||||
void VulkanDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
|
||||
// FIXME: implement me
|
||||
}
|
||||
|
||||
void VulkanDriver::scissor(Viewport scissorBox) {
|
||||
VulkanCommandBuffer& commands = mCommands.get();
|
||||
VkCommandBuffer cmdbuffer = commands.buffer();
|
||||
|
||||
// Set scissoring.
|
||||
// clamp left-bottom to 0,0 and avoid overflows
|
||||
constexpr int32_t maxvali = std::numeric_limits<int32_t>::max();
|
||||
@@ -1945,19 +1809,45 @@ void VulkanDriver::scissor(Viewport scissorBox) {
|
||||
.extent = { uint32_t(r - l), uint32_t(t - b) }
|
||||
};
|
||||
|
||||
const VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget;
|
||||
rt->transformClientRectToPlatform(&scissor);
|
||||
mPipelineCache.bindScissor(cmdbuffer, scissor);
|
||||
|
||||
// Bind a new pipeline if the pipeline state changed.
|
||||
// If allocation failed, skip the draw call and bail. We do not emit an error since the
|
||||
// validation layer will already do so.
|
||||
if (!mPipelineCache.bindPipeline(commands)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Next bind the vertex buffers and index buffer. One potential performance improvement is to
|
||||
// avoid rebinding these if they are already bound, but since we do not (yet) support subranges
|
||||
// it would be rare for a client to make consecutive draw calls with the same render primitive.
|
||||
vkCmdBindVertexBuffers(cmdbuffer, 0, bufferCount, buffers, offsets);
|
||||
vkCmdBindIndexBuffer(cmdbuffer, prim.indexBuffer->buffer.getGpuBuffer(), 0,
|
||||
prim.indexBuffer->indexType);
|
||||
|
||||
// Finally, make the actual draw call. TODO: support subranges
|
||||
const uint32_t indexCount = prim.count;
|
||||
const uint32_t firstIndex = prim.offset / prim.indexBuffer->elementSize;
|
||||
const int32_t vertexOffset = 0;
|
||||
const uint32_t firstInstId = 0;
|
||||
|
||||
vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId);
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void VulkanDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
|
||||
// FIXME: implement me
|
||||
}
|
||||
|
||||
void VulkanDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
VulkanTimerQuery* vtq = mResourceAllocator.handle_cast<VulkanTimerQuery*>(tqh);
|
||||
mTimestamps->beginQuery(&(mCommands.get()), vtq);
|
||||
mTimestamps->beginQuery(&(mCommands->get()), vtq);
|
||||
}
|
||||
|
||||
void VulkanDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
VulkanTimerQuery* vtq = mResourceAllocator.handle_cast<VulkanTimerQuery*>(tqh);
|
||||
mTimestamps->endQuery(&(mCommands.get()), vtq);
|
||||
mTimestamps->endQuery(&(mCommands->get()), vtq);
|
||||
}
|
||||
|
||||
void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, const char* methodName) noexcept {
|
||||
|
||||
@@ -45,30 +45,6 @@ public:
|
||||
static Driver* create(VulkanPlatform* platform, VulkanContext const& context,
|
||||
Platform::DriverConfig const& driverConfig) noexcept;
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
// Encapsulates the VK_EXT_debug_utils extension. In particular, we use
|
||||
// vkSetDebugUtilsObjectNameEXT and vkCreateDebugUtilsMessengerEXT
|
||||
class DebugUtils {
|
||||
public:
|
||||
static void setName(VkObjectType type, uint64_t handle, char const* name);
|
||||
|
||||
private:
|
||||
static DebugUtils* get();
|
||||
|
||||
DebugUtils(VkInstance instance, VkDevice device, VulkanContext const* context);
|
||||
~DebugUtils();
|
||||
|
||||
VkInstance const mInstance;
|
||||
VkDevice const mDevice;
|
||||
bool const mEnabled;
|
||||
VkDebugUtilsMessengerEXT mDebugMessenger = VK_NULL_HANDLE;
|
||||
|
||||
static DebugUtils* mSingleton;
|
||||
|
||||
friend class VulkanDriver;
|
||||
};
|
||||
#endif // FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
|
||||
private:
|
||||
void debugCommandBegin(CommandStream* cmds, bool synchronous,
|
||||
const char* methodName) noexcept override;
|
||||
@@ -101,18 +77,25 @@ private:
|
||||
VulkanDriver& operator=(VulkanDriver const&) = delete;
|
||||
|
||||
private:
|
||||
inline void setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, Handle<HwVertexBuffer> vbh,
|
||||
Handle<HwIndexBuffer> ibh);
|
||||
|
||||
inline void setRenderPrimitiveRange(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
|
||||
uint32_t offset, uint32_t minIndex, uint32_t maxIndex, uint32_t count);
|
||||
|
||||
void collectGarbage();
|
||||
|
||||
VulkanPlatform* mPlatform = nullptr;
|
||||
std::unique_ptr<VulkanCommands> mCommands;
|
||||
std::unique_ptr<VulkanTimestamps> mTimestamps;
|
||||
|
||||
VulkanTexture* mEmptyTexture;
|
||||
std::unique_ptr<VulkanTexture> mEmptyTexture;
|
||||
|
||||
VulkanSwapChain* mCurrentSwapChain = nullptr;
|
||||
VulkanRenderTarget* mDefaultRenderTarget = nullptr;
|
||||
VulkanRenderPass mCurrentRenderPass = {};
|
||||
VmaAllocator mAllocator = VK_NULL_HANDLE;
|
||||
VkDebugReportCallbackEXT mDebugCallback = VK_NULL_HANDLE;
|
||||
VkDebugUtilsMessengerEXT mDebugMessenger = VK_NULL_HANDLE;
|
||||
|
||||
VulkanContext mContext = {};
|
||||
VulkanResourceAllocator mResourceAllocator;
|
||||
@@ -122,7 +105,6 @@ private:
|
||||
// thread.
|
||||
VulkanThreadSafeResourceManager mThreadSafeResourceManager;
|
||||
|
||||
VulkanCommands mCommands;
|
||||
VulkanPipelineCache mPipelineCache;
|
||||
VulkanStagePool mStagePool;
|
||||
VulkanFboCache mFramebufferCache;
|
||||
|
||||
@@ -64,8 +64,7 @@ bool VulkanFboCache::FboKeyEqualFn::operator()(const FboKey& k1, const FboKey& k
|
||||
return true;
|
||||
}
|
||||
|
||||
VulkanFboCache::VulkanFboCache(VkDevice device)
|
||||
: mDevice(device) {}
|
||||
void VulkanFboCache::initialize(VkDevice device) noexcept { mDevice = device; }
|
||||
|
||||
VulkanFboCache::~VulkanFboCache() {
|
||||
ASSERT_POSTCONDITION(mFramebufferCache.empty() && mRenderPassCache.empty(),
|
||||
@@ -239,7 +238,7 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
|
||||
.format = config.colorFormat[i],
|
||||
.samples = (VkSampleCountFlagBits) config.samples,
|
||||
.loadOp = clear ? kClear : (discard ? kDontCare : kKeep),
|
||||
.storeOp = kEnableStore,
|
||||
.storeOp = config.samples == 1 ? kEnableStore : kDisableStore,
|
||||
.stencilLoadOp = kDontCare,
|
||||
.stencilStoreOp = kDisableStore,
|
||||
.initialLayout = ((!discard && config.initialColorLayoutMask & (1 << i)) || clear)
|
||||
|
||||
@@ -104,9 +104,10 @@ public:
|
||||
bool operator()(const FboKey& k1, const FboKey& k2) const;
|
||||
};
|
||||
|
||||
explicit VulkanFboCache(VkDevice device);
|
||||
~VulkanFboCache();
|
||||
|
||||
void initialize(VkDevice device) noexcept;
|
||||
|
||||
// Retrieves or creates a VkFramebuffer handle.
|
||||
VkFramebuffer getFramebuffer(FboKey config) noexcept;
|
||||
|
||||
|
||||
@@ -16,9 +16,7 @@
|
||||
|
||||
#include "VulkanHandles.h"
|
||||
|
||||
#include "VulkanDriver.h"
|
||||
#include "VulkanConstants.h"
|
||||
#include "VulkanDriver.h"
|
||||
#include "VulkanMemory.h"
|
||||
#include "spirv/VulkanSpirvUtils.h"
|
||||
|
||||
@@ -80,29 +78,8 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
|
||||
};
|
||||
VkResult result = vkCreateShaderModule(mDevice, &moduleInfo, VKALLOC, &module);
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create shader module.");
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
std::string name{ builder.getName().c_str(), builder.getName().size() };
|
||||
switch (static_cast<ShaderStage>(i)) {
|
||||
case ShaderStage::VERTEX:
|
||||
name += "_vs";
|
||||
break;
|
||||
case ShaderStage::FRAGMENT:
|
||||
name += "_fs";
|
||||
break;
|
||||
default:
|
||||
PANIC_POSTCONDITION("Unexpected stage");
|
||||
break;
|
||||
}
|
||||
VulkanDriver::DebugUtils::setName(VK_OBJECT_TYPE_SHADER_MODULE,
|
||||
reinterpret_cast<uint64_t>(module), name.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
|
||||
auto& bindingToName = mInfo->bindingToName;
|
||||
#endif
|
||||
|
||||
auto& groupInfo = builder.getSamplerGroupInfo();
|
||||
auto& bindingToSamplerIndex = mInfo->bindingToSamplerIndex;
|
||||
auto& usage = mInfo->usage;
|
||||
@@ -113,17 +90,13 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
|
||||
uint32_t const binding = samplers[i].binding;
|
||||
bindingToSamplerIndex[binding] = (groupInd << 8) | (0xff & i);
|
||||
usage = VulkanPipelineCache::getUsageFlags(binding, group.stageFlags, usage);
|
||||
|
||||
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
|
||||
bindingToName[binding] = samplers[i].name.c_str();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
|
||||
utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << modules[0]
|
||||
<< ", " << modules[1] << ")" << utils::io::endl;
|
||||
#endif
|
||||
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
|
||||
utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << bundle.vertex
|
||||
<< ", " << bundle.fragment << ")" << utils::io::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
VulkanProgram::VulkanProgram(VkDevice device, VkShaderModule vs, VkShaderModule fs,
|
||||
@@ -289,21 +262,22 @@ uint8_t VulkanRenderTarget::getColorTargetCount(const VulkanRenderPass& pass) co
|
||||
return count;
|
||||
}
|
||||
|
||||
VulkanVertexBufferInfo::VulkanVertexBufferInfo(
|
||||
uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes)
|
||||
: HwVertexBufferInfo(bufferCount, attributeCount),
|
||||
VulkanResource(VulkanResourceType::VERTEX_BUFFER_INFO),
|
||||
mInfo(attributes.size()) {
|
||||
VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
|
||||
VulkanResourceAllocator* allocator, uint8_t bufferCount, uint8_t attributeCount,
|
||||
uint32_t elementCount, AttributeArray const& attribs)
|
||||
: HwVertexBuffer(bufferCount, attributeCount, elementCount, attribs),
|
||||
VulkanResource(VulkanResourceType::VERTEX_BUFFER),
|
||||
mInfo(new PipelineInfo(attribs.size())),
|
||||
mResources(allocator) {
|
||||
auto attribDesc = mInfo->mSoa.data<PipelineInfo::ATTRIBUTE_DESCRIPTION>();
|
||||
auto bufferDesc = mInfo->mSoa.data<PipelineInfo::BUFFER_DESCRIPTION>();
|
||||
auto offsets = mInfo->mSoa.data<PipelineInfo::OFFSETS>();
|
||||
auto attribToBufferIndex = mInfo->mSoa.data<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>();
|
||||
std::fill(mInfo->mSoa.begin<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>(),
|
||||
mInfo->mSoa.end<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>(), -1);
|
||||
|
||||
auto attribDesc = mInfo.mSoa.data<PipelineInfo::ATTRIBUTE_DESCRIPTION>();
|
||||
auto bufferDesc = mInfo.mSoa.data<PipelineInfo::BUFFER_DESCRIPTION>();
|
||||
auto offsets = mInfo.mSoa.data<PipelineInfo::OFFSETS>();
|
||||
auto attribToBufferIndex = mInfo.mSoa.data<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>();
|
||||
std::fill(mInfo.mSoa.begin<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>(),
|
||||
mInfo.mSoa.end<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>(), -1);
|
||||
|
||||
for (uint32_t attribIndex = 0; attribIndex < attributes.size(); attribIndex++) {
|
||||
Attribute attrib = attributes[attribIndex];
|
||||
for (uint32_t attribIndex = 0; attribIndex < attribs.size(); attribIndex++) {
|
||||
Attribute attrib = attribs[attribIndex];
|
||||
bool const isInteger = attrib.flags & Attribute::FLAG_INTEGER_TARGET;
|
||||
bool const isNormalized = attrib.flags & Attribute::FLAG_NORMALIZED;
|
||||
VkFormat vkformat = getVkFormat(attrib.type, isNormalized, isInteger);
|
||||
@@ -315,7 +289,7 @@ VulkanVertexBufferInfo::VulkanVertexBufferInfo(
|
||||
// expects to receive floats or ints.
|
||||
if (attrib.buffer == Attribute::BUFFER_UNUSED) {
|
||||
vkformat = isInteger ? VK_FORMAT_R8G8B8A8_UINT : VK_FORMAT_R8G8B8A8_SNORM;
|
||||
attrib = attributes[0];
|
||||
attrib = attribs[0];
|
||||
}
|
||||
offsets[attribIndex] = attrib.offset;
|
||||
attribDesc[attribIndex] = {
|
||||
@@ -331,23 +305,14 @@ VulkanVertexBufferInfo::VulkanVertexBufferInfo(
|
||||
}
|
||||
}
|
||||
|
||||
VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
|
||||
VulkanResourceAllocator* allocator,
|
||||
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih)
|
||||
: HwVertexBuffer(vertexCount),
|
||||
VulkanResource(VulkanResourceType::VERTEX_BUFFER),
|
||||
vbih(vbih),
|
||||
mBuffers(MAX_VERTEX_BUFFER_COUNT), // TODO: can we do better here?
|
||||
mResources(allocator) {
|
||||
VulkanVertexBuffer::~VulkanVertexBuffer() {
|
||||
delete mInfo;
|
||||
}
|
||||
|
||||
void VulkanVertexBuffer::setBuffer(VulkanResourceAllocator const& allocator,
|
||||
VulkanBufferObject* bufferObject, uint32_t index) {
|
||||
VulkanVertexBufferInfo const* const vbi =
|
||||
const_cast<VulkanResourceAllocator&>(allocator).handle_cast<VulkanVertexBufferInfo*>(vbih);
|
||||
size_t const count = vbi->getAttributeCount();
|
||||
VkBuffer* const vkbuffers = getVkBuffers();
|
||||
int8_t const* const attribToBuffer = vbi->getAttributeToBuffer();
|
||||
void VulkanVertexBuffer::setBuffer(VulkanBufferObject* bufferObject, uint32_t index) {
|
||||
size_t count = attributes.size();
|
||||
auto vkbuffers = mInfo->mSoa.data<PipelineInfo::VK_BUFFER>();
|
||||
auto attribToBuffer = mInfo->mSoa.data<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>();
|
||||
for (uint8_t attribIndex = 0; attribIndex < count; attribIndex++) {
|
||||
if (attribToBuffer[attribIndex] == static_cast<int8_t>(index)) {
|
||||
vkbuffers[attribIndex] = bufferObject->buffer.getGpuBuffer();
|
||||
@@ -363,6 +328,35 @@ VulkanBufferObject::VulkanBufferObject(VmaAllocator allocator, VulkanStagePool&
|
||||
buffer(allocator, stagePool, getBufferObjectUsage(bindingType), byteCount),
|
||||
bindingType(bindingType) {}
|
||||
|
||||
void VulkanRenderPrimitive::setPrimitiveType(PrimitiveType pt) {
|
||||
this->type = pt;
|
||||
switch (pt) {
|
||||
case PrimitiveType::POINTS:
|
||||
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
|
||||
break;
|
||||
case PrimitiveType::LINES:
|
||||
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
|
||||
break;
|
||||
case PrimitiveType::LINE_STRIP:
|
||||
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
|
||||
break;
|
||||
case PrimitiveType::TRIANGLES:
|
||||
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
break;
|
||||
case PrimitiveType::TRIANGLE_STRIP:
|
||||
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanRenderPrimitive::setBuffers(VulkanVertexBuffer* vertexBuffer,
|
||||
VulkanIndexBuffer* indexBuffer) {
|
||||
this->vertexBuffer = vertexBuffer;
|
||||
this->indexBuffer = indexBuffer;
|
||||
mResources.acquire(vertexBuffer);
|
||||
mResources.acquire(indexBuffer);
|
||||
}
|
||||
|
||||
VulkanTimerQuery::VulkanTimerQuery(std::tuple<uint32_t, uint32_t> indices)
|
||||
: VulkanThreadSafeResource(VulkanResourceType::TIMER_QUERY),
|
||||
mStartingQueryIndex(std::get<0>(indices)),
|
||||
@@ -394,15 +388,4 @@ bool VulkanTimerQuery::isCompleted() noexcept {
|
||||
|
||||
VulkanTimerQuery::~VulkanTimerQuery() = default;
|
||||
|
||||
VulkanRenderPrimitive::VulkanRenderPrimitive(VulkanResourceAllocator* resourceAllocator,
|
||||
PrimitiveType pt, Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh)
|
||||
: VulkanResource(VulkanResourceType::RENDER_PRIMITIVE),
|
||||
mResources(resourceAllocator) {
|
||||
type = pt;
|
||||
vertexBuffer = resourceAllocator->handle_cast<VulkanVertexBuffer*>(vbh);
|
||||
indexBuffer = resourceAllocator->handle_cast<VulkanIndexBuffer*>(ibh);
|
||||
mResources.acquire(vertexBuffer);
|
||||
mResources.acquire(indexBuffer);
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -64,12 +64,6 @@ struct VulkanProgram : public HwProgram, VulkanResource {
|
||||
return mInfo->bindingToSamplerIndex;
|
||||
}
|
||||
|
||||
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
|
||||
inline utils::FixedCapacityVector<std::string> const& getBindingToName() const {
|
||||
return mInfo->bindingToName;
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
// TODO: handle compute shaders.
|
||||
// The expected order of shaders - from frontend to backend - is vertex, fragment, compute.
|
||||
@@ -77,11 +71,7 @@ private:
|
||||
|
||||
struct PipelineInfo {
|
||||
PipelineInfo()
|
||||
: bindingToSamplerIndex(MAX_SAMPLER_COUNT, 0xffff)
|
||||
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
|
||||
, bindingToName(MAX_SAMPLER_COUNT, "")
|
||||
#endif
|
||||
{}
|
||||
: bindingToSamplerIndex(MAX_SAMPLER_COUNT, 0xffff) {}
|
||||
|
||||
// This bitset maps to each of the sampler in the sampler groups associated with this
|
||||
// program, and whether each sampler is used in which shader (i.e. vert, frag, compute).
|
||||
@@ -90,12 +80,6 @@ private:
|
||||
// We store the samplerGroupIndex as the top 8-bit and the index within each group as the lower 8-bit.
|
||||
utils::FixedCapacityVector<uint16_t> bindingToSamplerIndex;
|
||||
VkShaderModule shaders[MAX_SHADER_MODULES] = { VK_NULL_HANDLE };
|
||||
|
||||
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
|
||||
// We store the sampler name mapped from binding index (only for debug purposes).
|
||||
utils::FixedCapacityVector<std::string> bindingToName;
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
PipelineInfo* mInfo;
|
||||
@@ -145,72 +129,55 @@ private:
|
||||
|
||||
struct VulkanBufferObject;
|
||||
|
||||
struct VulkanVertexBufferInfo : public HwVertexBufferInfo, VulkanResource {
|
||||
VulkanVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
|
||||
AttributeArray const& attributes);
|
||||
struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource {
|
||||
VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
|
||||
VulkanResourceAllocator* allocator, uint8_t bufferCount, uint8_t attributeCount,
|
||||
uint32_t elementCount, AttributeArray const& attributes);
|
||||
|
||||
inline VkVertexInputAttributeDescription const* getAttribDescriptions() const {
|
||||
return mInfo.mSoa.data<PipelineInfo::ATTRIBUTE_DESCRIPTION>();
|
||||
~VulkanVertexBuffer();
|
||||
|
||||
void setBuffer(VulkanBufferObject* bufferObject, uint32_t index);
|
||||
|
||||
inline VkVertexInputAttributeDescription const* getAttribDescriptions() {
|
||||
return mInfo->mSoa.data<PipelineInfo::ATTRIBUTE_DESCRIPTION>();
|
||||
}
|
||||
|
||||
inline VkVertexInputBindingDescription const* getBufferDescriptions() const {
|
||||
return mInfo.mSoa.data<PipelineInfo::BUFFER_DESCRIPTION>();
|
||||
inline VkVertexInputBindingDescription const* getBufferDescriptions() {
|
||||
return mInfo->mSoa.data<PipelineInfo::BUFFER_DESCRIPTION>();
|
||||
}
|
||||
|
||||
inline int8_t const* getAttributeToBuffer() const {
|
||||
return mInfo.mSoa.data<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>();
|
||||
inline VkBuffer const* getVkBuffers() const {
|
||||
return mInfo->mSoa.data<PipelineInfo::VK_BUFFER>();
|
||||
}
|
||||
|
||||
inline VkDeviceSize const* getOffsets() const {
|
||||
return mInfo.mSoa.data<PipelineInfo::OFFSETS>();
|
||||
}
|
||||
|
||||
size_t getAttributeCount() const noexcept {
|
||||
return mInfo.mSoa.size();
|
||||
return mInfo->mSoa.data<PipelineInfo::OFFSETS>();
|
||||
}
|
||||
|
||||
private:
|
||||
struct PipelineInfo {
|
||||
PipelineInfo(size_t size) : mSoa(size /* capacity */) {
|
||||
PipelineInfo(size_t size)
|
||||
: mSoa(size /* capacity */) {
|
||||
mSoa.resize(size);
|
||||
}
|
||||
|
||||
// These correspond to the index of the element in the SoA
|
||||
// These corresponds to the index of the element in the SoA
|
||||
static constexpr uint8_t ATTRIBUTE_DESCRIPTION = 0;
|
||||
static constexpr uint8_t BUFFER_DESCRIPTION = 1;
|
||||
static constexpr uint8_t OFFSETS = 2;
|
||||
static constexpr uint8_t ATTRIBUTE_TO_BUFFER_INDEX = 3;
|
||||
static constexpr uint8_t VK_BUFFER = 2;
|
||||
static constexpr uint8_t OFFSETS = 3;
|
||||
static constexpr uint8_t ATTRIBUTE_TO_BUFFER_INDEX = 4;
|
||||
|
||||
utils::StructureOfArrays<
|
||||
VkVertexInputAttributeDescription,
|
||||
VkVertexInputBindingDescription,
|
||||
VkBuffer,
|
||||
VkDeviceSize,
|
||||
int8_t
|
||||
> mSoa;
|
||||
};
|
||||
|
||||
PipelineInfo mInfo;
|
||||
};
|
||||
|
||||
struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource {
|
||||
VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
|
||||
VulkanResourceAllocator* allocator,
|
||||
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih);
|
||||
|
||||
void setBuffer(VulkanResourceAllocator const& allocator,
|
||||
VulkanBufferObject* bufferObject, uint32_t index);
|
||||
|
||||
inline VkBuffer const* getVkBuffers() const {
|
||||
return mBuffers.data();
|
||||
}
|
||||
|
||||
inline VkBuffer* getVkBuffers() {
|
||||
return mBuffers.data();
|
||||
}
|
||||
|
||||
Handle<HwVertexBufferInfo> vbih;
|
||||
private:
|
||||
utils::FixedCapacityVector<VkBuffer> mBuffers;
|
||||
PipelineInfo* mInfo;
|
||||
FixedSizeVulkanResourceManager<MAX_VERTEX_BUFFER_COUNT> mResources;
|
||||
};
|
||||
|
||||
@@ -243,15 +210,19 @@ struct VulkanSamplerGroup : public HwSamplerGroup, VulkanResource {
|
||||
};
|
||||
|
||||
struct VulkanRenderPrimitive : public HwRenderPrimitive, VulkanResource {
|
||||
VulkanRenderPrimitive(VulkanResourceAllocator* resourceAllocator,
|
||||
PrimitiveType pt, Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh);
|
||||
VulkanRenderPrimitive(VulkanResourceAllocator* allocator)
|
||||
: VulkanResource(VulkanResourceType::RENDER_PRIMITIVE),
|
||||
mResources(allocator) {}
|
||||
|
||||
~VulkanRenderPrimitive() {
|
||||
mResources.clear();
|
||||
}
|
||||
|
||||
void setPrimitiveType(PrimitiveType pt);
|
||||
void setBuffers(VulkanVertexBuffer* vertexBuffer, VulkanIndexBuffer* indexBuffer);
|
||||
VulkanVertexBuffer* vertexBuffer = nullptr;
|
||||
VulkanIndexBuffer* indexBuffer = nullptr;
|
||||
VkPrimitiveTopology primitiveTopology;
|
||||
|
||||
private:
|
||||
// Keep references to the vertex buffer and the index buffer.
|
||||
|
||||
@@ -563,14 +563,6 @@ VulkanPipelineCache::PipelineLayoutCacheEntry* VulkanPipelineCache::getOrCreateP
|
||||
void VulkanPipelineCache::bindProgram(VulkanProgram* program) noexcept {
|
||||
mPipelineRequirements.shaders[0] = program->getVertexShader();
|
||||
mPipelineRequirements.shaders[1] = program->getFragmentShader();
|
||||
|
||||
// If this is a debug build, validate the current shader.
|
||||
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
|
||||
if (mPipelineRequirements.shaders[0] == VK_NULL_HANDLE ||
|
||||
mPipelineRequirements.shaders[1] == VK_NULL_HANDLE) {
|
||||
utils::slog.e << "Binding missing shader: " << program->name.c_str() << utils::io::endl;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void VulkanPipelineCache::bindRasterState(const RasterState& rasterState) noexcept {
|
||||
@@ -636,8 +628,12 @@ void VulkanPipelineCache::unbindImageView(VkImageView imageView) noexcept {
|
||||
|
||||
void VulkanPipelineCache::bindUniformBufferObject(uint32_t bindingIndex,
|
||||
VulkanBufferObject* bufferObject, VkDeviceSize offset, VkDeviceSize size) noexcept {
|
||||
VkBuffer buffer = bufferObject->buffer.getGpuBuffer();
|
||||
bindUniformBuffer(bindingIndex, bufferObject->buffer.getGpuBuffer(), offset, size);
|
||||
mPipelineBoundResources.acquire(bufferObject);
|
||||
}
|
||||
|
||||
void VulkanPipelineCache::bindUniformBuffer(uint32_t bindingIndex, VkBuffer buffer,
|
||||
VkDeviceSize offset, VkDeviceSize size) noexcept {
|
||||
ASSERT_POSTCONDITION(bindingIndex < UBUFFER_BINDING_COUNT,
|
||||
"Uniform bindings overflow: index = %d, capacity = %d.", bindingIndex,
|
||||
UBUFFER_BINDING_COUNT);
|
||||
@@ -653,8 +649,6 @@ void VulkanPipelineCache::bindUniformBufferObject(uint32_t bindingIndex,
|
||||
|
||||
key.uniformBufferOffsets[bindingIndex] = offset;
|
||||
key.uniformBufferSizes[bindingIndex] = size;
|
||||
|
||||
mPipelineBoundResources.acquire(bufferObject);
|
||||
}
|
||||
|
||||
void VulkanPipelineCache::bindSamplers(VkDescriptorImageInfo samplers[SAMPLER_BINDING_COUNT],
|
||||
|
||||
@@ -157,6 +157,8 @@ public:
|
||||
void bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept;
|
||||
void bindUniformBufferObject(uint32_t bindingIndex, VulkanBufferObject* bufferObject,
|
||||
VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE) noexcept;
|
||||
void bindUniformBuffer(uint32_t bindingIndex, VkBuffer buffer,
|
||||
VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE) noexcept;
|
||||
void bindSamplers(VkDescriptorImageInfo samplers[SAMPLER_BINDING_COUNT],
|
||||
VulkanTexture* textures[SAMPLER_BINDING_COUNT], UsageFlags flags) noexcept;
|
||||
void bindInputAttachment(uint32_t bindingIndex, VkDescriptorImageInfo imageInfo) noexcept;
|
||||
@@ -191,19 +193,10 @@ public:
|
||||
mDummyTargetInfo.imageView = imageView;
|
||||
}
|
||||
|
||||
static VkPrimitiveTopology getPrimitiveTopology(PrimitiveType pt) noexcept {
|
||||
switch (pt) {
|
||||
case PrimitiveType::POINTS:
|
||||
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
|
||||
case PrimitiveType::LINES:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
|
||||
case PrimitiveType::LINE_STRIP:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
|
||||
case PrimitiveType::TRIANGLES:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
case PrimitiveType::TRIANGLE_STRIP:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
|
||||
}
|
||||
// Acquires a resource to be bound to the current pipeline. The ownership of the resource
|
||||
// will be transferred to the corresponding pipeline when pipeline is bound.
|
||||
void acquireResource(VulkanResource* resource) {
|
||||
mPipelineBoundResources.acquire(resource);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANRESOURCEALLOCATOR_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANRESOURCEALLOCATOR_H
|
||||
|
||||
#include "VulkanConstants.h"
|
||||
#include "VulkanHandles.h"
|
||||
|
||||
#include <private/backend/HandleAllocator.h>
|
||||
@@ -30,17 +29,18 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
#define RESOURCE_TYPE_COUNT (static_cast<int>(VulkanResourceType::END_TYPE))
|
||||
#define DEBUG_RESOURCE_LEAKS FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
|
||||
// RESOURCE_TYPE_COUNT matches the count of enum VulkanResourceType.
|
||||
#define RESOURCE_TYPE_COUNT 12
|
||||
#define DEBUG_RESOURCE_LEAKS 0
|
||||
|
||||
#if DEBUG_RESOURCE_LEAKS
|
||||
#define TRACK_INCREMENT() \
|
||||
if (!IS_HEAP_ALLOC_TYPE(obj->getType())) { \
|
||||
mDebugOnlyResourceCount[static_cast<size_t>(obj->getType())]++; \
|
||||
#define TRACK_INCREMENT() \
|
||||
if (!IS_MANAGED_TYPE(obj->mType)) { \
|
||||
mDebugOnlyResourceCount[static_cast<size_t>(obj->mType)]++; \
|
||||
}
|
||||
#define TRACK_DECREMENT() \
|
||||
if (!IS_HEAP_ALLOC_TYPE(obj->getType())) { \
|
||||
mDebugOnlyResourceCount[static_cast<size_t>(obj->getType())]--; \
|
||||
#define TRACK_DECREMENT() \
|
||||
if (!IS_MANAGED_TYPE(obj->mType)) { \
|
||||
mDebugOnlyResourceCount[static_cast<size_t>(obj->mType)]--; \
|
||||
}
|
||||
#else
|
||||
// No-op
|
||||
@@ -51,8 +51,8 @@ namespace filament::backend {
|
||||
class VulkanResourceAllocator {
|
||||
|
||||
public:
|
||||
VulkanResourceAllocator(size_t arenaSize, bool disableUseAfterFreeCheck)
|
||||
: mHandleAllocatorImpl("Handles", arenaSize, disableUseAfterFreeCheck)
|
||||
VulkanResourceAllocator(size_t arenaSize)
|
||||
: mHandleAllocatorImpl("Handles", arenaSize)
|
||||
#if DEBUG_RESOURCE_LEAKS
|
||||
, mDebugOnlyResourceCount(RESOURCE_TYPE_COUNT) {
|
||||
std::memset(mDebugOnlyResourceCount.data(), 0, sizeof(size_t) * RESOURCE_TYPE_COUNT);
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
#include "VulkanResources.h"
|
||||
#include "VulkanHandles.h"
|
||||
#include "VulkanResourceAllocator.h"
|
||||
#include "caching/VulkanDescriptorSet.h"
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -53,24 +53,16 @@ void deallocateResource(VulkanResourceAllocator* allocator, VulkanResourceType t
|
||||
case VulkanResourceType::TIMER_QUERY:
|
||||
allocator->destruct<VulkanTimerQuery>(Handle<HwTimerQuery>(id));
|
||||
break;
|
||||
case VulkanResourceType::VERTEX_BUFFER_INFO:
|
||||
allocator->destruct<VulkanVertexBufferInfo>(Handle<HwVertexBufferInfo>(id));
|
||||
break;
|
||||
case VulkanResourceType::VERTEX_BUFFER:
|
||||
allocator->destruct<VulkanVertexBuffer>(Handle<HwVertexBuffer>(id));
|
||||
break;
|
||||
case VulkanResourceType::RENDER_PRIMITIVE:
|
||||
allocator->destruct<VulkanRenderPrimitive>(Handle<VulkanRenderPrimitive>(id));
|
||||
break;
|
||||
case VulkanResourceType::DESCRIPTOR_SET:
|
||||
allocator->destruct<VulkanDescriptorSet>(Handle<VulkanDescriptorSet>(id));
|
||||
break;
|
||||
|
||||
// If the resource is heap allocated, then the resource manager just skip refcounted
|
||||
// destruction.
|
||||
case VulkanResourceType::FENCE:
|
||||
case VulkanResourceType::HEAP_ALLOCATED:
|
||||
case VulkanResourceType::END_TYPE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANRESOURCES_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANRESOURCES_H
|
||||
|
||||
#include "VulkanUtility.h"
|
||||
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include <tsl/robin_set.h>
|
||||
@@ -45,14 +43,10 @@ enum class VulkanResourceType : uint8_t {
|
||||
TEXTURE,
|
||||
TIMER_QUERY,
|
||||
VERTEX_BUFFER,
|
||||
VERTEX_BUFFER_INFO,
|
||||
DESCRIPTOR_SET,
|
||||
|
||||
// Below are resources that are managed manually (i.e. not ref counted).
|
||||
FENCE,
|
||||
HEAP_ALLOCATED,
|
||||
|
||||
END_TYPE, // A placeholder
|
||||
};
|
||||
|
||||
#define IS_HEAP_ALLOC_TYPE(f) \
|
||||
@@ -68,16 +62,16 @@ struct VulkanResourceBase {
|
||||
protected:
|
||||
explicit VulkanResourceBase(VulkanResourceType type)
|
||||
: mRefCount(IS_HEAP_ALLOC_TYPE(type) ? 1 : 0),
|
||||
mType(uint32_t(type)),
|
||||
mType(type),
|
||||
mHandleId(0) {
|
||||
}
|
||||
|
||||
private:
|
||||
inline VulkanResourceType getType() const noexcept {
|
||||
return VulkanResourceType(mType);
|
||||
inline VulkanResourceType getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
inline HandleBase::HandleId getId() const noexcept {
|
||||
inline HandleBase::HandleId getId() {
|
||||
return mHandleId;
|
||||
}
|
||||
|
||||
@@ -86,7 +80,7 @@ private:
|
||||
}
|
||||
|
||||
inline void ref() noexcept {
|
||||
if (IS_HEAP_ALLOC_TYPE(getType())) {
|
||||
if (IS_HEAP_ALLOC_TYPE(mType)) {
|
||||
return;
|
||||
}
|
||||
assert_invariant(mRefCount < ((1<<24) - 1));
|
||||
@@ -94,19 +88,19 @@ private:
|
||||
}
|
||||
|
||||
inline void deref() noexcept {
|
||||
if (IS_HEAP_ALLOC_TYPE(getType())) {
|
||||
if (IS_HEAP_ALLOC_TYPE(mType)) {
|
||||
return;
|
||||
}
|
||||
assert_invariant(mRefCount > 0);
|
||||
--mRefCount;
|
||||
}
|
||||
|
||||
inline size_t refcount() const noexcept {
|
||||
inline size_t refcount() noexcept {
|
||||
return mRefCount;
|
||||
}
|
||||
|
||||
uint32_t mRefCount : 24; // 16M is enough for the refcount
|
||||
uint32_t mType : 8; // must be uint32_t or MSVC doesn't pack it. no codegen impact w/ clang.
|
||||
VulkanResourceType mType : 8;
|
||||
HandleBase::HandleId mHandleId;
|
||||
|
||||
friend struct VulkanThreadSafeResource;
|
||||
@@ -116,8 +110,6 @@ private:
|
||||
friend class VulkanResourceManagerImpl;
|
||||
};
|
||||
|
||||
static_assert(sizeof(VulkanResourceBase) == 8, "VulkanResourceBase should be 8 bytes");
|
||||
|
||||
struct VulkanThreadSafeResource {
|
||||
protected:
|
||||
explicit VulkanThreadSafeResource(VulkanResourceType type)
|
||||
@@ -167,7 +159,62 @@ namespace {
|
||||
// When the size of the resource set is known to be small, (for example for VulkanRenderPrimitive),
|
||||
// we just use a std::array to back the set.
|
||||
template<std::size_t SIZE>
|
||||
using FixedCapacityResourceSet = CappedArray<VulkanResource*, SIZE>;
|
||||
class FixedCapacityResourceSet {
|
||||
private:
|
||||
using FixedSizeArray = std::array<VulkanResource*, SIZE>;
|
||||
|
||||
public:
|
||||
using const_iterator = typename FixedSizeArray::const_iterator;
|
||||
|
||||
inline ~FixedCapacityResourceSet() {
|
||||
clear();
|
||||
}
|
||||
|
||||
inline const_iterator begin() {
|
||||
if (mInd == 0) {
|
||||
return mArray.cend();
|
||||
}
|
||||
return mArray.cbegin();
|
||||
}
|
||||
|
||||
inline const_iterator end() {
|
||||
if (mInd == 0) {
|
||||
return mArray.cend();
|
||||
}
|
||||
if (mInd < SIZE) {
|
||||
return mArray.begin() + mInd;
|
||||
}
|
||||
return mArray.cend();
|
||||
}
|
||||
|
||||
inline const_iterator find(VulkanResource* resource) {
|
||||
return std::find(begin(), end(), resource);
|
||||
}
|
||||
|
||||
inline void insert(VulkanResource* resource) {
|
||||
assert_invariant(mInd < SIZE);
|
||||
mArray[mInd++] = resource;
|
||||
}
|
||||
|
||||
inline void erase(VulkanResource* resource) {
|
||||
assert_invariant(false && "FixedCapacityResourceSet::erase should not be called");
|
||||
}
|
||||
|
||||
inline void clear() {
|
||||
if (mInd == 0) {
|
||||
return;
|
||||
}
|
||||
mInd = 0;
|
||||
}
|
||||
|
||||
inline size_t size() {
|
||||
return mInd;
|
||||
}
|
||||
|
||||
private:
|
||||
FixedSizeArray mArray{nullptr};
|
||||
size_t mInd = 0;
|
||||
};
|
||||
|
||||
// robin_set/map are useful for sets that are acquire only and the set will be iterated when the set
|
||||
// is cleared.
|
||||
|
||||
@@ -95,8 +95,7 @@ constexpr inline VkBool32 getCompareEnable(SamplerCompareMode mode) noexcept {
|
||||
return mode == SamplerCompareMode::NONE ? VK_FALSE : VK_TRUE;
|
||||
}
|
||||
|
||||
VulkanSamplerCache::VulkanSamplerCache(VkDevice device)
|
||||
: mDevice(device) {}
|
||||
void VulkanSamplerCache::initialize(VkDevice device) { mDevice = device; }
|
||||
|
||||
VkSampler VulkanSamplerCache::getSampler(SamplerParams params) noexcept {
|
||||
auto iter = mCache.find(params);
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace filament::backend {
|
||||
// Simple manager for VkSampler objects.
|
||||
class VulkanSamplerCache {
|
||||
public:
|
||||
explicit VulkanSamplerCache(VkDevice device);
|
||||
void initialize(VkDevice device);
|
||||
VkSampler getSampler(SamplerParams params) noexcept;
|
||||
void terminate() noexcept;
|
||||
private:
|
||||
|
||||
@@ -26,9 +26,10 @@ static constexpr uint32_t TIME_BEFORE_EVICTION = FVK_MAX_COMMAND_BUFFERS;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
VulkanStagePool::VulkanStagePool(VmaAllocator allocator, VulkanCommands* commands)
|
||||
: mAllocator(allocator),
|
||||
mCommands(commands) {}
|
||||
void VulkanStagePool::initialize(VmaAllocator allocator, VulkanCommands* commands) noexcept {
|
||||
mAllocator = allocator;
|
||||
mCommands = commands;
|
||||
}
|
||||
|
||||
VulkanStage const* VulkanStagePool::acquireStage(uint32_t numBytes) {
|
||||
// First check if a stage exists whose capacity is greater than or equal to the requested size.
|
||||
|
||||
@@ -45,7 +45,7 @@ struct VulkanStageImage {
|
||||
// This class manages two types of host-mappable staging areas: buffer stages and image stages.
|
||||
class VulkanStagePool {
|
||||
public:
|
||||
VulkanStagePool(VmaAllocator allocator, VulkanCommands* commands);
|
||||
void initialize(VmaAllocator allocator, VulkanCommands* commands) noexcept;
|
||||
|
||||
// Finds or creates a stage whose capacity is at least the given number of bytes.
|
||||
// The stage is automatically released back to the pool after TIME_BEFORE_EVICTION frames.
|
||||
|
||||
@@ -35,7 +35,7 @@ VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const&
|
||||
mStagePool(stagePool),
|
||||
mHeadless(extent.width != 0 && extent.height != 0 && !nativeWindow),
|
||||
mFlushAndWaitOnResize(platform->getCustomization().flushAndWaitOnWindowResize),
|
||||
mCurrentImageReadyIndex(0),
|
||||
mImageReady(VK_NULL_HANDLE),
|
||||
mAcquired(false),
|
||||
mIsFirstRenderPass(true) {
|
||||
swapChain = mPlatform->createSwapChain(nativeWindow, flags, extent);
|
||||
@@ -46,15 +46,10 @@ VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const&
|
||||
};
|
||||
|
||||
// No need to wait on this semaphore before drawing when in Headless mode.
|
||||
if (mHeadless) {
|
||||
// Set all sempahores to VK_NULL_HANDLE
|
||||
memset(mImageReady, 0, sizeof(mImageReady[0]) * IMAGE_READY_SEMAPHORE_COUNT);
|
||||
} else {
|
||||
for (uint32_t i = 0; i < IMAGE_READY_SEMAPHORE_COUNT; ++i) {
|
||||
VkResult result =
|
||||
vkCreateSemaphore(mPlatform->getDevice(), &createInfo, nullptr, mImageReady + i);
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Failed to create semaphore");
|
||||
}
|
||||
if (!mHeadless) {
|
||||
VkResult result =
|
||||
vkCreateSemaphore(mPlatform->getDevice(), &createInfo, nullptr, &mImageReady);
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Failed to create semaphore");
|
||||
}
|
||||
|
||||
update();
|
||||
@@ -67,11 +62,9 @@ VulkanSwapChain::~VulkanSwapChain() {
|
||||
mCommands->wait();
|
||||
|
||||
mPlatform->destroy(swapChain);
|
||||
for (uint32_t i = 0; i < IMAGE_READY_SEMAPHORE_COUNT; ++i) {
|
||||
if (mImageReady[i] != VK_NULL_HANDLE) {
|
||||
vkDestroySemaphore(mPlatform->getDevice(), mImageReady[i], VKALLOC);
|
||||
}
|
||||
}
|
||||
if (mImageReady != VK_NULL_HANDLE) {
|
||||
vkDestroySemaphore(mPlatform->getDevice(), mImageReady, VKALLOC);
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanSwapChain::update() {
|
||||
@@ -138,13 +131,11 @@ void VulkanSwapChain::acquire(bool& resized) {
|
||||
update();
|
||||
}
|
||||
|
||||
mCurrentImageReadyIndex = (mCurrentImageReadyIndex + 1) % IMAGE_READY_SEMAPHORE_COUNT;
|
||||
const VkSemaphore imageReady = mImageReady[mCurrentImageReadyIndex];
|
||||
VkResult const result = mPlatform->acquire(swapChain, imageReady, &mCurrentSwapIndex);
|
||||
VkResult const result = mPlatform->acquire(swapChain, mImageReady, &mCurrentSwapIndex);
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR,
|
||||
"Cannot acquire in swapchain.");
|
||||
if (imageReady != VK_NULL_HANDLE) {
|
||||
mCommands->injectDependency(imageReady);
|
||||
if (mImageReady != VK_NULL_HANDLE) {
|
||||
mCommands->injectDependency(mImageReady);
|
||||
}
|
||||
mAcquired = true;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user