Compare commits
564 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4cfc10609 | ||
|
|
0bdf54e6d9 | ||
|
|
10af9d64f2 | ||
|
|
a76b0cb9da | ||
|
|
0450358df3 | ||
|
|
a632010b95 | ||
|
|
a1038f3972 | ||
|
|
93f6ea1a07 | ||
|
|
d721fd65b3 | ||
|
|
e207f2e025 | ||
|
|
be37d9b5a9 | ||
|
|
e39327c017 | ||
|
|
edbc22961e | ||
|
|
3250725aae | ||
|
|
803011582c | ||
|
|
2206f68129 | ||
|
|
6388324552 | ||
|
|
89f35779b4 | ||
|
|
ad1e3f1d2d | ||
|
|
9477a5f32f | ||
|
|
8e71cf8e6e | ||
|
|
ea7396d2cf | ||
|
|
5c7debecc8 | ||
|
|
c6d6eed7d3 | ||
|
|
39f8deb498 | ||
|
|
665569dfc7 | ||
|
|
19f72cf967 | ||
|
|
a4363de59e | ||
|
|
c3aa321d51 | ||
|
|
9b268c3654 | ||
|
|
925c42294f | ||
|
|
23811df3cf | ||
|
|
f7cc9c4d33 | ||
|
|
66ccff964b | ||
|
|
0c82b8bd3a | ||
|
|
4e8b69206c | ||
|
|
0a7fc0e65e | ||
|
|
787e03e821 | ||
|
|
f381b0c2dd | ||
|
|
b8744b9e6a | ||
|
|
59ee1a1041 | ||
|
|
88d0983e37 | ||
|
|
3680e022fc | ||
|
|
a642b725e0 | ||
|
|
dbc3a8c65f | ||
|
|
86cafba0df | ||
|
|
60ded2ad14 | ||
|
|
fa560a7081 | ||
|
|
5023f23cc4 | ||
|
|
0fef1407e3 | ||
|
|
a09c505fa0 | ||
|
|
023909b8e0 | ||
|
|
60081d7ed5 | ||
|
|
0b5235d054 | ||
|
|
071554acae | ||
|
|
f05343dec1 | ||
|
|
a403cef605 | ||
|
|
9b4b234c12 | ||
|
|
42518dbfaa | ||
|
|
4e700a5808 | ||
|
|
647af6c15a | ||
|
|
b91ba258e1 | ||
|
|
fe579d6e4b | ||
|
|
6d3044b448 | ||
|
|
db8564bb3c | ||
|
|
28f73d1037 | ||
|
|
dd9196efac | ||
|
|
c542c7bdbb | ||
|
|
f8aad35775 | ||
|
|
657c8e8729 | ||
|
|
a451a8f566 | ||
|
|
0e5b1a8fa7 | ||
|
|
e922207797 | ||
|
|
e447bd98a5 | ||
|
|
a0e9e8d4b2 | ||
|
|
09817403ff | ||
|
|
8f6ef05429 | ||
|
|
4cbf259ab3 | ||
|
|
042622b5db | ||
|
|
eff72e15cd | ||
|
|
aacd33ada7 | ||
|
|
fe00eed401 | ||
|
|
000d00579b | ||
|
|
63dd39ea51 | ||
|
|
87cb88f2f7 | ||
|
|
cb3e8215d6 | ||
|
|
a3900a4830 | ||
|
|
0f9139305a | ||
|
|
0a8504fb3c | ||
|
|
d709200fc0 | ||
|
|
77df33ebf6 | ||
|
|
8064d561f4 | ||
|
|
aaf1e4d21b | ||
|
|
9f9a4cbaaf | ||
|
|
e7806f9dac | ||
|
|
d7eb4fc656 | ||
|
|
d10724ae6f | ||
|
|
0a3116422a | ||
|
|
0a59b3bb54 | ||
|
|
e0b21b08fa | ||
|
|
d4591828c8 | ||
|
|
cad0597524 | ||
|
|
e10581e319 | ||
|
|
f7b2139e81 | ||
|
|
f55452d6dd | ||
|
|
b0c675498c | ||
|
|
f16da13198 | ||
|
|
0827b3fce9 | ||
|
|
a3a7f72b7c | ||
|
|
acf378e9f2 | ||
|
|
a39209cd37 | ||
|
|
2ef5673c18 | ||
|
|
4c10ae1a4c | ||
|
|
66c3f282de | ||
|
|
fec75d9436 | ||
|
|
ef68f43687 | ||
|
|
13780fae2a | ||
|
|
c294456e2a | ||
|
|
6410fb3209 | ||
|
|
af22591cc0 | ||
|
|
b0267e4631 | ||
|
|
69a2d0bc5e | ||
|
|
108b9211ff | ||
|
|
97f2474956 | ||
|
|
b4c68d56b6 | ||
|
|
03926c2374 | ||
|
|
787f91085d | ||
|
|
1b37bd5df6 | ||
|
|
ea0a5c521f | ||
|
|
6ba69adccc | ||
|
|
481361f714 | ||
|
|
e1e34623cb | ||
|
|
78409deaa7 | ||
|
|
787a18d3a8 | ||
|
|
548f9d0a88 | ||
|
|
8450386330 | ||
|
|
8adf2965fd | ||
|
|
e0bf075f7f | ||
|
|
8a49d4ef94 | ||
|
|
d85e1afd7e | ||
|
|
c96d1c3aa5 | ||
|
|
1cbc825461 | ||
|
|
f0d6200e8e | ||
|
|
ad24fcca39 | ||
|
|
06837a2a77 | ||
|
|
47537ba764 | ||
|
|
cd51a6f795 | ||
|
|
3d40cd86ac | ||
|
|
b03b4db7c7 | ||
|
|
859e2701f2 | ||
|
|
2617df44db | ||
|
|
01a8e11ef3 | ||
|
|
5a455eac91 | ||
|
|
e18e051bfc | ||
|
|
bf44d3f98e | ||
|
|
b6fa215c4e | ||
|
|
726dc02aaa | ||
|
|
9ee0c5acf3 | ||
|
|
5e0140ba81 | ||
|
|
a690c970e6 | ||
|
|
db28c382e0 | ||
|
|
1747d529d8 | ||
|
|
27763d8390 | ||
|
|
ef79781c9a | ||
|
|
7b6ff27c4b | ||
|
|
2526d04397 | ||
|
|
4ffa06ae07 | ||
|
|
03c0509a6c | ||
|
|
f68211d83e | ||
|
|
7694f915f5 | ||
|
|
0340778775 | ||
|
|
6fecfad2a2 | ||
|
|
e9fa5e6f3d | ||
|
|
b5ec7fe015 | ||
|
|
b0ccec2b62 | ||
|
|
55cce46454 | ||
|
|
071745007a | ||
|
|
b755bcd495 | ||
|
|
315552d1e0 | ||
|
|
e7b2972fc8 | ||
|
|
e1675f6bd1 | ||
|
|
2044b88015 | ||
|
|
77a21f95e5 | ||
|
|
a4e05eea9d | ||
|
|
373cb7fb39 | ||
|
|
2e240e70d6 | ||
|
|
bdcb5dc877 | ||
|
|
e7a59c1031 | ||
|
|
8ae25e4812 | ||
|
|
3548521bef | ||
|
|
d15ed9ed34 | ||
|
|
655365247c | ||
|
|
eaecc9a4d0 | ||
|
|
dfe33963bb | ||
|
|
661f5ede04 | ||
|
|
5858f2822c | ||
|
|
379ecccec2 | ||
|
|
e69c605c4f | ||
|
|
f6711e6b4f | ||
|
|
e327e7a15e | ||
|
|
9973dfd1c7 | ||
|
|
b045d9bd96 | ||
|
|
64181e19b8 | ||
|
|
ef927134f0 | ||
|
|
f9398a3ab1 | ||
|
|
450402d83d | ||
|
|
9f7e9299bd | ||
|
|
681901cb7c | ||
|
|
a0c549534f | ||
|
|
9453c2c2e9 | ||
|
|
32c6365bf9 | ||
|
|
6c921ba340 | ||
|
|
f5818b7621 | ||
|
|
ac1a6101e1 | ||
|
|
7ff58abc17 | ||
|
|
c548aa3497 | ||
|
|
d0c782c7ae | ||
|
|
d9640ecc65 | ||
|
|
b177324058 | ||
|
|
e7a20b04de | ||
|
|
5245547f28 | ||
|
|
ef696d895d | ||
|
|
a15e72dc03 | ||
|
|
b7997a2677 | ||
|
|
635c8d0f72 | ||
|
|
c9269c04be | ||
|
|
0c3bff3763 | ||
|
|
f06d9294a2 | ||
|
|
c39ddb5991 | ||
|
|
690338a122 | ||
|
|
9dd8f1b8ab | ||
|
|
744b5b6404 | ||
|
|
381d86adb7 | ||
|
|
26858c026a | ||
|
|
c454044913 | ||
|
|
6e57791397 | ||
|
|
1c5fc42772 | ||
|
|
9bab3a362f | ||
|
|
fc65380f2a | ||
|
|
c1b749e12c | ||
|
|
d310ae3bc6 | ||
|
|
9ab0cd11e7 | ||
|
|
51c3f3253b | ||
|
|
00c00b617c | ||
|
|
c0dd8cac46 | ||
|
|
8c35361878 | ||
|
|
2e59ae1340 | ||
|
|
856507a580 | ||
|
|
8899dbef3c | ||
|
|
8e4ea7e3b4 | ||
|
|
5524959f33 | ||
|
|
39959712c1 | ||
|
|
c652df10c6 | ||
|
|
131dc84256 | ||
|
|
afed6dad84 | ||
|
|
b18033d023 | ||
|
|
26bb68fcb7 | ||
|
|
2c4f850e5f | ||
|
|
502fd90a8d | ||
|
|
f2c6ab27e3 | ||
|
|
40bbeebd27 | ||
|
|
f9d802f832 | ||
|
|
a6f95d21af | ||
|
|
87f0edc652 | ||
|
|
97c0800701 | ||
|
|
da96043a24 | ||
|
|
f6fcf8ca38 | ||
|
|
43b826bd52 | ||
|
|
cef03d52cb | ||
|
|
29cdfdc13e | ||
|
|
2b8a5b4bd1 | ||
|
|
910cb29a7f | ||
|
|
3261332e0b | ||
|
|
6939d2d70b | ||
|
|
df65a3b7fc | ||
|
|
7d9299d870 | ||
|
|
00ae7c3cd3 | ||
|
|
769c4f43bd | ||
|
|
7fec659979 | ||
|
|
d104d15c53 | ||
|
|
22137671da | ||
|
|
9577607c22 | ||
|
|
4340cee991 | ||
|
|
d385de9139 | ||
|
|
c4e0122846 | ||
|
|
6374f012de | ||
|
|
7d4c2aa54f | ||
|
|
c5024ff04d | ||
|
|
63117686b1 | ||
|
|
f6bd78083d | ||
|
|
f176aeed8d | ||
|
|
51746dc751 | ||
|
|
94d6ef126f | ||
|
|
14f2df2a72 | ||
|
|
82693aae35 | ||
|
|
c4e55464c9 | ||
|
|
4a4b786a60 | ||
|
|
d7d65df9e0 | ||
|
|
40400d696b | ||
|
|
4c201a9b83 | ||
|
|
b338506229 | ||
|
|
7805508e8d | ||
|
|
9837b0ea2e | ||
|
|
60d5bb81f2 | ||
|
|
76cb68600c | ||
|
|
a6b270b02d | ||
|
|
f68a899185 | ||
|
|
3299d27197 | ||
|
|
a39bf61977 | ||
|
|
6a290864af | ||
|
|
20ab55752d | ||
|
|
4190816023 | ||
|
|
e33a15d410 | ||
|
|
419f7b41af | ||
|
|
670b85a86f | ||
|
|
e835d51bdf | ||
|
|
242134d2af | ||
|
|
f0f2834cfe | ||
|
|
860bddb1f5 | ||
|
|
14f9cd57e1 | ||
|
|
063112bf59 | ||
|
|
e764329701 | ||
|
|
27a17b332f | ||
|
|
373688cfb0 | ||
|
|
09d37d7dfd | ||
|
|
b2dd4f652e | ||
|
|
6bbff07e00 | ||
|
|
09c7e66844 | ||
|
|
c371b82900 | ||
|
|
5da990e8fb | ||
|
|
b96beb478c | ||
|
|
ad3a16fd3b | ||
|
|
21b6b89577 | ||
|
|
55a66737c0 | ||
|
|
198e499798 | ||
|
|
9d3b276f61 | ||
|
|
b0967463ca | ||
|
|
1cc33caaa3 | ||
|
|
0777bedde8 | ||
|
|
be978ab76b | ||
|
|
21a64b306d | ||
|
|
5a66c2bfba | ||
|
|
e5a27e6558 | ||
|
|
6f3a92d772 | ||
|
|
4feb69e342 | ||
|
|
d2b2c501bc | ||
|
|
465660e235 | ||
|
|
4eea8624b7 | ||
|
|
09213950c3 | ||
|
|
d93f0bb56a | ||
|
|
4de23007a5 | ||
|
|
8543106f5e | ||
|
|
ced937cc46 | ||
|
|
9476aedaf6 | ||
|
|
68bc6749e5 | ||
|
|
c5d4ccc78d | ||
|
|
e847b105f2 | ||
|
|
d6e9b50b99 | ||
|
|
61beedd3d9 | ||
|
|
35333f0105 | ||
|
|
be700d0719 | ||
|
|
5dd2f5e2cf | ||
|
|
e9bd6a3b0d | ||
|
|
d505e515e4 | ||
|
|
9fd813267e | ||
|
|
f14e6cdb60 | ||
|
|
fe87ac0f3d | ||
|
|
a6f36abab0 | ||
|
|
621e677898 | ||
|
|
626a94cf8f | ||
|
|
d66074d2ea | ||
|
|
bf28f6514f | ||
|
|
cf62e22409 | ||
|
|
900d063d4f | ||
|
|
b911e86b02 | ||
|
|
54bfdb7704 | ||
|
|
566152c230 | ||
|
|
dcbf39bcfa | ||
|
|
c73913a864 | ||
|
|
d3ba4a2b80 | ||
|
|
af7affa9dd | ||
|
|
89e738003c | ||
|
|
a3f7399d7d | ||
|
|
dff195d120 | ||
|
|
48552e3027 | ||
|
|
c6ce417fac | ||
|
|
a7510d3b3a | ||
|
|
aad10c9b8d | ||
|
|
ca96e00135 | ||
|
|
536189340d | ||
|
|
23b97efb69 | ||
|
|
2c8e3bb0a3 | ||
|
|
7a48cb9979 | ||
|
|
2f5caa27fd | ||
|
|
d69f00741f | ||
|
|
8c4a52e58d | ||
|
|
fd22d80dfd | ||
|
|
e071dc52fd | ||
|
|
e1cbeb7e02 | ||
|
|
9d367cf9f2 | ||
|
|
3890e61fbc | ||
|
|
3df853b74b | ||
|
|
10bc483305 | ||
|
|
f55fa45b73 | ||
|
|
acb24dd3e1 | ||
|
|
4b87c45298 | ||
|
|
a2fb56bf48 | ||
|
|
37095dc2cd | ||
|
|
a57ac5a954 | ||
|
|
29045609a3 | ||
|
|
b4dad425aa | ||
|
|
c73414f1a1 | ||
|
|
6920bee5e2 | ||
|
|
6a5e203884 | ||
|
|
6b1f44ed6d | ||
|
|
ab8cd3e5d5 | ||
|
|
78f7cc0d64 | ||
|
|
b607650d5c | ||
|
|
1bfa72fa7c | ||
|
|
0030ca3af7 | ||
|
|
a53e08032f | ||
|
|
62febda165 | ||
|
|
a726de0e99 | ||
|
|
918d527b14 | ||
|
|
5919c9a788 | ||
|
|
14f2248ea1 | ||
|
|
81499fc6ea | ||
|
|
ffb285c6ff | ||
|
|
e08e8cf131 | ||
|
|
40b4b47023 | ||
|
|
2565caf62a | ||
|
|
d63df79b85 | ||
|
|
697dd7e929 | ||
|
|
f613be9276 | ||
|
|
f5a08b1c12 | ||
|
|
f40bbb1602 | ||
|
|
aeb766ee37 | ||
|
|
cd67046b33 | ||
|
|
66f4c170cb | ||
|
|
a485760028 | ||
|
|
0b5a2cd6a3 | ||
|
|
8e7733b2ce | ||
|
|
08d2c17c65 | ||
|
|
09f7136cf4 | ||
|
|
c4b77e247e | ||
|
|
9f9daba33d | ||
|
|
dc3831c86b | ||
|
|
ab14f1908c | ||
|
|
bf6e1d6c75 | ||
|
|
5d8d402403 | ||
|
|
b8eb3076ce | ||
|
|
d16e48e128 | ||
|
|
e46c906a8b | ||
|
|
83e8a9076b | ||
|
|
56b28756ad | ||
|
|
13fad75811 | ||
|
|
1cf367aae5 | ||
|
|
70909f86c8 | ||
|
|
1fd908b2c4 | ||
|
|
f0108c17b9 | ||
|
|
0566bde61b | ||
|
|
a31fc8a612 | ||
|
|
2ba9e16a6c | ||
|
|
e6dce6ad7b | ||
|
|
c87caebcf7 | ||
|
|
121a557fe5 | ||
|
|
6aa7f219cd | ||
|
|
e9e05bec90 | ||
|
|
8421661a89 | ||
|
|
7fea7c589d | ||
|
|
0e3b735a60 | ||
|
|
236f5757a2 | ||
|
|
0a44e3ee09 | ||
|
|
ee5a9c97be | ||
|
|
2126654979 | ||
|
|
df40a8c5cf | ||
|
|
34fb647903 | ||
|
|
5de4f1e2da | ||
|
|
18dd31f51a | ||
|
|
40689f7d1e | ||
|
|
4ce0345842 | ||
|
|
844e38702a | ||
|
|
5f074c8c82 | ||
|
|
5c2b1d396b | ||
|
|
b0b7837c4d | ||
|
|
7789c5a703 | ||
|
|
b64cb8d339 | ||
|
|
3891928092 | ||
|
|
f5983208eb | ||
|
|
0ef5931212 | ||
|
|
6964cec6f1 | ||
|
|
a35ef93d19 | ||
|
|
e5f75d1e48 | ||
|
|
231af66b90 | ||
|
|
f386fc75df | ||
|
|
cc45a24aff | ||
|
|
e06d0b2e70 | ||
|
|
0281ac4afe | ||
|
|
7227c01d79 | ||
|
|
c5abde1616 | ||
|
|
c2a4951f20 | ||
|
|
0fde15246b | ||
|
|
b3423de80a | ||
|
|
743449e635 | ||
|
|
405182025d | ||
|
|
bd6d14d741 | ||
|
|
0e190cadcd | ||
|
|
408f5a4a51 | ||
|
|
53f5957b92 | ||
|
|
fb32a0be5a | ||
|
|
474e1ec7cc | ||
|
|
1e9d09b82e | ||
|
|
c3b1bf4b76 | ||
|
|
e2d9c31f5e | ||
|
|
7b81e9d248 | ||
|
|
6e6c438e2a | ||
|
|
4606c7512e | ||
|
|
88e373c960 | ||
|
|
f2deb39dac | ||
|
|
8a5793ce23 | ||
|
|
a36c5f2ee6 | ||
|
|
c63bf31328 | ||
|
|
5d811163a6 | ||
|
|
78a98cbf08 | ||
|
|
91348909b5 | ||
|
|
73f2543a6f | ||
|
|
d80458b726 | ||
|
|
22385c44c8 | ||
|
|
40261d3472 | ||
|
|
06ff562cd2 | ||
|
|
e7b023778a | ||
|
|
3e12fe15bc | ||
|
|
95634e8276 | ||
|
|
6c19346855 | ||
|
|
2e4419a295 | ||
|
|
bdc95a1544 | ||
|
|
68e143de1b | ||
|
|
5d6aadb268 | ||
|
|
4368c57243 | ||
|
|
ef54491760 | ||
|
|
d88f696604 | ||
|
|
82b0171a86 | ||
|
|
142427b3e1 | ||
|
|
9c4d50eda3 | ||
|
|
f3c8419de8 | ||
|
|
d6524fbd26 | ||
|
|
4446b0fbad | ||
|
|
58ea5f88dc | ||
|
|
8ce30e9ff7 | ||
|
|
67230a3290 | ||
|
|
c413d62536 | ||
|
|
d1b19aa389 | ||
|
|
06048ae818 | ||
|
|
18e5b94615 | ||
|
|
ff786ad2c7 | ||
|
|
c95696e8a0 | ||
|
|
4dbed63f0b | ||
|
|
3de99315e2 | ||
|
|
5945793031 | ||
|
|
449e7b2a84 | ||
|
|
cdebc1c5cc | ||
|
|
e95ee85804 | ||
|
|
5e69c9fa05 | ||
|
|
370890b704 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,4 +1,5 @@
|
|||||||
/installed.xml
|
/installed.xml
|
||||||
|
/indra/llcommon/llversionviewer.h
|
||||||
/indra/build-*
|
/indra/build-*
|
||||||
/indra/tools/vstool/obj/
|
/indra/tools/vstool/obj/
|
||||||
*.aps
|
*.aps
|
||||||
@@ -11,11 +12,13 @@
|
|||||||
/indra/viewer-*
|
/indra/viewer-*
|
||||||
/indra/newview/vivox-runtime/
|
/indra/newview/vivox-runtime/
|
||||||
/libraries/
|
/libraries/
|
||||||
|
/lib/
|
||||||
*.pyc
|
*.pyc
|
||||||
*.orig
|
*.orig
|
||||||
*.rej
|
*.rej
|
||||||
*.bak
|
*.bak
|
||||||
*~
|
*~
|
||||||
|
*.DS_Store
|
||||||
/LICENSES/
|
/LICENSES/
|
||||||
/edited-files.txt
|
/edited-files.txt
|
||||||
qtcreator-build/
|
qtcreator-build/
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
http://www.jclark.com/xml/copying.txt
|
|
||||||
|
|
||||||
Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included
|
|
||||||
in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
||||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
||||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
||||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
This is a modified version of openal-soft from GIT (56cc03860378e2758370b773b2a6f1b4e086b49a).
|
|
||||||
The modified source which this was built from is available here:
|
|
||||||
http://viewer-source-downloads.s3.amazonaws.com/install_pkgs/openal-soft-linden-56cc03860378e2758370b773b2a6f1b4e086b49a.tar.bz2
|
|
||||||
|
|
||||||
@@ -670,19 +670,19 @@
|
|||||||
<key>EstateChangeInfo</key>
|
<key>EstateChangeInfo</key>
|
||||||
<boolean>true</boolean>
|
<boolean>true</boolean>
|
||||||
|
|
||||||
<key>FetchInventoryDescendents</key>
|
<key>FetchInventoryDescendents2</key>
|
||||||
<boolean>false</boolean>
|
<boolean>false</boolean>
|
||||||
|
|
||||||
<key>WebFetchInventoryDescendents</key>
|
<key>WebFetchInventoryDescendents</key>
|
||||||
<boolean>false</boolean>
|
<boolean>false</boolean>
|
||||||
|
|
||||||
<key>FetchInventory</key>
|
<key>FetchInventory2</key>
|
||||||
<boolean>true</boolean>
|
<boolean>true</boolean>
|
||||||
|
|
||||||
<key>FetchLibDescendents</key>
|
<key>FetchLibDescendents2</key>
|
||||||
<boolean>true</boolean>
|
<boolean>true</boolean>
|
||||||
|
|
||||||
<key>FetchLib</key>
|
<key>FetchLib2</key>
|
||||||
<boolean>true</boolean>
|
<boolean>true</boolean>
|
||||||
</map>
|
</map>
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
|||||||
include(Variables)
|
include(Variables)
|
||||||
|
|
||||||
# Load versions now. Install locations need them.
|
# Load versions now. Install locations need them.
|
||||||
include(Versions)
|
include(BuildVersion)
|
||||||
|
|
||||||
include(UnixInstall)
|
include(UnixInstall)
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo;Release;ReleaseSSE2;Debug" CACHE S
|
|||||||
# Platform-specific compilation flags.
|
# Platform-specific compilation flags.
|
||||||
|
|
||||||
if (WINDOWS)
|
if (WINDOWS)
|
||||||
|
# Remove default /Zm1000 flag that cmake inserts
|
||||||
|
string (REPLACE "/Zm1000" " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
|
||||||
|
|
||||||
# Don't build DLLs.
|
# Don't build DLLs.
|
||||||
set(BUILD_SHARED_LIBS OFF)
|
set(BUILD_SHARED_LIBS OFF)
|
||||||
|
|
||||||
@@ -67,6 +70,10 @@ if (WINDOWS)
|
|||||||
/Oy-
|
/Oy-
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# configure win32 API for windows XP+ compatibility
|
||||||
|
set(WINVER "0x0501" CACHE STRING "Win32 API Target version (see http://msdn.microsoft.com/en-us/library/aa383745%28v=VS.85%29.aspx)")
|
||||||
|
add_definitions("/DWINVER=${WINVER}" "/D_WIN32_WINNT=${WINVER}")
|
||||||
|
|
||||||
if(MSVC80 OR MSVC90 OR MSVC10)
|
if(MSVC80 OR MSVC90 OR MSVC10)
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE
|
set(CMAKE_CXX_FLAGS_RELEASE
|
||||||
"${CMAKE_CXX_FLAGS_RELEASE} -D_SECURE_STL=0 -D_HAS_ITERATOR_DEBUGGING=0"
|
"${CMAKE_CXX_FLAGS_RELEASE} -D_SECURE_STL=0 -D_HAS_ITERATOR_DEBUGGING=0"
|
||||||
@@ -113,7 +120,7 @@ if (WINDOWS)
|
|||||||
|
|
||||||
endif (WINDOWS)
|
endif (WINDOWS)
|
||||||
|
|
||||||
set (GCC_EXTRA_OPTIMIZATION "-ffast-math -frounding-math")
|
set (GCC_EXTRA_OPTIMIZATIONS "-ffast-math")
|
||||||
|
|
||||||
if (LINUX)
|
if (LINUX)
|
||||||
set(CMAKE_SKIP_RPATH TRUE)
|
set(CMAKE_SKIP_RPATH TRUE)
|
||||||
@@ -156,7 +163,7 @@ if (LINUX)
|
|||||||
endif (NOT ${GXX_VERSION} MATCHES " 4.1.*Red Hat")
|
endif (NOT ${GXX_VERSION} MATCHES " 4.1.*Red Hat")
|
||||||
endif (${GXX_VERSION} STREQUAL ${CXX_VERSION})
|
endif (${GXX_VERSION} STREQUAL ${CXX_VERSION})
|
||||||
|
|
||||||
#Lets actualy get a numerical version of gxx's version
|
#Lets actually get a numerical version of gxx's version
|
||||||
STRING(REGEX REPLACE ".* ([0-9])\\.([0-9])\\.([0-9]).*" "\\1\\2\\3" CXX_VERSION ${CXX_VERSION})
|
STRING(REGEX REPLACE ".* ([0-9])\\.([0-9])\\.([0-9]).*" "\\1\\2\\3" CXX_VERSION ${CXX_VERSION})
|
||||||
|
|
||||||
#gcc 4.3 and above don't like the LL boost
|
#gcc 4.3 and above don't like the LL boost
|
||||||
@@ -182,53 +189,31 @@ if (LINUX)
|
|||||||
-pthread
|
-pthread
|
||||||
)
|
)
|
||||||
|
|
||||||
if (SERVER)
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftemplate-depth-60")
|
|
||||||
if (EXISTS /etc/debian_version)
|
|
||||||
FILE(READ /etc/debian_version DEBIAN_VERSION)
|
|
||||||
else (EXISTS /etc/debian_version)
|
|
||||||
set(DEBIAN_VERSION "")
|
|
||||||
endif (EXISTS /etc/debian_version)
|
|
||||||
|
|
||||||
if (NOT DEBIAN_VERSION STREQUAL "3.1")
|
add_definitions(-DAPPID=secondlife)
|
||||||
add_definitions(-DCTYPE_WORKAROUND)
|
add_definitions(-fvisibility=hidden)
|
||||||
endif (NOT DEBIAN_VERSION STREQUAL "3.1")
|
# don't catch SIGCHLD in our base application class for the viewer - some of our 3rd party libs may need their *own* SIGCHLD handler to work. Sigh! The viewer doesn't need to catch SIGCHLD anyway.
|
||||||
|
add_definitions(-DLL_IGNORE_SIGCHLD)
|
||||||
if (EXISTS /usr/lib/mysql4/mysql)
|
if (NOT STANDALONE)
|
||||||
link_directories(/usr/lib/mysql4/mysql)
|
# this stops us requiring a really recent glibc at runtime
|
||||||
endif (EXISTS /usr/lib/mysql4/mysql)
|
add_definitions(-fno-stack-protector)
|
||||||
|
endif (NOT STANDALONE)
|
||||||
add_definitions(
|
if (${ARCH} STREQUAL "x86_64")
|
||||||
-msse2
|
add_definitions(-DLINUX64=1 -pipe)
|
||||||
-mfpmath=sse
|
set(CMAKE_CXX_FLAGS_RELEASESSE2 "${CMAKE_CXX_FLAGS_RELEASESSE2} -fomit-frame-pointer -mmmx -msse -mfpmath=sse -msse2 -ffast-math -ftree-vectorize -fweb -fexpensive-optimizations -frename-registers")
|
||||||
)
|
set(CMAKE_C_FLAGS_RELEASESSE2 "${CMAKE_C_FLAGS_RELEASESSE2} -fomit-frame-pointer -mmmx -msse -mfpmath=sse -msse2 -ffast-math -ftree-vectorize -fweb -fexpensive-optimizations -frename-registers")
|
||||||
endif (SERVER)
|
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -fomit-frame-pointer -mmmx -msse -mfpmath=sse -msse2 -ffast-math -ftree-vectorize -fweb -fexpensive-optimizations -frename-registers")
|
||||||
|
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -fomit-frame-pointer -mmmx -msse -mfpmath=sse -msse2 -ffast-math -ftree-vectorize -fweb -fexpensive-optimizations -frename-registers")
|
||||||
if (VIEWER)
|
else (${ARCH} STREQUAL "x86_64")
|
||||||
add_definitions(-DAPPID=secondlife)
|
|
||||||
add_definitions(-fvisibility=hidden)
|
|
||||||
# don't catch SIGCHLD in our base application class for the viewer - some of our 3rd party libs may need their *own* SIGCHLD handler to work. Sigh! The viewer doesn't need to catch SIGCHLD anyway.
|
|
||||||
add_definitions(-DLL_IGNORE_SIGCHLD)
|
|
||||||
if (NOT STANDALONE)
|
if (NOT STANDALONE)
|
||||||
# this stops us requiring a really recent glibc at runtime
|
set(MARCH_FLAG " -march=pentium4")
|
||||||
add_definitions(-fno-stack-protector)
|
|
||||||
endif (NOT STANDALONE)
|
endif (NOT STANDALONE)
|
||||||
if (${ARCH} STREQUAL "x86_64")
|
set(CMAKE_CXX_FLAGS_RELEASESSE2 "${CMAKE_CXX_FLAGS_RELEASESSE2}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
|
||||||
add_definitions(-DLINUX64=1 -pipe)
|
set(CMAKE_C_FLAGS_RELEASESSE2 "${CMAKE_C_FLAGS_RELEASESSE2}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASESSE2 "${CMAKE_CXX_FLAGS_RELEASESSE2} -fomit-frame-pointer -mmmx -msse -mfpmath=sse -msse2 -ffast-math -ftree-vectorize -fweb -fexpensive-optimizations -frename-registers")
|
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
|
||||||
set(CMAKE_C_FLAGS_RELEASESSE2 "${CMAKE_C_FLAGS_RELEASESSE2} -fomit-frame-pointer -mmmx -msse -mfpmath=sse -msse2 -ffast-math -ftree-vectorize -fweb -fexpensive-optimizations -frename-registers")
|
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse,387 -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
|
||||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -fomit-frame-pointer -mmmx -msse -mfpmath=sse -msse2 -ffast-math -ftree-vectorize -fweb -fexpensive-optimizations -frename-registers")
|
endif (${ARCH} STREQUAL "x86_64")
|
||||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -fomit-frame-pointer -mmmx -msse -mfpmath=sse -msse2 -ffast-math -ftree-vectorize -fweb -fexpensive-optimizations -frename-registers")
|
|
||||||
else (${ARCH} STREQUAL "x86_64")
|
|
||||||
if (NOT STANDALONE)
|
|
||||||
set(MARCH_FLAG " -march=pentium4")
|
|
||||||
endif (NOT STANDALONE)
|
|
||||||
set(CMAKE_CXX_FLAGS_RELEASESSE2 "${CMAKE_CXX_FLAGS_RELEASESSE2}${MARCH_FLAG} -mfpmath=sse -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
|
|
||||||
set(CMAKE_C_FLAGS_RELEASESSE2 "${CMAKE_C_FLAGS_RELEASESSE2}${MARCH_FLAG} -mfpmath=sse -msse2 "${GCC_EXTRA_OPTIMIZATIONS})
|
|
||||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse -msse2 ${GCC_EXTRA_OPTIMIZATIONS}")
|
|
||||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}${MARCH_FLAG} -mfpmath=sse -msse2 "${GCC_EXTRA_OPTIMIZATIONS})
|
|
||||||
endif (${ARCH} STREQUAL "x86_64")
|
|
||||||
endif (VIEWER)
|
|
||||||
|
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "-fno-inline ${CMAKE_CXX_FLAGS_DEBUG} -msse2")
|
set(CMAKE_CXX_FLAGS_DEBUG "-fno-inline ${CMAKE_CXX_FLAGS_DEBUG} -msse2")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3 ${CMAKE_CXX_FLAGS_RELEASE}")
|
set(CMAKE_CXX_FLAGS_RELEASE "-O3 ${CMAKE_CXX_FLAGS_RELEASE}")
|
||||||
@@ -258,13 +243,13 @@ endif (DARWIN)
|
|||||||
|
|
||||||
|
|
||||||
if (LINUX OR DARWIN)
|
if (LINUX OR DARWIN)
|
||||||
set(GCC_WARNINGS "-Wall -Wno-sign-compare -Wno-trigraphs -Wno-non-virtual-dtor -Woverloaded-virtual")
|
set(GCC_WARNINGS "-Wall -Wno-sign-compare -Wno-trigraphs")
|
||||||
|
|
||||||
if (NOT GCC_DISABLE_FATAL_WARNINGS)
|
if (NOT GCC_DISABLE_FATAL_WARNINGS)
|
||||||
set(GCC_WARNINGS "${GCC_WARNINGS} -Werror")
|
set(GCC_WARNINGS "${GCC_WARNINGS} -Werror")
|
||||||
endif (NOT GCC_DISABLE_FATAL_WARNINGS)
|
endif (NOT GCC_DISABLE_FATAL_WARNINGS)
|
||||||
|
|
||||||
set(GCC_CXX_WARNINGS "${GCC_WARNINGS} -Wno-reorder")
|
set(GCC_CXX_WARNINGS "${GCC_WARNINGS} -Wno-reorder -Wno-non-virtual-dtor -Woverloaded-virtual")
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS "${GCC_WARNINGS} ${CMAKE_C_FLAGS}")
|
set(CMAKE_C_FLAGS "${GCC_WARNINGS} ${CMAKE_C_FLAGS}")
|
||||||
set(CMAKE_CXX_FLAGS "${GCC_CXX_WARNINGS} ${CMAKE_CXX_FLAGS}")
|
set(CMAKE_CXX_FLAGS "${GCC_CXX_WARNINGS} ${CMAKE_CXX_FLAGS}")
|
||||||
@@ -298,10 +283,6 @@ if(1 EQUAL 1)
|
|||||||
add_definitions(-DMESH_ENABLED=1)
|
add_definitions(-DMESH_ENABLED=1)
|
||||||
endif(1 EQUAL 1)
|
endif(1 EQUAL 1)
|
||||||
|
|
||||||
if(SERVER)
|
|
||||||
include_directories(${LIBS_PREBUILT_DIR}/include/havok)
|
|
||||||
endif(SERVER)
|
|
||||||
|
|
||||||
SET( CMAKE_EXE_LINKER_FLAGS_RELEASESSE2
|
SET( CMAKE_EXE_LINKER_FLAGS_RELEASESSE2
|
||||||
"${CMAKE_EXE_LINKER_FLAGS_RELEASE}" CACHE STRING
|
"${CMAKE_EXE_LINKER_FLAGS_RELEASE}" CACHE STRING
|
||||||
"Flags used for linking binaries under SSE2 build."
|
"Flags used for linking binaries under SSE2 build."
|
||||||
|
|||||||
@@ -29,12 +29,12 @@ else (STANDALONE)
|
|||||||
)
|
)
|
||||||
elseif (DARWIN)
|
elseif (DARWIN)
|
||||||
set(APR_LIBRARIES
|
set(APR_LIBRARIES
|
||||||
debug ${ARCH_PREBUILT_DIRS_DEBUG}/libapr-1.0.3.7.dylib
|
debug ${ARCH_PREBUILT_DIRS_DEBUG}/libapr-1.0.dylib
|
||||||
optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libapr-1.0.3.7.dylib
|
optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libapr-1.0.dylib
|
||||||
)
|
)
|
||||||
set(APRUTIL_LIBRARIES
|
set(APRUTIL_LIBRARIES
|
||||||
debug ${ARCH_PREBUILT_DIRS_DEBUG}/libaprutil-1.0.3.8.dylib
|
debug ${ARCH_PREBUILT_DIRS_DEBUG}/libaprutil-1.0.dylib
|
||||||
optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libaprutil-1.0.3.8.dylib
|
optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libaprutil-1.0.dylib
|
||||||
)
|
)
|
||||||
set(APRICONV_LIBRARIES iconv)
|
set(APRICONV_LIBRARIES iconv)
|
||||||
else (WINDOWS)
|
else (WINDOWS)
|
||||||
|
|||||||
@@ -6,6 +6,11 @@ set(DB_FIND_REQUIRED ON)
|
|||||||
if (STANDALONE)
|
if (STANDALONE)
|
||||||
include(FindBerkeleyDB)
|
include(FindBerkeleyDB)
|
||||||
else (STANDALONE)
|
else (STANDALONE)
|
||||||
set(DB_LIBRARIES db-4.2)
|
if (LINUX)
|
||||||
|
# Need to add dependency pthread explicitely to support ld.gold.
|
||||||
|
set(DB_LIBRARIES db-4.2 pthread)
|
||||||
|
else (LINUX)
|
||||||
|
set(DB_LIBRARIES db-4.2)
|
||||||
|
endif (LINUX)
|
||||||
set(DB_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include)
|
set(DB_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include)
|
||||||
endif (STANDALONE)
|
endif (STANDALONE)
|
||||||
|
|||||||
@@ -1,23 +1,45 @@
|
|||||||
# -*- cmake -*-
|
# -*- cmake -*-
|
||||||
|
|
||||||
function (build_version _target)
|
# Read version components from the header file.
|
||||||
# Read version components from the header file.
|
file(STRINGS ${LIBS_OPEN_DIR}/llcommon/llversionviewer.h.in lines
|
||||||
file(STRINGS ${LIBS_OPEN_DIR}/llcommon/llversion${_target}.h lines
|
REGEX " LL_VERSION_")
|
||||||
REGEX " LL_VERSION_")
|
foreach(line ${lines})
|
||||||
foreach(line ${lines})
|
string(REGEX REPLACE ".*LL_VERSION_([A-Z]+).*" "\\1" comp "${line}")
|
||||||
string(REGEX REPLACE ".*LL_VERSION_([A-Z]+).*" "\\1" comp "${line}")
|
string(REGEX REPLACE ".* = ([0-9]+);.*" "\\1" value "${line}")
|
||||||
string(REGEX REPLACE ".* = ([0-9]+);.*" "\\1" value "${line}")
|
set(v${comp} "${value}")
|
||||||
set(v${comp} "${value}")
|
endforeach(line)
|
||||||
endforeach(line)
|
|
||||||
|
|
||||||
# Compose the version.
|
execute_process(
|
||||||
set(${_target}_VERSION "${vMAJOR}.${vMINOR}.${vPATCH}.${vBUILD}")
|
COMMAND git rev-list HEAD
|
||||||
if (${_target}_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
|
OUTPUT_VARIABLE GIT_REV_LIST_STR
|
||||||
message(STATUS "Version of ${_target} is ${${_target}_VERSION}")
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
else (${_target}_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
message(FATAL_ERROR "Could not determine ${_target} version (${${_target}_VERSION})")
|
)
|
||||||
endif (${_target}_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
|
|
||||||
|
|
||||||
# Report version to caller.
|
if(GIT_REV_LIST_STR)
|
||||||
set(${_target}_VERSION "${${_target}_VERSION}" PARENT_SCOPE)
|
string(REPLACE "\n" ";" GIT_REV_LIST ${GIT_REV_LIST_STR})
|
||||||
endfunction (build_version)
|
else()
|
||||||
|
string(REPLACE "\n" ";" GIT_REV_LIST "")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(GIT_REV_LIST)
|
||||||
|
list(LENGTH GIT_REV_LIST vBUILD)
|
||||||
|
else()
|
||||||
|
set(vBUILD 99)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
${CMAKE_SOURCE_DIR}/llcommon/llversionviewer.h.in
|
||||||
|
${CMAKE_SOURCE_DIR}/llcommon/llversionviewer.h
|
||||||
|
)
|
||||||
|
|
||||||
|
# Compose the version.
|
||||||
|
set(viewer_VERSION "${vMAJOR}.${vMINOR}.${vPATCH}.${vBUILD}")
|
||||||
|
if (viewer_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
|
||||||
|
message(STATUS "Version is ${viewer_VERSION}")
|
||||||
|
else (viewer_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
|
||||||
|
message(FATAL_ERROR "Could not determine version (${viewer_VERSION})")
|
||||||
|
endif (viewer_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")
|
||||||
|
|
||||||
|
# Report version to caller.
|
||||||
|
#set(viewer_VERSION "${viewer_VERSION}" PARENT_SCOPE)
|
||||||
|
|||||||
@@ -19,5 +19,5 @@ else (STANDALONE)
|
|||||||
else (WINDOWS)
|
else (WINDOWS)
|
||||||
set(CARES_LIBRARIES cares)
|
set(CARES_LIBRARIES cares)
|
||||||
endif (WINDOWS)
|
endif (WINDOWS)
|
||||||
set(CARES_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/ares)
|
set(CARES_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/ares ${LIBS_PREBUILT_DIR}/include/ares )
|
||||||
endif (STANDALONE)
|
endif (STANDALONE)
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ set(cmake_SOURCE_FILES
|
|||||||
FindNDOF.cmake
|
FindNDOF.cmake
|
||||||
FindOpenJPEG.cmake
|
FindOpenJPEG.cmake
|
||||||
FindXmlRpcEpi.cmake
|
FindXmlRpcEpi.cmake
|
||||||
FMOD.cmake
|
FMOD.cmake
|
||||||
FreeType.cmake
|
FreeType.cmake
|
||||||
GStreamer010Plugin.cmake
|
GStreamer010Plugin.cmake
|
||||||
GooglePerfTools.cmake
|
GooglePerfTools.cmake
|
||||||
@@ -79,11 +79,14 @@ set(cmake_SOURCE_FILES
|
|||||||
UI.cmake
|
UI.cmake
|
||||||
UnixInstall.cmake
|
UnixInstall.cmake
|
||||||
Variables.cmake
|
Variables.cmake
|
||||||
Versions.cmake
|
|
||||||
XmlRpcEpi.cmake
|
XmlRpcEpi.cmake
|
||||||
ZLIB.cmake
|
ZLIB.cmake
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(FMODEX)
|
||||||
|
list(APPEND cmake_SOURCE_FILES FMODEX.cmake)
|
||||||
|
endif(FMODEX)
|
||||||
|
|
||||||
source_group("Shared Rules" FILES ${cmake_SOURCE_FILES})
|
source_group("Shared Rules" FILES ${cmake_SOURCE_FILES})
|
||||||
|
|
||||||
set(master_SOURCE_FILES
|
set(master_SOURCE_FILES
|
||||||
|
|||||||
@@ -243,13 +243,52 @@ set(all_targets ${all_targets} ${out_targets})
|
|||||||
set(release_src_dir "${CMAKE_SOURCE_DIR}/../libraries/i686-win32/lib/release")
|
set(release_src_dir "${CMAKE_SOURCE_DIR}/../libraries/i686-win32/lib/release")
|
||||||
set(release_files
|
set(release_files
|
||||||
libtcmalloc_minimal.dll
|
libtcmalloc_minimal.dll
|
||||||
fmod.dll
|
|
||||||
libhunspell.dll
|
libhunspell.dll
|
||||||
libapr-1.dll
|
libapr-1.dll
|
||||||
libaprutil-1.dll
|
libaprutil-1.dll
|
||||||
libapriconv-1.dll
|
libapriconv-1.dll
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(FMODEX)
|
||||||
|
find_path(FMODEX_BINARY_DIR fmodex.dll
|
||||||
|
${release_src_dir}
|
||||||
|
${FMODEX_SDK_DIR}/api
|
||||||
|
${FMODEX_SDK_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
if(FMODEX_BINARY_DIR)
|
||||||
|
copy_if_different("${FMODEX_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/Release" out_targets fmodex.dll)
|
||||||
|
set(all_targets ${all_targets} ${out_targets})
|
||||||
|
copy_if_different("${FMODEX_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/ReleaseSSE2" out_targets fmodex.dll)
|
||||||
|
set(all_targets ${all_targets} ${out_targets})
|
||||||
|
copy_if_different("${FMODEX_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo" out_targets fmodex.dll)
|
||||||
|
set(all_targets ${all_targets} ${out_targets})
|
||||||
|
copy_if_different("${FMODEX_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/Debug" out_targets fmodex.dll)
|
||||||
|
set(all_targets ${all_targets} ${out_targets})
|
||||||
|
endif(FMODEX_BINARY_DIR)
|
||||||
|
endif(FMODEX)
|
||||||
|
|
||||||
|
if(FMOD)
|
||||||
|
find_path(FMOD_BINARY_DIR fmod.dll
|
||||||
|
${release_src_dir}
|
||||||
|
${FMOD_SDK_DIR}/api
|
||||||
|
${FMOD_SDK_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
if(FMOD_BINARY_DIR)
|
||||||
|
copy_if_different("${FMOD_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/Release" out_targets fmod.dll)
|
||||||
|
set(all_targets ${all_targets} ${out_targets})
|
||||||
|
copy_if_different("${FMOD_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/ReleaseSSE2" out_targets fmod.dll)
|
||||||
|
set(all_targets ${all_targets} ${out_targets})
|
||||||
|
copy_if_different("${FMOD_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo" out_targets fmod.dll)
|
||||||
|
set(all_targets ${all_targets} ${out_targets})
|
||||||
|
copy_if_different("${FMOD_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/Debug" out_targets fmod.dll)
|
||||||
|
set(all_targets ${all_targets} ${out_targets})
|
||||||
|
else(FMOD_BINARY_DIR)
|
||||||
|
list(APPEND release_files fmod.dll) #Required for compile. This will cause an error in copying binaries.
|
||||||
|
endif(FMOD_BINARY_DIR)
|
||||||
|
endif(FMOD)
|
||||||
|
|
||||||
copy_if_different(
|
copy_if_different(
|
||||||
${release_src_dir}
|
${release_src_dir}
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/Release"
|
"${CMAKE_CURRENT_BINARY_DIR}/Release"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
if (VIEWER AND WINDOWS)
|
if (VIEWER AND WINDOWS)
|
||||||
find_path(DIRECTX_INCLUDE_DIR dxdiag.h
|
find_path(DIRECTX_INCLUDE_DIR dxdiag.h
|
||||||
"$ENV{DXSDK_DIR}/Include"
|
"$ENV{DXSDK_DIR}/Include"
|
||||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2010)/Include"
|
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2010)/Include"
|
||||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (March 2009)/Include"
|
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (March 2009)/Include"
|
||||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (August 2008)/Include"
|
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (August 2008)/Include"
|
||||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2008)/Include"
|
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2008)/Include"
|
||||||
@@ -25,7 +25,7 @@ if (VIEWER AND WINDOWS)
|
|||||||
|
|
||||||
find_path(DIRECTX_LIBRARY_DIR dxguid.lib
|
find_path(DIRECTX_LIBRARY_DIR dxguid.lib
|
||||||
"$ENV{DXSDK_DIR}/Lib/x86"
|
"$ENV{DXSDK_DIR}/Lib/x86"
|
||||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2010)/Lib/x86"
|
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2010)/Lib/x86"
|
||||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (March 2009)/Lib/x86"
|
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (March 2009)/Lib/x86"
|
||||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (August 2008)/Lib/x86"
|
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (August 2008)/Lib/x86"
|
||||||
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2008)/Lib/x86"
|
"$ENV{PROGRAMFILES}/Microsoft DirectX SDK (June 2008)/Lib/x86"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ set(install_dir "@CMAKE_SOURCE_DIR@/..")
|
|||||||
set(scp "@SCP_EXECUTABLE@")
|
set(scp "@SCP_EXECUTABLE@")
|
||||||
set(scripts_dir "@SCRIPTS_DIR@")
|
set(scripts_dir "@SCRIPTS_DIR@")
|
||||||
set(sentinel_dir "@CMAKE_BINARY_DIR@/prepare")
|
set(sentinel_dir "@CMAKE_BINARY_DIR@/prepare")
|
||||||
|
set(prebuilt_type "@PREBUILT_TYPE@")
|
||||||
|
|
||||||
# In proprietary mode we use scp for download.
|
# In proprietary mode we use scp for download.
|
||||||
set(proprietary "@INSTALL_PROPRIETARY@")
|
set(proprietary "@INSTALL_PROPRIETARY@")
|
||||||
@@ -19,7 +20,7 @@ foreach(package ${packages})
|
|||||||
# This package is missing or out of date.
|
# This package is missing or out of date.
|
||||||
message(STATUS "Obtaining${proprietary_message} prebuilt '${package}'")
|
message(STATUS "Obtaining${proprietary_message} prebuilt '${package}'")
|
||||||
execute_process(
|
execute_process(
|
||||||
COMMAND ${python} install.py --install-dir=${install_dir} ${scp_option} ${package}
|
COMMAND ${python} install.py -p${prebuilt_type} --install-dir=${install_dir} ${scp_option} ${package}
|
||||||
WORKING_DIRECTORY ${scripts_dir}
|
WORKING_DIRECTORY ${scripts_dir}
|
||||||
RESULT_VARIABLE result
|
RESULT_VARIABLE result
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,26 +7,13 @@ if(INSTALL_PROPRIETARY)
|
|||||||
use_prebuilt_binary(fmod)
|
use_prebuilt_binary(fmod)
|
||||||
endif(INSTALL_PROPRIETARY)
|
endif(INSTALL_PROPRIETARY)
|
||||||
|
|
||||||
find_library(FMOD_LIBRARY_RELEASE
|
find_library(FMOD_LIBRARY
|
||||||
NAMES fmod fmodvc fmod-3.75
|
NAMES fmod fmodvc fmod-3.75
|
||||||
PATHS
|
PATHS
|
||||||
${ARCH_PREBUILT_DIRS_RELEASE}
|
optimized ${ARCH_PREBUILT_DIRS_RELEASE}
|
||||||
|
debug ${ARCH_PREBUILT_DIRS_DEBUG}
|
||||||
)
|
)
|
||||||
|
|
||||||
find_library(FMOD_LIBRARY_DEBUG
|
|
||||||
NAMES fmod fmodvc fmod-3.75
|
|
||||||
PATHS
|
|
||||||
${ARCH_PREBUILT_DIRS_DEBUG}
|
|
||||||
)
|
|
||||||
|
|
||||||
if (FMOD_LIBRARY_RELEASE AND FMOD_LIBRARY_DEBUG)
|
|
||||||
set(FMOD_LIBRARY
|
|
||||||
debug ${FMOD_LIBRARY_DEBUG}
|
|
||||||
optimized ${FMOD_LIBRARY_RELEASE})
|
|
||||||
elseif (FMOD_LIBRARY_RELEASE)
|
|
||||||
set(FMOD_LIBRARY ${FMOD_LIBRARY_RELEASE})
|
|
||||||
endif (FMOD_LIBRARY_RELEASE AND FMOD_LIBRARY_DEBUG)
|
|
||||||
|
|
||||||
if (NOT FMOD_LIBRARY)
|
if (NOT FMOD_LIBRARY)
|
||||||
set(FMOD_SDK_DIR CACHE PATH "Path to the FMOD SDK.")
|
set(FMOD_SDK_DIR CACHE PATH "Path to the FMOD SDK.")
|
||||||
if (FMOD_SDK_DIR)
|
if (FMOD_SDK_DIR)
|
||||||
|
|||||||
70
indra/cmake/FMODEX.cmake
Normal file
70
indra/cmake/FMODEX.cmake
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
# -*- cmake -*-
|
||||||
|
|
||||||
|
include(Linking)
|
||||||
|
|
||||||
|
if(INSTALL_PROPRIETARY)
|
||||||
|
include(Prebuilt)
|
||||||
|
use_prebuilt_binary(fmodex)
|
||||||
|
endif(INSTALL_PROPRIETARY)
|
||||||
|
|
||||||
|
find_library(FMODEX_LIBRARY
|
||||||
|
NAMES fmodex fmodex_vc fmodexL_vc
|
||||||
|
PATHS
|
||||||
|
optimized ${ARCH_PREBUILT_DIRS_RELEASE}
|
||||||
|
debug ${ARCH_PREBUILT_DIRS_DEBUG}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (NOT FMODEX_LIBRARY)
|
||||||
|
set(FMODEX_SDK_DIR CACHE PATH "Path to the FMOD Ex SDK.")
|
||||||
|
if (FMODEX_SDK_DIR)
|
||||||
|
find_library(FMODEX_LIBRARY
|
||||||
|
fmodex fmodex_vc fmodexL_vc
|
||||||
|
PATHS
|
||||||
|
${FMODEX_SDK_DIR}/api/lib
|
||||||
|
${FMODEX_SDK_DIR}/api
|
||||||
|
${FMODEX_SDK_DIR}/lib
|
||||||
|
${FMODEX_SDK_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
endif(FMODEX_SDK_DIR)
|
||||||
|
if(WINDOWS AND NOT FMODEX_LIBRARY)
|
||||||
|
set(FMODEX_PROG_DIR "$ENV{PROGRAMFILES}/FMOD SoundSystem/FMOD Programmers API Windows")
|
||||||
|
find_library(FMODEX_LIBRARY
|
||||||
|
fmodex_vc fmodexL_vc
|
||||||
|
PATHS
|
||||||
|
${FMODEX_PROG_DIR}/api/lib
|
||||||
|
${FMODEX_PROG_DIR}/api
|
||||||
|
${FMODEX_PROG_DIR}
|
||||||
|
)
|
||||||
|
if(FMODEX_LIBRARY)
|
||||||
|
message(STATUS "Found fmodex in ${FMODEX_PROG_DIR}")
|
||||||
|
set(FMODEX_SDK_DIR ${FMODEX_PROG_DIR})
|
||||||
|
set(FMODEX_SDK_DIR ${FMODEX_PROG_DIR} CACHE PATH "Path to the FMOD Ex SDK." FORCE)
|
||||||
|
endif(FMODEX_LIBRARY)
|
||||||
|
endif(WINDOWS AND NOT FMODEX_LIBRARY)
|
||||||
|
endif (NOT FMODEX_LIBRARY)
|
||||||
|
|
||||||
|
find_path(FMODEX_INCLUDE_DIR fmod.h
|
||||||
|
${LIBS_PREBUILT_DIR}/include/fmodex
|
||||||
|
${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/fmodex
|
||||||
|
${FMODEX_SDK_DIR}/api/inc
|
||||||
|
${FMODEX_SDK_DIR}/inc
|
||||||
|
${FMODEX_SDK_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (FMODEX_LIBRARY AND FMODEX_INCLUDE_DIR)
|
||||||
|
set(FMODEX ON CACHE BOOL "Use closed source FMOD Ex sound library.")
|
||||||
|
else (FMODEX_LIBRARY AND FMODEX_INCLUDE_DIR)
|
||||||
|
set(FMODEX_LIBRARY "")
|
||||||
|
set(FMODEX_INCLUDE_DIR "")
|
||||||
|
if (FMODEX)
|
||||||
|
message(STATUS "No support for FMOD Ex audio (need to set FMODEX_SDK_DIR?)")
|
||||||
|
endif (FMODEX)
|
||||||
|
set(FMODEX OFF CACHE BOOL "Use closed source FMOD Ex sound library.")
|
||||||
|
set(FMODEX OFF)
|
||||||
|
endif (FMODEX_LIBRARY AND FMODEX_INCLUDE_DIR)
|
||||||
|
|
||||||
|
if (FMODEX)
|
||||||
|
message(STATUS "Building with FMOD Ex audio support")
|
||||||
|
endif (FMODEX)
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
# also defined, but not for general use are
|
# also defined, but not for general use are
|
||||||
# JSONCPP_LIBRARY, where to find the jsoncpp library.
|
# JSONCPP_LIBRARY, where to find the jsoncpp library.
|
||||||
|
|
||||||
FIND_PATH(JSONCPP_INCLUDE_DIR jsoncpp/json.h
|
FIND_PATH(JSONCPP_INCLUDE_DIR json/json.h
|
||||||
/usr/local/include
|
/usr/local/include
|
||||||
/usr/include
|
/usr/include
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
# -*- cmake -*-
|
# -*- cmake -*-
|
||||||
include(Prebuilt)
|
include(Prebuilt)
|
||||||
|
|
||||||
|
if(WORD_SIZE EQUAL 64)
|
||||||
|
set(DISABLE_TCMALLOC TRUE)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (STANDALONE)
|
if (STANDALONE)
|
||||||
include(FindGooglePerfTools)
|
include(FindGooglePerfTools)
|
||||||
else (STANDALONE)
|
else (STANDALONE)
|
||||||
if (LINUX OR WINDOWS)
|
if (LINUX OR WINDOWS AND NOT WORD_SIZE EQUAL 64)
|
||||||
use_prebuilt_binary(google)
|
use_prebuilt_binary(google)
|
||||||
endif (LINUX OR WINDOWS)
|
endif (LINUX OR WINDOWS)
|
||||||
if (WINDOWS)
|
if (WINDOWS)
|
||||||
@@ -30,10 +34,6 @@ else ()
|
|||||||
set(USE_GOOGLE_PERFTOOLS OFF)
|
set(USE_GOOGLE_PERFTOOLS OFF)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
# XXX Disable temporarily, until we have compilation issues on 64-bit
|
|
||||||
# Etch sorted.
|
|
||||||
#set(USE_GOOGLE_PERFTOOLS OFF)
|
|
||||||
|
|
||||||
if (USE_GOOGLE_PERFTOOLS)
|
if (USE_GOOGLE_PERFTOOLS)
|
||||||
set(TCMALLOC_FLAG -DLL_USE_TCMALLOC=1)
|
set(TCMALLOC_FLAG -DLL_USE_TCMALLOC=1)
|
||||||
include_directories(${GOOGLE_PERFTOOLS_INCLUDE_DIR})
|
include_directories(${GOOGLE_PERFTOOLS_INCLUDE_DIR})
|
||||||
|
|||||||
@@ -14,11 +14,19 @@ endif (DARWIN)
|
|||||||
set(LLCOMMON_INCLUDE_DIRS
|
set(LLCOMMON_INCLUDE_DIRS
|
||||||
${LIBS_OPEN_DIR}/cwdebug
|
${LIBS_OPEN_DIR}/cwdebug
|
||||||
${LIBS_OPEN_DIR}/llcommon
|
${LIBS_OPEN_DIR}/llcommon
|
||||||
|
${APRUTIL_INCLUDE_DIR}
|
||||||
${APR_INCLUDE_DIR}
|
${APR_INCLUDE_DIR}
|
||||||
${Boost_INCLUDE_DIRS}
|
${Boost_INCLUDE_DIRS}
|
||||||
)
|
)
|
||||||
|
|
||||||
set(LLCOMMON_LIBRARIES llcommon)
|
if (LINUX)
|
||||||
|
# In order to support using ld.gold on linux, we need to explicitely
|
||||||
|
# specify all libraries that llcommon uses.
|
||||||
|
# llcommon uses `clock_gettime' which is provided by librt on linux.
|
||||||
|
set(LLCOMMON_LIBRARIES llcommon rt)
|
||||||
|
else (LINUX)
|
||||||
|
set(LLCOMMON_LIBRARIES llcommon)
|
||||||
|
endif (LINUX)
|
||||||
|
|
||||||
set(LLCOMMON_LINK_SHARED ON CACHE BOOL "Build the llcommon target as a shared library.")
|
set(LLCOMMON_LINK_SHARED ON CACHE BOOL "Build the llcommon target as a shared library.")
|
||||||
if(LLCOMMON_LINK_SHARED)
|
if(LLCOMMON_LINK_SHARED)
|
||||||
|
|||||||
@@ -5,4 +5,10 @@ set(LLPLUGIN_INCLUDE_DIRS
|
|||||||
${LIBS_OPEN_DIR}/llplugin
|
${LIBS_OPEN_DIR}/llplugin
|
||||||
)
|
)
|
||||||
|
|
||||||
set(LLPLUGIN_LIBRARIES llplugin)
|
if (LINUX)
|
||||||
|
# In order to support using ld.gold on linux, we need to explicitely
|
||||||
|
# specify all libraries that llplugin uses.
|
||||||
|
set(LLPLUGIN_LIBRARIES llplugin pthread)
|
||||||
|
else (LINUX)
|
||||||
|
set(LLPLUGIN_LIBRARIES llplugin)
|
||||||
|
endif (LINUX)
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# -*- cmake -*-
|
# -*- cmake -*-
|
||||||
include(Prebuilt)
|
include(Prebuilt)
|
||||||
|
|
||||||
if (NOT STANDALONE)
|
if (NOT (STANDALONE OR DARWIN))
|
||||||
use_prebuilt_binary(glext)
|
use_prebuilt_binary(glext)
|
||||||
# possible glh_linear should have its own .cmake file instead
|
# possible glh_linear should have its own .cmake file instead
|
||||||
#use_prebuilt_binary(glh_linear)
|
#use_prebuilt_binary(glh_linear)
|
||||||
# actually... not any longer, it's now in git -SG
|
# actually... not any longer, it's now in git -SG
|
||||||
set(GLEXT_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include)
|
set(GLEXT_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include)
|
||||||
endif (NOT STANDALONE)
|
endif ()
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ if (WINDOWS)
|
|||||||
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.5\\InstallPath]
|
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.5\\InstallPath]
|
||||||
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.4\\InstallPath]
|
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.4\\InstallPath]
|
||||||
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.3\\InstallPath]
|
[HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.3\\InstallPath]
|
||||||
|
|
||||||
)
|
)
|
||||||
elseif (EXISTS /etc/debian_version)
|
elseif (EXISTS /etc/debian_version)
|
||||||
# On Debian and Ubuntu, avoid Python 2.4 if possible.
|
# On Debian and Ubuntu, avoid Python 2.4 if possible.
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ else (STANDALONE)
|
|||||||
if (LINUX)
|
if (LINUX)
|
||||||
set(UI_LIBRARIES
|
set(UI_LIBRARIES
|
||||||
atk-1.0
|
atk-1.0
|
||||||
|
X11
|
||||||
gdk-x11-2.0
|
gdk-x11-2.0
|
||||||
gdk_pixbuf-2.0
|
gdk_pixbuf-2.0
|
||||||
Xinerama
|
Xinerama
|
||||||
|
|||||||
@@ -76,24 +76,16 @@ endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
|||||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||||
set(DARWIN 1)
|
set(DARWIN 1)
|
||||||
|
|
||||||
# NOTE: If specifying a different SDK with CMAKE_OSX_SYSROOT at configure
|
#SDK Compiler and Deployment targets for XCode
|
||||||
# time you should also specify CMAKE_OSX_DEPLOYMENT_TARGET explicitly,
|
if (${XCODE_VERSION} VERSION_LESS 4.0.0)
|
||||||
# otherwise CMAKE_OSX_SYSROOT will be overridden here. We can't just check
|
|
||||||
# for it being unset, as it gets set to the system default :(
|
|
||||||
|
|
||||||
# Default to building against the 10.5 SDK if no deployment target is
|
|
||||||
# specified.
|
|
||||||
if (NOT CMAKE_OSX_DEPLOYMENT_TARGET)
|
|
||||||
# NOTE: setting -isysroot is NOT adequate: http://lists.apple.com/archives/Xcode-users/2007/Oct/msg00696.html
|
|
||||||
# see http://public.kitware.com/Bug/view.php?id=9959 + poppy
|
|
||||||
set(CMAKE_OSX_SYSROOT /Developer/SDKs/MacOSX10.5.sdk)
|
set(CMAKE_OSX_SYSROOT /Developer/SDKs/MacOSX10.5.sdk)
|
||||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.5)
|
set(CMAKE_XCODE_ATTIBUTE_GCC_VERSION "4.2")
|
||||||
endif (NOT CMAKE_OSX_DEPLOYMENT_TARGET)
|
else (${XCODE_VERSION} VERSION_LESS 4.0.0)
|
||||||
|
set(CMAKE_OSX_SYSROOT /Developer/SDKs/MacOSX10.6.sdk)
|
||||||
|
set(CMAKE_XCODE_ATTRIBUTE_GCC_VERSION "com.apple.compilers.llvmgcc42")
|
||||||
|
endif (${XCODE_VERSION} VERSION_LESS 4.0.0)
|
||||||
|
|
||||||
# Use GCC 4.2
|
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.5)
|
||||||
if (${CMAKE_OSX_SYSROOT} MATCHES "10.5")
|
|
||||||
set(CMAKE_XCODE_ATTRIBUTE_GCC_VERSION "4.2")
|
|
||||||
endif (${CMAKE_OSX_SYSROOT} MATCHES "10.5")
|
|
||||||
|
|
||||||
# NOTE: To attempt an i386/PPC Universal build, add this on the configure line:
|
# NOTE: To attempt an i386/PPC Universal build, add this on the configure line:
|
||||||
# -DCMAKE_OSX_ARCHITECTURES:STRING='i386;ppc'
|
# -DCMAKE_OSX_ARCHITECTURES:STRING='i386;ppc'
|
||||||
@@ -118,6 +110,17 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
|||||||
endif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
endif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||||
|
|
||||||
|
|
||||||
|
if (WINDOWS)
|
||||||
|
set(PREBUILT_TYPE windows)
|
||||||
|
elseif(DARWIN)
|
||||||
|
set(PREBUILT_TYPE darwin)
|
||||||
|
elseif(LINUX AND WORD_SIZE EQUAL 32)
|
||||||
|
set(PREBUILT_TYPE linux)
|
||||||
|
elseif(LINUX AND WORD_SIZE EQUAL 64)
|
||||||
|
set(PREBUILT_TYPE linux64)
|
||||||
|
endif(WINDOWS)
|
||||||
|
|
||||||
|
|
||||||
# Default deploy grid
|
# Default deploy grid
|
||||||
set(GRID agni CACHE STRING "Target Grid")
|
set(GRID agni CACHE STRING "Target Grid")
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
include(BuildVersion)
|
|
||||||
|
|
||||||
if(VIEWER)
|
|
||||||
build_version(viewer)
|
|
||||||
endif(VIEWER)
|
|
||||||
|
|
||||||
if(SERVER)
|
|
||||||
build_version(server)
|
|
||||||
endif(SERVER)
|
|
||||||
@@ -67,12 +67,19 @@ elseif (LINUX)
|
|||||||
QtNetwork
|
QtNetwork
|
||||||
QtGui
|
QtGui
|
||||||
QtCore
|
QtCore
|
||||||
jscore
|
crypto
|
||||||
|
ssl
|
||||||
|
# qgif
|
||||||
|
# qjpeg
|
||||||
jpeg
|
jpeg
|
||||||
fontconfig
|
fontconfig
|
||||||
X11
|
X11
|
||||||
Xrender
|
Xrender
|
||||||
|
Xext
|
||||||
GL
|
GL
|
||||||
)
|
)
|
||||||
|
if (WORD_SIZE EQUAL 32)
|
||||||
|
set(WEBKIT_PLUGIN_LIBRARIES ${WEBKIT_PLUGIN_LIBRARIES} jscore)
|
||||||
|
endif (WORD_SIZE EQUAL 32)
|
||||||
endif (STANDALONE)
|
endif (STANDALONE)
|
||||||
endif (WINDOWS)
|
endif (WINDOWS)
|
||||||
|
|||||||
@@ -171,7 +171,8 @@ void stop_recording_backtraces(void)
|
|||||||
channel_ct gtk DDCN("GTK"); //!< This debug channel is used for output related to gtk.
|
channel_ct gtk DDCN("GTK"); //!< This debug channel is used for output related to gtk.
|
||||||
channel_ct sdl DDCN("SDL"); //!< This debug channel is used for output related to sdl locking.
|
channel_ct sdl DDCN("SDL"); //!< This debug channel is used for output related to sdl locking.
|
||||||
channel_ct backtrace DDCN("BACKTRACE"); //!< This debug channel is used for backtraces.
|
channel_ct backtrace DDCN("BACKTRACE"); //!< This debug channel is used for backtraces.
|
||||||
channel_ct statemachine DDCN("STATEMACHINE"); //!< This debug channel is used class AIStateMachine.
|
channel_ct statemachine DDCN("STATEMACHINE"); //!< This debug channel is used for output related to class AIStateMachine.
|
||||||
|
channel_ct caps DDCN("CAPS"); //!< This debug channel is used for output related to Capabilities.
|
||||||
|
|
||||||
} // namespace dc
|
} // namespace dc
|
||||||
} // namespace DEBUGCHANNELS
|
} // namespace DEBUGCHANNELS
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ extern CWD_API channel_ct gtk;
|
|||||||
extern CWD_API channel_ct sdl;
|
extern CWD_API channel_ct sdl;
|
||||||
extern CWD_API channel_ct backtrace;
|
extern CWD_API channel_ct backtrace;
|
||||||
extern CWD_API channel_ct statemachine;
|
extern CWD_API channel_ct statemachine;
|
||||||
|
extern CWD_API channel_ct caps;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -775,9 +775,8 @@ Options:
|
|||||||
-p | --project=NAME set the root project name. (Doesn't effect makefiles)
|
-p | --project=NAME set the root project name. (Doesn't effect makefiles)
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
build configure and build default target
|
build configure and build default target
|
||||||
clean delete all build directories, does not affect sources
|
clean delete all build directories, does not affect sources
|
||||||
configure configure project by running cmake (default command if none given)
|
|
||||||
configure configure project by running cmake (default if none given)
|
configure configure project by running cmake (default if none given)
|
||||||
printbuilddirs print the build directory that will be used
|
printbuilddirs print the build directory that will be used
|
||||||
|
|
||||||
|
|||||||
@@ -168,7 +168,10 @@ ARGUMENTS=[
|
|||||||
dict(name='version',
|
dict(name='version',
|
||||||
description="""This specifies the version of Second Life that is
|
description="""This specifies the version of Second Life that is
|
||||||
being packaged up.""",
|
being packaged up.""",
|
||||||
default=get_default_version)
|
default=get_default_version),
|
||||||
|
dict(name='extra_libraries',
|
||||||
|
description="""List of extra libraries to include in package""",
|
||||||
|
default=None)
|
||||||
]
|
]
|
||||||
|
|
||||||
def usage(srctree=""):
|
def usage(srctree=""):
|
||||||
|
|||||||
@@ -1,105 +1,84 @@
|
|||||||
# Main CMakeLists.txt to build the OpenJPEG project using CMake (www.cmake.org)
|
# -*- cmake -*-
|
||||||
# Written by Mathieu Malaterre
|
|
||||||
|
|
||||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
|
project(openjpeg)
|
||||||
|
|
||||||
IF(COMMAND CMAKE_POLICY)
|
include(00-Common)
|
||||||
CMAKE_POLICY(SET CMP0003 NEW)
|
|
||||||
ENDIF(COMMAND CMAKE_POLICY)
|
|
||||||
|
|
||||||
PROJECT(openjpeg)
|
|
||||||
|
|
||||||
# Do full dependency headers.
|
|
||||||
INCLUDE_REGULAR_EXPRESSION("^.*$")
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
# OPENJPEG version number, useful for packaging and doxygen doc:
|
# OPENJPEG version number, useful for packaging and doxygen doc:
|
||||||
SET(OPENJPEG_VERSION_MAJOR 1)
|
set(OPENJPEG_VERSION_MAJOR 1)
|
||||||
SET(OPENJPEG_VERSION_MINOR 4)
|
set(OPENJPEG_VERSION_MINOR 4)
|
||||||
SET(OPENJPEG_VERSION_BUILD 0)
|
set(OPENJPEG_VERSION_BUILD 0)
|
||||||
SET(OPENJPEG_VERSION
|
set(OPENJPEG_VERSION
|
||||||
"${OPENJPEG_VERSION_MAJOR}.${OPENJPEG_VERSION_MINOR}.${OPENJPEG_VERSION_BUILD}")
|
"${OPENJPEG_VERSION_MAJOR}.${OPENJPEG_VERSION_MINOR}.${OPENJPEG_VERSION_BUILD}")
|
||||||
|
|
||||||
|
set(openjpeg_SOURCE_FILES
|
||||||
|
bio.c
|
||||||
|
cio.c
|
||||||
|
dwt.c
|
||||||
|
event.c
|
||||||
|
image.c
|
||||||
|
j2k.c
|
||||||
|
j2k_lib.c
|
||||||
|
jp2.c
|
||||||
|
jpt.c
|
||||||
|
mct.c
|
||||||
|
mqc.c
|
||||||
|
openjpeg.c
|
||||||
|
pi.c
|
||||||
|
raw.c
|
||||||
|
t1.c
|
||||||
|
t2.c
|
||||||
|
tcd.c
|
||||||
|
tgt.c
|
||||||
|
)
|
||||||
|
|
||||||
|
set(openjpeg_HEADER_FILES
|
||||||
|
bio.h
|
||||||
|
cio.h
|
||||||
|
dwt.h
|
||||||
|
event.h
|
||||||
|
fix.h
|
||||||
|
image.h
|
||||||
|
int.h
|
||||||
|
j2k.h
|
||||||
|
j2k_lib.h
|
||||||
|
jp2.h
|
||||||
|
jpt.h
|
||||||
|
mct.h
|
||||||
|
mqc.h
|
||||||
|
openjpeg.h
|
||||||
|
opj_includes.h
|
||||||
|
opj_malloc.h
|
||||||
|
pi.h
|
||||||
|
raw.h
|
||||||
|
t1.h
|
||||||
|
t1_luts.h
|
||||||
|
t2.h
|
||||||
|
tcd.h
|
||||||
|
tgt.h
|
||||||
|
)
|
||||||
|
|
||||||
|
IF(WINDOWS)
|
||||||
|
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||||
|
add_definitions(-DOPJ_STATIC)
|
||||||
|
ENDIF(WINDOWS)
|
||||||
|
|
||||||
|
|
||||||
|
set_source_files_properties(${openjpeg_HEADER_FILES}
|
||||||
|
PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||||
|
|
||||||
|
list(APPEND openjpeg_SOURCE_FILES ${openjpeg_HEADER_FILES})
|
||||||
|
|
||||||
|
add_library (openjpeg ${openjpeg_SOURCE_FILES})
|
||||||
|
|
||||||
# This setting of SOVERSION assumes that any API change
|
# This setting of SOVERSION assumes that any API change
|
||||||
# will increment either the minor or major version number of openjpeg
|
# will increment either the minor or major version number of openjpeg
|
||||||
SET(OPENJPEG_LIBRARY_PROPERTIES
|
set(OPENJPEG_LIBRARY_PROPERTIES
|
||||||
VERSION "${OPENJPEG_VERSION_MAJOR}.${OPENJPEG_VERSION_MINOR}.${OPENJPEG_VERSION_BUILD}"
|
VERSION "${OPENJPEG_VERSION_MAJOR}.${OPENJPEG_VERSION_MINOR}.${OPENJPEG_VERSION_BUILD}"
|
||||||
SOVERSION "${OPENJPEG_VERSION_MAJOR}.${OPENJPEG_VERSION_MINOR}"
|
SOVERSION "${OPENJPEG_VERSION_MAJOR}.${OPENJPEG_VERSION_MINOR}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# On Visual Studio 8 MS deprecated C. This removes all 1.276E1265 security
|
set_target_properties(openjpeg PROPERTIES
|
||||||
# warnings
|
|
||||||
IF(WIN32)
|
|
||||||
IF(NOT BORLAND)
|
|
||||||
IF(NOT CYGWIN)
|
|
||||||
IF(NOT MINGW)
|
|
||||||
IF(NOT ITK_ENABLE_VISUAL_STUDIO_DEPRECATED_C_WARNINGS)
|
|
||||||
ADD_DEFINITIONS(
|
|
||||||
-D_CRT_FAR_MAPPINGS_NO_DEPRECATE
|
|
||||||
-D_CRT_IS_WCTYPE_NO_DEPRECATE
|
|
||||||
-D_CRT_MANAGED_FP_NO_DEPRECATE
|
|
||||||
-D_CRT_NONSTDC_NO_DEPRECATE
|
|
||||||
-D_CRT_SECURE_NO_DEPRECATE
|
|
||||||
-D_CRT_SECURE_NO_DEPRECATE_GLOBALS
|
|
||||||
-D_CRT_SETERRORMODE_BEEP_SLEEP_NO_DEPRECATE
|
|
||||||
-D_CRT_TIME_FUNCTIONS_NO_DEPRECATE
|
|
||||||
-D_CRT_VCCLRIT_NO_DEPRECATE
|
|
||||||
-D_SCL_SECURE_NO_DEPRECATE
|
|
||||||
)
|
|
||||||
ENDIF(NOT ITK_ENABLE_VISUAL_STUDIO_DEPRECATED_C_WARNINGS)
|
|
||||||
ENDIF(NOT MINGW)
|
|
||||||
ENDIF(NOT CYGWIN)
|
|
||||||
ENDIF(NOT BORLAND)
|
|
||||||
ENDIF(WIN32)
|
|
||||||
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Test for some required system information.
|
|
||||||
# INCLUDE (${CMAKE_ROOT}/Modules/CMakeBackwardCompatibilityC.cmake)
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Compiler specific flags:
|
|
||||||
IF(CMAKE_COMPILER_IS_GNUCC)
|
|
||||||
# For all builds, make sure openjpeg is std99 compliant:
|
|
||||||
# SET(CMAKE_C_FLAGS "-Wall -std=c99 ${CMAKE_C_FLAGS}") # FIXME: this setting prevented us from setting a coverage build.
|
|
||||||
# Do not use ffast-math for all build, it would produce incorrect results, only set for release:
|
|
||||||
SET(CMAKE_C_FLAGS_RELEASE "-ffast-math ${CMAKE_C_FLAGS_RELEASE}")
|
|
||||||
ENDIF(CMAKE_COMPILER_IS_GNUCC)
|
|
||||||
|
|
||||||
# Defines the source code for the library
|
|
||||||
SET(OPENJPEG_SRCS
|
|
||||||
bio.c
|
|
||||||
cio.c
|
|
||||||
dwt.c
|
|
||||||
event.c
|
|
||||||
image.c
|
|
||||||
j2k.c
|
|
||||||
j2k_lib.c
|
|
||||||
jp2.c
|
|
||||||
jpt.c
|
|
||||||
mct.c
|
|
||||||
mqc.c
|
|
||||||
openjpeg.c
|
|
||||||
pi.c
|
|
||||||
raw.c
|
|
||||||
t1.c
|
|
||||||
t2.c
|
|
||||||
tcd.c
|
|
||||||
tgt.c
|
|
||||||
)
|
|
||||||
|
|
||||||
# Pass proper definition to preprocessor to generate shared lib
|
|
||||||
IF(WIN32)
|
|
||||||
IF(BUILD_SHARED_LIBS)
|
|
||||||
ADD_DEFINITIONS(-DOPJ_EXPORTS)
|
|
||||||
ELSE(BUILD_SHARED_LIBS)
|
|
||||||
ADD_DEFINITIONS(-DOPJ_STATIC)
|
|
||||||
ENDIF(BUILD_SHARED_LIBS)
|
|
||||||
ENDIF(WIN32)
|
|
||||||
|
|
||||||
# Create the library
|
|
||||||
ADD_LIBRARY(openjpeg ${OPENJPEG_SRCS})
|
|
||||||
SET_TARGET_PROPERTIES(openjpeg PROPERTIES
|
|
||||||
${OPENJPEG_LIBRARY_PROPERTIES})
|
${OPENJPEG_LIBRARY_PROPERTIES})
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -527,7 +527,7 @@ static void dwt_decode_tile(opj_tcd_tilecomp_t* tilec, int numres, DWT1DFN dwt_1
|
|||||||
|
|
||||||
int w = tilec->x1 - tilec->x0;
|
int w = tilec->x1 - tilec->x0;
|
||||||
|
|
||||||
h.mem = opj_aligned_malloc(dwt_decode_max_resolution(tr, numres) * sizeof(int));
|
h.mem = (int *)opj_aligned_malloc(dwt_decode_max_resolution(tr, numres) * sizeof(int));
|
||||||
v.mem = h.mem;
|
v.mem = h.mem;
|
||||||
|
|
||||||
while( --numres) {
|
while( --numres) {
|
||||||
@@ -570,7 +570,7 @@ static void v4dwt_interleave_h(v4dwt_t* restrict w, float* restrict a, int x, in
|
|||||||
int count = w->sn;
|
int count = w->sn;
|
||||||
int i, k;
|
int i, k;
|
||||||
for(k = 0; k < 2; ++k){
|
for(k = 0; k < 2; ++k){
|
||||||
if (count + 3 * x < size && ((int) a & 0x0f) == 0 && ((int) bi & 0x0f) == 0 && (x & 0x0f) == 0) {
|
if (count + 3 * x < size && ((long) a & 0x0f) == 0 && ((long) bi & 0x0f) == 0 && (x & 0x0f) == 0) {
|
||||||
/* Fast code path */
|
/* Fast code path */
|
||||||
for(i = 0; i < count; ++i){
|
for(i = 0; i < count; ++i){
|
||||||
int j = i;
|
int j = i;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2005, Herv<EFBFBD> Drolon, FreeImage Team
|
* Copyright (c) 2005, Hervé Drolon, FreeImage Team
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@@ -26,41 +26,6 @@
|
|||||||
|
|
||||||
#include "opj_includes.h"
|
#include "opj_includes.h"
|
||||||
|
|
||||||
/* ==========================================================
|
|
||||||
Utility functions
|
|
||||||
==========================================================*/
|
|
||||||
|
|
||||||
#if !defined(_MSC_VER) && !defined(__MINGW32__)
|
|
||||||
static char*
|
|
||||||
i2a(unsigned i, char *a, unsigned r) {
|
|
||||||
if (i/r > 0) a = i2a(i/r,a,r);
|
|
||||||
*a = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i%r];
|
|
||||||
return a+1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Transforms integer i into an ascii string and stores the result in a;
|
|
||||||
string is encoded in the base indicated by r.
|
|
||||||
@param i Number to be converted
|
|
||||||
@param a String result
|
|
||||||
@param r Base of value; must be in the range 2 - 36
|
|
||||||
@return Returns a
|
|
||||||
*/
|
|
||||||
static char *
|
|
||||||
_itoa(int i, char *a, int r) {
|
|
||||||
r = ((r < 2) || (r > 36)) ? 10 : r;
|
|
||||||
if(i < 0) {
|
|
||||||
*a = '-';
|
|
||||||
*i2a(-i, a+1, r) = 0;
|
|
||||||
}
|
|
||||||
else *i2a(i, a, r) = 0;
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* !WIN32 */
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
opj_event_mgr_t* OPJ_CALLCONV opj_set_event_mgr(opj_common_ptr cinfo, opj_event_mgr_t *event_mgr, void *context) {
|
opj_event_mgr_t* OPJ_CALLCONV opj_set_event_mgr(opj_common_ptr cinfo, opj_event_mgr_t *event_mgr, void *context) {
|
||||||
if(cinfo) {
|
if(cinfo) {
|
||||||
opj_event_mgr_t *previous = cinfo->event_mgr;
|
opj_event_mgr_t *previous = cinfo->event_mgr;
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ static void j2k_read_unk(opj_j2k_t *j2k);
|
|||||||
/* ----------------------------------------------------------------------- */
|
/* ----------------------------------------------------------------------- */
|
||||||
typedef struct j2k_prog_order{
|
typedef struct j2k_prog_order{
|
||||||
OPJ_PROG_ORDER enum_prog;
|
OPJ_PROG_ORDER enum_prog;
|
||||||
char str_prog[4];
|
char str_prog[5];
|
||||||
}j2k_prog_order_t;
|
}j2k_prog_order_t;
|
||||||
|
|
||||||
j2k_prog_order_t j2k_prog_order_list[] = {
|
j2k_prog_order_t j2k_prog_order_list[] = {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ list(APPEND linux_crash_logger_SOURCE_FILES
|
|||||||
${linux_crash_logger_HEADER_FILES}
|
${linux_crash_logger_HEADER_FILES}
|
||||||
)
|
)
|
||||||
|
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lrt -lapr-1 -Wl,--as-needed")
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed")
|
||||||
|
|
||||||
add_executable(linux-crash-logger ${linux_crash_logger_SOURCE_FILES})
|
add_executable(linux-crash-logger ${linux_crash_logger_SOURCE_FILES})
|
||||||
|
|
||||||
|
|||||||
@@ -5,16 +5,30 @@ project(llaudio)
|
|||||||
include(00-Common)
|
include(00-Common)
|
||||||
include(Audio)
|
include(Audio)
|
||||||
include(LLAudio)
|
include(LLAudio)
|
||||||
include(FMOD)
|
if(FMODEX)
|
||||||
|
include(FMODEX)
|
||||||
|
if(FMODEX)
|
||||||
|
set(FMOD OFF)
|
||||||
|
endif(FMODEX)
|
||||||
|
endif(FMODEX)
|
||||||
|
if(NOT FMODEX)
|
||||||
|
include(FMOD)
|
||||||
|
endif(NOT FMODEX)
|
||||||
include(OPENAL)
|
include(OPENAL)
|
||||||
include(LLCommon)
|
include(LLCommon)
|
||||||
include(LLMath)
|
include(LLMath)
|
||||||
include(LLMessage)
|
include(LLMessage)
|
||||||
include(LLVFS)
|
include(LLVFS)
|
||||||
|
|
||||||
|
if(FMODEX)
|
||||||
|
include_directories(${FMODEX_INCLUDE_DIR})
|
||||||
|
endif(FMODEX)
|
||||||
|
if(FMOD)
|
||||||
|
include_directories(${FMOD_INCLUDE_DIR})
|
||||||
|
endif(FMOD)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
${LLAUDIO_INCLUDE_DIRS}
|
${LLAUDIO_INCLUDE_DIRS}
|
||||||
${FMOD_INCLUDE_DIR}
|
|
||||||
${LLCOMMON_INCLUDE_DIRS}
|
${LLCOMMON_INCLUDE_DIRS}
|
||||||
${LLMATH_INCLUDE_DIRS}
|
${LLMATH_INCLUDE_DIRS}
|
||||||
${LLMESSAGE_INCLUDE_DIRS}
|
${LLMESSAGE_INCLUDE_DIRS}
|
||||||
@@ -46,6 +60,19 @@ set(llaudio_HEADER_FILES
|
|||||||
llwindgen.h
|
llwindgen.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (FMODEX)
|
||||||
|
list(APPEND llaudio_SOURCE_FILES
|
||||||
|
llaudioengine_fmodex.cpp
|
||||||
|
lllistener_fmodex.cpp
|
||||||
|
llstreamingaudio_fmodex.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
list(APPEND llaudio_HEADER_FILES
|
||||||
|
llaudioengine_fmodex.h
|
||||||
|
lllistener_fmodex.h
|
||||||
|
llstreamingaudio_fmodex.h
|
||||||
|
)
|
||||||
|
endif (FMODEX)
|
||||||
if (FMOD)
|
if (FMOD)
|
||||||
list(APPEND llaudio_SOURCE_FILES
|
list(APPEND llaudio_SOURCE_FILES
|
||||||
llaudioengine_fmod.cpp
|
llaudioengine_fmod.cpp
|
||||||
|
|||||||
@@ -239,12 +239,15 @@ protected:
|
|||||||
// A list of all audio sources that are known to the viewer at this time.
|
// A list of all audio sources that are known to the viewer at this time.
|
||||||
// This is most likely a superset of the ones that we actually have audio
|
// This is most likely a superset of the ones that we actually have audio
|
||||||
// data for, or are playing back.
|
// data for, or are playing back.
|
||||||
|
public://Jay: IDGAF
|
||||||
typedef std::map<LLUUID, LLAudioSource *> source_map;
|
typedef std::map<LLUUID, LLAudioSource *> source_map;
|
||||||
|
protected:
|
||||||
typedef std::map<LLUUID, LLAudioData *> data_map;
|
typedef std::map<LLUUID, LLAudioData *> data_map;
|
||||||
|
|
||||||
|
public://Jay: IDGAF
|
||||||
source_map mAllSources;
|
source_map mAllSources;
|
||||||
|
protected:
|
||||||
data_map mAllData;
|
data_map mAllData;
|
||||||
|
|
||||||
LLAudioChannel *mChannels[MAX_CHANNELS];
|
LLAudioChannel *mChannels[MAX_CHANNELS];
|
||||||
|
|
||||||
// Buffers needs to change into a different data structure, as the number of buffers
|
// Buffers needs to change into a different data structure, as the number of buffers
|
||||||
@@ -351,6 +354,9 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
LLUUID mID; // The ID of the source is that of the object if it's attached to an object.
|
LLUUID mID; // The ID of the source is that of the object if it's attached to an object.
|
||||||
LLUUID mOwnerID; // owner of the object playing the sound
|
LLUUID mOwnerID; // owner of the object playing the sound
|
||||||
|
public:
|
||||||
|
const LLUUID &getOwnerID() { return mOwnerID; }
|
||||||
|
protected:
|
||||||
F32 mPriority;
|
F32 mPriority;
|
||||||
F32 mGain;
|
F32 mGain;
|
||||||
bool mSourceMuted;
|
bool mSourceMuted;
|
||||||
|
|||||||
782
indra/llaudio/llaudioengine_fmodex.cpp
Normal file
782
indra/llaudio/llaudioengine_fmodex.cpp
Normal file
@@ -0,0 +1,782 @@
|
|||||||
|
/**
|
||||||
|
* @file audioengine_FMODEX.cpp
|
||||||
|
* @brief Implementation of LLAudioEngine class abstracting the audio support as a FMOD 3D implementation
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2002&license=viewergpl$
|
||||||
|
*
|
||||||
|
* Copyright (c) 2002-2009, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||||
|
* to you under the terms of the GNU General Public License, version 2.0
|
||||||
|
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||||
|
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||||
|
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||||
|
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||||
|
*
|
||||||
|
* There are special exceptions to the terms and conditions of the GPL as
|
||||||
|
* it is applied to this Source Code. View the full text of the exception
|
||||||
|
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||||
|
* online at
|
||||||
|
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||||
|
*
|
||||||
|
* By copying, modifying or distributing this software, you acknowledge
|
||||||
|
* that you have read and understood your obligations described above,
|
||||||
|
* and agree to abide by those obligations.
|
||||||
|
*
|
||||||
|
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||||
|
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||||
|
* COMPLETENESS OR PERFORMANCE.
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "linden_common.h"
|
||||||
|
|
||||||
|
#include "llstreamingaudio.h"
|
||||||
|
#include "llstreamingaudio_fmodex.h"
|
||||||
|
|
||||||
|
#include "llaudioengine_fmodex.h"
|
||||||
|
#include "lllistener_fmodex.h"
|
||||||
|
|
||||||
|
#include "llerror.h"
|
||||||
|
#include "llmath.h"
|
||||||
|
#include "llrand.h"
|
||||||
|
|
||||||
|
#include "fmod.hpp"
|
||||||
|
#include "fmod_errors.h"
|
||||||
|
#include "lldir.h"
|
||||||
|
#include "llapr.h"
|
||||||
|
|
||||||
|
#include "sound_ids.h"
|
||||||
|
|
||||||
|
#if LL_WINDOWS //Some ugly code to make missing fmodex.dll not cause a fatal error.
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include "windows.h"
|
||||||
|
#include <DelayImp.h>
|
||||||
|
#pragma comment(lib, "delayimp.lib")
|
||||||
|
|
||||||
|
bool attemptDelayLoad()
|
||||||
|
{
|
||||||
|
__try
|
||||||
|
{
|
||||||
|
if( FAILED( __HrLoadAllImportsForDll( "fmodex.dll" ) ) )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
__except( EXCEPTION_EXECUTE_HANDLER )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels);
|
||||||
|
|
||||||
|
LLAudioEngine_FMODEX::LLAudioEngine_FMODEX(bool enable_profiler)
|
||||||
|
{
|
||||||
|
mInited = false;
|
||||||
|
mWindGen = NULL;
|
||||||
|
mWindDSP = NULL;
|
||||||
|
mSystem = NULL;
|
||||||
|
mEnableProfiler = enable_profiler;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LLAudioEngine_FMODEX::~LLAudioEngine_FMODEX()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline bool Check_FMOD_Error(FMOD_RESULT result, const char *string)
|
||||||
|
{
|
||||||
|
if(result == FMOD_OK)
|
||||||
|
return false;
|
||||||
|
llwarns << string << " Error: " << FMOD_ErrorString(result) << llendl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LLAudioEngine_FMODEX::init(const S32 num_channels, void* userdata)
|
||||||
|
{
|
||||||
|
|
||||||
|
#if LL_WINDOWS
|
||||||
|
if(!attemptDelayLoad())
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
U32 version;
|
||||||
|
FMOD_RESULT result;
|
||||||
|
int numdrivers;
|
||||||
|
FMOD_SPEAKERMODE speakermode;
|
||||||
|
FMOD_CAPS caps;
|
||||||
|
char name[256];
|
||||||
|
|
||||||
|
LL_DEBUGS("AppInit") << "LLAudioEngine_FMODEX::init() initializing FMOD" << LL_ENDL;
|
||||||
|
|
||||||
|
result = FMOD::System_Create(&mSystem);
|
||||||
|
if(Check_FMOD_Error(result, "FMOD::System_Create"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//will call LLAudioEngine_FMODEX::allocateListener, which needs a valid mSystem pointer.
|
||||||
|
LLAudioEngine::init(num_channels, userdata);
|
||||||
|
|
||||||
|
result = mSystem->getVersion(&version);
|
||||||
|
Check_FMOD_Error(result, "FMOD::System::getVersion");
|
||||||
|
|
||||||
|
if (version < FMOD_VERSION)
|
||||||
|
{
|
||||||
|
LL_WARNS("AppInit") << "Error : You are using the wrong FMOD version (" << version
|
||||||
|
<< ")! You should be using FMOD " << FMOD_VERSION << LL_ENDL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LL_WINDOWS
|
||||||
|
//Is this block applicable to linux?
|
||||||
|
{
|
||||||
|
result = mSystem->getNumDrivers(&numdrivers);
|
||||||
|
Check_FMOD_Error(result, "FMOD::System::getNumDrivers");
|
||||||
|
if (numdrivers == 0)
|
||||||
|
{
|
||||||
|
result = mSystem->setOutput(FMOD_OUTPUTTYPE_NOSOUND);
|
||||||
|
Check_FMOD_Error(result, "FMOD::System::setOutput");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = mSystem->getDriverCaps(0, &caps, 0, &speakermode);
|
||||||
|
Check_FMOD_Error(result,"FMOD::System::getDriverCaps");
|
||||||
|
/*
|
||||||
|
Set the user selected speaker mode.
|
||||||
|
*/
|
||||||
|
result = mSystem->setSpeakerMode(speakermode);
|
||||||
|
Check_FMOD_Error(result, "FMOD::System::setSpeakerMode");
|
||||||
|
if (caps & FMOD_CAPS_HARDWARE_EMULATED)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
The user has the 'Acceleration' slider set to off! This is really bad
|
||||||
|
for latency! You might want to warn the user about this.
|
||||||
|
*/
|
||||||
|
result = mSystem->setDSPBufferSize(1024, 10);
|
||||||
|
Check_FMOD_Error(result, "FMOD::System::setDSPBufferSize");
|
||||||
|
}
|
||||||
|
result = mSystem->getDriverInfo(0, name, 256, 0);
|
||||||
|
Check_FMOD_Error(result, "FMOD::System::getDriverInfo");
|
||||||
|
|
||||||
|
if (strstr(name, "SigmaTel"))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Sigmatel sound devices crackle for some reason if the format is PCM 16bit.
|
||||||
|
PCM floating point output seems to solve it.
|
||||||
|
*/
|
||||||
|
result = mSystem->setSoftwareFormat(48000, FMOD_SOUND_FORMAT_PCMFLOAT, 0,0, FMOD_DSP_RESAMPLER_LINEAR);
|
||||||
|
Check_FMOD_Error(result,"FMOD::System::setSoftwareFormat");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif //LL_WINDOWS
|
||||||
|
|
||||||
|
// In this case, all sounds, PLUS wind and stream will be software.
|
||||||
|
result = mSystem->setSoftwareChannels(num_channels+2);
|
||||||
|
Check_FMOD_Error(result,"FMOD::System::setSoftwareChannels");
|
||||||
|
|
||||||
|
U32 fmod_flags = FMOD_INIT_NORMAL;
|
||||||
|
if(mEnableProfiler)
|
||||||
|
fmod_flags |= FMOD_INIT_ENABLE_PROFILE;
|
||||||
|
|
||||||
|
#if LL_LINUX
|
||||||
|
// If we don't set an output method, Linux FMOD always
|
||||||
|
// decides on OSS and fails otherwise. So we'll manually
|
||||||
|
// try ESD, then OSS, then ALSA.
|
||||||
|
// Why this order? See SL-13250, but in short, OSS emulated
|
||||||
|
// on top of ALSA is ironically more reliable than raw ALSA.
|
||||||
|
// Ack, and ESD has more reliable failure modes - but has worse
|
||||||
|
// latency - than all of them, so wins for now.
|
||||||
|
bool audio_ok = false;
|
||||||
|
|
||||||
|
if (!audio_ok)
|
||||||
|
{
|
||||||
|
if (NULL == getenv("LL_BAD_FMODEX_ESD")) /*Flawfinder: ignore*/
|
||||||
|
{
|
||||||
|
LL_DEBUGS("AppInit") << "Trying ESD audio output..." << LL_ENDL;
|
||||||
|
if(mSystem->SetOutput(FMOD_OUTPUTTYPE_ESD) == FMOD_OK &&
|
||||||
|
(result = mSystem->init(num_channels, fmod_flags, 0)) == FMOD_OK)
|
||||||
|
{
|
||||||
|
LL_DEBUGS("AppInit") << "ESD audio output initialized OKAY" << LL_ENDL;
|
||||||
|
audio_ok = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Check_FMOD_Error(result, "ESD audio output FAILED to initialize");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LL_DEBUGS("AppInit") << "ESD audio output SKIPPED" << LL_ENDL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!audio_ok)
|
||||||
|
{
|
||||||
|
if (NULL == getenv("LL_BAD_FMODEX_OSS")) /*Flawfinder: ignore*/
|
||||||
|
{
|
||||||
|
LL_DEBUGS("AppInit") << "Trying OSS audio output..." << LL_ENDL;
|
||||||
|
if(mSystem->SetOutput(FMOD_OUTPUTTYPE_OSS) == FMOD_OK &&
|
||||||
|
(result = mSystem->init(num_channels, fmod_flags, 0)) == FMOD_OK)
|
||||||
|
{
|
||||||
|
LL_DEBUGS("AppInit") << "OSS audio output initialized OKAY" << LL_ENDL;
|
||||||
|
audio_ok = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Check_FMOD_Error(result, "OSS audio output FAILED to initialize" << LL_ENDL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LL_DEBUGS("AppInit") << "OSS audio output SKIPPED" << LL_ENDL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!audio_ok)
|
||||||
|
{
|
||||||
|
if (NULL == getenv("LL_BAD_FMODEX_ALSA")) /*Flawfinder: ignore*/
|
||||||
|
{
|
||||||
|
LL_DEBUGS("AppInit") << "Trying ALSA audio output..." << LL_ENDL;
|
||||||
|
if(mSystem->SetOutput(FMOD_OUTPUTTYPE_ALSA) &&
|
||||||
|
(result = mSystem->init(num_channels, fmod_flags, 0)) == FMOD_OK)
|
||||||
|
{
|
||||||
|
LL_DEBUGS("AppInit") << "ALSA audio output initialized OKAY" << LL_ENDL;
|
||||||
|
audio_ok = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Check_FMOD_Error(result, "ALSA audio output FAILED to initialize");
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
LL_DEBUGS("AppInit") << "OSS audio output SKIPPED" << LL_ENDL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!audio_ok)
|
||||||
|
{
|
||||||
|
LL_WARNS("AppInit") << "Overall audio init failure." << LL_ENDL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// On Linux, FMOD causes a SIGPIPE for some netstream error
|
||||||
|
// conditions (an FMOD bug); ignore SIGPIPE so it doesn't crash us.
|
||||||
|
// NOW FIXED in FMOD 3.x since 2006-10-01.
|
||||||
|
//signal(SIGPIPE, SIG_IGN);
|
||||||
|
|
||||||
|
// We're interested in logging which output method we
|
||||||
|
// ended up with, for QA purposes.
|
||||||
|
FMOD_OUTPUTTYPE output_type;
|
||||||
|
mSystem->getOutput(output_type);
|
||||||
|
switch (output_type)
|
||||||
|
{
|
||||||
|
case FSOUND_OUTPUT_NOSOUND:
|
||||||
|
LL_DEBUGS("AppInit") << "Audio output: NoSound" << LL_ENDL; break;
|
||||||
|
case FSOUND_OUTPUT_OSS:
|
||||||
|
LL_DEBUGS("AppInit") << "Audio output: OSS" << LL_ENDL; break;
|
||||||
|
case FSOUND_OUTPUT_ESD:
|
||||||
|
LL_DEBUGS("AppInit") << "Audio output: ESD" << LL_ENDL; break;
|
||||||
|
case FSOUND_OUTPUT_ALSA:
|
||||||
|
LL_DEBUGS("AppInit") << "Audio output: ALSA" << LL_ENDL; break;
|
||||||
|
default:
|
||||||
|
LL_INFOS("AppInit") << "Audio output: Unknown!" << LL_ENDL; break;
|
||||||
|
};
|
||||||
|
#else // LL_LINUX
|
||||||
|
|
||||||
|
// initialize the FMOD engine
|
||||||
|
result = mSystem->init( num_channels + 2, fmod_flags, 0);
|
||||||
|
if (result == FMOD_ERR_OUTPUT_CREATEBUFFER)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Ok, the speaker mode selected isn't supported by this soundcard. Switch it
|
||||||
|
back to stereo...
|
||||||
|
*/
|
||||||
|
result = mSystem->setSpeakerMode(FMOD_SPEAKERMODE_STEREO);
|
||||||
|
Check_FMOD_Error(result,"Error falling back to stereo mode");
|
||||||
|
/*
|
||||||
|
... and re-init.
|
||||||
|
*/
|
||||||
|
result = mSystem->init(100, FMOD_INIT_NORMAL, 0);
|
||||||
|
}
|
||||||
|
if(Check_FMOD_Error(result, "Error initializing FMOD"))
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// set up our favourite FMOD-native streaming audio implementation if none has already been added
|
||||||
|
if (!getStreamingAudioImpl()) // no existing implementation added
|
||||||
|
setStreamingAudioImpl(new LLStreamingAudio_FMODEX(mSystem));
|
||||||
|
|
||||||
|
LL_DEBUGS("AppInit") << "LLAudioEngine_FMODEX::init() FMOD initialized correctly" << LL_ENDL;
|
||||||
|
|
||||||
|
mInited = true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string LLAudioEngine_FMODEX::getDriverName(bool verbose)
|
||||||
|
{
|
||||||
|
llassert_always(mSystem);
|
||||||
|
if (verbose)
|
||||||
|
{
|
||||||
|
U32 version;
|
||||||
|
if(!Check_FMOD_Error(mSystem->getVersion(&version), "FMOD::System::getVersion"))
|
||||||
|
{
|
||||||
|
return llformat("FMOD version %1x.%02x.%02x", version >> 16, version >> 8 & 0x000000FF, version & 0x000000FF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "FMOD";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LLAudioEngine_FMODEX::allocateListener(void)
|
||||||
|
{
|
||||||
|
mListenerp = (LLListener *) new LLListener_FMODEX(mSystem);
|
||||||
|
if (!mListenerp)
|
||||||
|
{
|
||||||
|
llwarns << "Listener creation failed" << llendl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LLAudioEngine_FMODEX::shutdown()
|
||||||
|
{
|
||||||
|
stopInternetStream();
|
||||||
|
|
||||||
|
LLAudioEngine::shutdown();
|
||||||
|
|
||||||
|
llinfos << "LLAudioEngine_FMODEX::shutdown() closing FMOD" << llendl;
|
||||||
|
mSystem->close();
|
||||||
|
mSystem->release();
|
||||||
|
llinfos << "LLAudioEngine_FMODEX::shutdown() done closing FMOD" << llendl;
|
||||||
|
|
||||||
|
delete mListenerp;
|
||||||
|
mListenerp = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LLAudioBuffer * LLAudioEngine_FMODEX::createBuffer()
|
||||||
|
{
|
||||||
|
return new LLAudioBufferFMODEX(mSystem);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LLAudioChannel * LLAudioEngine_FMODEX::createChannel()
|
||||||
|
{
|
||||||
|
return new LLAudioChannelFMODEX(mSystem);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LLAudioEngine_FMODEX::initWind()
|
||||||
|
{
|
||||||
|
mNextWindUpdate = 0.0;
|
||||||
|
|
||||||
|
if (!mWindDSP)
|
||||||
|
{
|
||||||
|
FMOD_DSP_DESCRIPTION dspdesc;
|
||||||
|
memset(&dspdesc, 0, sizeof(FMOD_DSP_DESCRIPTION)); //Set everything to zero
|
||||||
|
strncpy(dspdesc.name,"Wind Unit", sizeof(dspdesc.name)); //Set name to "Wind Unit"
|
||||||
|
dspdesc.read = &windCallback; //Assign callback.
|
||||||
|
if(Check_FMOD_Error(mSystem->createDSP(&dspdesc, &mWindDSP), "FMOD::createDSP"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(mWindGen)
|
||||||
|
delete mWindGen;
|
||||||
|
|
||||||
|
float frequency = 44100;
|
||||||
|
mWindDSP->getDefaults(&frequency,0,0,0);
|
||||||
|
mWindGen = new LLWindGen<MIXBUFFERFORMAT>((U32)frequency);
|
||||||
|
mWindDSP->setUserData((void*)mWindGen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mWindDSP)
|
||||||
|
{
|
||||||
|
mSystem->playDSP(FMOD_CHANNEL_FREE, mWindDSP, false, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LLAudioEngine_FMODEX::cleanupWind()
|
||||||
|
{
|
||||||
|
if (mWindDSP)
|
||||||
|
{
|
||||||
|
mWindDSP->remove();
|
||||||
|
mWindDSP->release();
|
||||||
|
mWindDSP = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete mWindGen;
|
||||||
|
mWindGen = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
void LLAudioEngine_FMODEX::updateWind(LLVector3 wind_vec, F32 camera_height_above_water)
|
||||||
|
{
|
||||||
|
LLVector3 wind_pos;
|
||||||
|
F64 pitch;
|
||||||
|
F64 center_freq;
|
||||||
|
|
||||||
|
if (!mEnableWind)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mWindUpdateTimer.checkExpirationAndReset(LL_WIND_UPDATE_INTERVAL))
|
||||||
|
{
|
||||||
|
|
||||||
|
// wind comes in as Linden coordinate (+X = forward, +Y = left, +Z = up)
|
||||||
|
// need to convert this to the conventional orientation DS3D and OpenAL use
|
||||||
|
// where +X = right, +Y = up, +Z = backwards
|
||||||
|
|
||||||
|
wind_vec.setVec(-wind_vec.mV[1], wind_vec.mV[2], -wind_vec.mV[0]);
|
||||||
|
|
||||||
|
// cerr << "Wind update" << endl;
|
||||||
|
|
||||||
|
pitch = 1.0 + mapWindVecToPitch(wind_vec);
|
||||||
|
center_freq = 80.0 * pow(pitch,2.5*(mapWindVecToGain(wind_vec)+1.0));
|
||||||
|
|
||||||
|
mWindGen->mTargetFreq = (F32)center_freq;
|
||||||
|
mWindGen->mTargetGain = (F32)mapWindVecToGain(wind_vec) * mMaxWindGain;
|
||||||
|
mWindGen->mTargetPanGainR = (F32)mapWindVecToPan(wind_vec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
void LLAudioEngine_FMODEX::setInternalGain(F32 gain)
|
||||||
|
{
|
||||||
|
if (!mInited)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gain = llclamp( gain, 0.0f, 1.0f );
|
||||||
|
|
||||||
|
FMOD::ChannelGroup *master_group;
|
||||||
|
mSystem->getMasterChannelGroup(&master_group);
|
||||||
|
|
||||||
|
master_group->setVolume(gain);
|
||||||
|
|
||||||
|
LLStreamingAudioInterface *saimpl = getStreamingAudioImpl();
|
||||||
|
if ( saimpl )
|
||||||
|
{
|
||||||
|
// fmod likes its streaming audio channel gain re-asserted after
|
||||||
|
// master volume change.
|
||||||
|
saimpl->setGain(saimpl->getGain());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// LLAudioChannelFMODEX implementation
|
||||||
|
//
|
||||||
|
|
||||||
|
LLAudioChannelFMODEX::LLAudioChannelFMODEX(FMOD::System *system) : LLAudioChannel(), mSystemp(system), mChannelp(NULL), mLastSamplePos(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LLAudioChannelFMODEX::~LLAudioChannelFMODEX()
|
||||||
|
{
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LLAudioChannelFMODEX::updateBuffer()
|
||||||
|
{
|
||||||
|
if (LLAudioChannel::updateBuffer())
|
||||||
|
{
|
||||||
|
// Base class update returned true, which means that we need to actually
|
||||||
|
// set up the channel for a different buffer.
|
||||||
|
|
||||||
|
LLAudioBufferFMODEX *bufferp = (LLAudioBufferFMODEX *)mCurrentSourcep->getCurrentBuffer();
|
||||||
|
|
||||||
|
// Grab the FMOD sample associated with the buffer
|
||||||
|
FMOD::Sound *soundp = bufferp->getSound();
|
||||||
|
if (!soundp)
|
||||||
|
{
|
||||||
|
// This is bad, there should ALWAYS be a sound associated with a legit
|
||||||
|
// buffer.
|
||||||
|
llerrs << "No FMOD sound!" << llendl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Actually play the sound. Start it off paused so we can do all the necessary
|
||||||
|
// setup.
|
||||||
|
if(!mChannelp)
|
||||||
|
{
|
||||||
|
FMOD_RESULT result = getSystem()->playSound(FMOD_CHANNEL_FREE, soundp, true, &mChannelp);
|
||||||
|
Check_FMOD_Error(result, "FMOD::System::playSound");
|
||||||
|
}
|
||||||
|
|
||||||
|
//llinfos << "Setting up channel " << std::hex << mChannelID << std::dec << llendl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a source for the channel, we need to update its gain.
|
||||||
|
if (mCurrentSourcep)
|
||||||
|
{
|
||||||
|
// SJB: warnings can spam and hurt framerate, disabling
|
||||||
|
FMOD_RESULT result;
|
||||||
|
|
||||||
|
result = mChannelp->setVolume(getSecondaryGain() * mCurrentSourcep->getGain());
|
||||||
|
//Check_FMOD_Error(result, "FMOD::Channel::setVolume");
|
||||||
|
|
||||||
|
result = mChannelp->setMode(mCurrentSourcep->isLoop() ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF);
|
||||||
|
/*if(Check_FMOD_Error(result, "FMOD::Channel::setMode"))
|
||||||
|
{
|
||||||
|
S32 index;
|
||||||
|
mChannelp->getIndex(&index);
|
||||||
|
llwarns << "Channel " << index << "Source ID: " << mCurrentSourcep->getID()
|
||||||
|
<< " at " << mCurrentSourcep->getPositionGlobal() << llendl;
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LLAudioChannelFMODEX::update3DPosition()
|
||||||
|
{
|
||||||
|
if (!mChannelp)
|
||||||
|
{
|
||||||
|
// We're not actually a live channel (i.e., we're not playing back anything)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LLAudioBufferFMODEX *bufferp = (LLAudioBufferFMODEX *)mCurrentBufferp;
|
||||||
|
if (!bufferp)
|
||||||
|
{
|
||||||
|
// We don't have a buffer associated with us (should really have been picked up
|
||||||
|
// by the above if.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mCurrentSourcep->isAmbient())
|
||||||
|
{
|
||||||
|
// Ambient sound, don't need to do any positional updates.
|
||||||
|
set3DMode(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Localized sound. Update the position and velocity of the sound.
|
||||||
|
set3DMode(true);
|
||||||
|
|
||||||
|
LLVector3 float_pos;
|
||||||
|
float_pos.setVec(mCurrentSourcep->getPositionGlobal());
|
||||||
|
FMOD_RESULT result = mChannelp->set3DAttributes((FMOD_VECTOR*)float_pos.mV, (FMOD_VECTOR*)mCurrentSourcep->getVelocity().mV);
|
||||||
|
Check_FMOD_Error(result, "FMOD::Channel::set3DAttributes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LLAudioChannelFMODEX::updateLoop()
|
||||||
|
{
|
||||||
|
if (!mChannelp)
|
||||||
|
{
|
||||||
|
// May want to clear up the loop/sample counters.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Hack: We keep track of whether we looped or not by seeing when the
|
||||||
|
// sample position looks like it's going backwards. Not reliable; may
|
||||||
|
// yield false negatives.
|
||||||
|
//
|
||||||
|
U32 cur_pos;
|
||||||
|
mChannelp->getPosition(&cur_pos,FMOD_TIMEUNIT_PCMBYTES);
|
||||||
|
|
||||||
|
if (cur_pos < (U32)mLastSamplePos)
|
||||||
|
{
|
||||||
|
mLoopedThisFrame = true;
|
||||||
|
}
|
||||||
|
mLastSamplePos = cur_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LLAudioChannelFMODEX::cleanup()
|
||||||
|
{
|
||||||
|
if (!mChannelp)
|
||||||
|
{
|
||||||
|
//llinfos << "Aborting cleanup with no channel handle." << llendl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//llinfos << "Cleaning up channel: " << mChannelID << llendl;
|
||||||
|
Check_FMOD_Error(mChannelp->stop(),"FMOD::Channel::stop");
|
||||||
|
|
||||||
|
mCurrentBufferp = NULL;
|
||||||
|
mChannelp = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LLAudioChannelFMODEX::play()
|
||||||
|
{
|
||||||
|
if (!mChannelp)
|
||||||
|
{
|
||||||
|
llwarns << "Playing without a channel handle, aborting" << llendl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Check_FMOD_Error(mChannelp->setPaused(false), "FMOD::Channel::pause");
|
||||||
|
|
||||||
|
getSource()->setPlayedOnce(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LLAudioChannelFMODEX::playSynced(LLAudioChannel *channelp)
|
||||||
|
{
|
||||||
|
LLAudioChannelFMODEX *fmod_channelp = (LLAudioChannelFMODEX*)channelp;
|
||||||
|
if (!(fmod_channelp->mChannelp && mChannelp))
|
||||||
|
{
|
||||||
|
// Don't have channels allocated to both the master and the slave
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
U32 cur_pos;
|
||||||
|
if(Check_FMOD_Error(mChannelp->getPosition(&cur_pos,FMOD_TIMEUNIT_PCMBYTES), "Unable to retrieve current position"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
cur_pos %= mCurrentBufferp->getLength();
|
||||||
|
|
||||||
|
// Try to match the position of our sync master
|
||||||
|
Check_FMOD_Error(mChannelp->setPosition(cur_pos,FMOD_TIMEUNIT_PCMBYTES),"Unable to set current position");
|
||||||
|
|
||||||
|
// Start us playing
|
||||||
|
play();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool LLAudioChannelFMODEX::isPlaying()
|
||||||
|
{
|
||||||
|
if (!mChannelp)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool paused, playing;
|
||||||
|
mChannelp->getPaused(&paused);
|
||||||
|
mChannelp->isPlaying(&playing);
|
||||||
|
return !paused && playing;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// LLAudioChannelFMODEX implementation
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
LLAudioBufferFMODEX::LLAudioBufferFMODEX(FMOD::System *system) : mSystemp(system), mSoundp(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LLAudioBufferFMODEX::~LLAudioBufferFMODEX()
|
||||||
|
{
|
||||||
|
if(mSoundp)
|
||||||
|
{
|
||||||
|
mSoundp->release();
|
||||||
|
mSoundp = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool LLAudioBufferFMODEX::loadWAV(const std::string& filename)
|
||||||
|
{
|
||||||
|
// Try to open a wav file from disk. This will eventually go away, as we don't
|
||||||
|
// really want to block doing this.
|
||||||
|
if (filename.empty())
|
||||||
|
{
|
||||||
|
// invalid filename, abort.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!LLAPRFile::isExist(filename, LL_APR_RPB))
|
||||||
|
{
|
||||||
|
// File not found, abort.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mSoundp)
|
||||||
|
{
|
||||||
|
// If there's already something loaded in this buffer, clean it up.
|
||||||
|
mSoundp->release();
|
||||||
|
mSoundp = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMOD_MODE base_mode = FMOD_LOOP_NORMAL | FMOD_SOFTWARE;
|
||||||
|
FMOD_CREATESOUNDEXINFO exinfo;
|
||||||
|
memset(&exinfo,0,sizeof(exinfo));
|
||||||
|
exinfo.cbsize = sizeof(exinfo);
|
||||||
|
exinfo.suggestedsoundtype = FMOD_SOUND_TYPE_WAV; //Hint to speed up loading.
|
||||||
|
// Load up the wav file into an fmod sample
|
||||||
|
#if LL_WINDOWS
|
||||||
|
FMOD_RESULT result = getSystem()->createSound((const char*)utf8str_to_utf16str(filename).c_str(), base_mode | FMOD_UNICODE, &exinfo, &mSoundp);
|
||||||
|
#else
|
||||||
|
FMOD_RESULT result = getSystem()->createSound(filename.c_str(), base_mode, &exinfo, &mSoundp);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (result != FMOD_OK)
|
||||||
|
{
|
||||||
|
// We failed to load the file for some reason.
|
||||||
|
llwarns << "Could not load data '" << filename << "': " << FMOD_ErrorString(result) << llendl;
|
||||||
|
|
||||||
|
//
|
||||||
|
// If we EVER want to load wav files provided by end users, we need
|
||||||
|
// to rethink this!
|
||||||
|
//
|
||||||
|
// file is probably corrupt - remove it.
|
||||||
|
LLFile::remove(filename);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Everything went well, return true
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
U32 LLAudioBufferFMODEX::getLength()
|
||||||
|
{
|
||||||
|
if (!mSoundp)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
U32 length;
|
||||||
|
mSoundp->getLength(&length, FMOD_TIMEUNIT_PCMBYTES);
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LLAudioChannelFMODEX::set3DMode(bool use3d)
|
||||||
|
{
|
||||||
|
FMOD_MODE current_mode;
|
||||||
|
if(mChannelp->getMode(¤t_mode) != FMOD_OK)
|
||||||
|
return;
|
||||||
|
FMOD_MODE new_mode = current_mode;
|
||||||
|
new_mode &= ~(use3d ? FMOD_2D : FMOD_3D);
|
||||||
|
new_mode |= use3d ? FMOD_3D : FMOD_2D;
|
||||||
|
|
||||||
|
if(current_mode != new_mode)
|
||||||
|
{
|
||||||
|
mChannelp->setMode(new_mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *originalbuffer, float *newbuffer, unsigned int length, int inchannels, int outchannels)
|
||||||
|
{
|
||||||
|
// originalbuffer = fmod's original mixbuffer.
|
||||||
|
// newbuffer = the buffer passed from the previous DSP unit.
|
||||||
|
// length = length in samples at this mix time.
|
||||||
|
// userdata = user parameter passed through in FSOUND_DSP_Create.
|
||||||
|
|
||||||
|
LLWindGen<LLAudioEngine_FMODEX::MIXBUFFERFORMAT> *windgen;
|
||||||
|
FMOD::DSP *thisdsp = (FMOD::DSP *)dsp_state->instance;
|
||||||
|
|
||||||
|
thisdsp->getUserData((void **)&windgen);
|
||||||
|
S32 channels, configwidth, configheight;
|
||||||
|
thisdsp->getInfo(0, 0, &channels, &configwidth, &configheight);
|
||||||
|
|
||||||
|
windgen->windGenerate((LLAudioEngine_FMODEX::MIXBUFFERFORMAT *)newbuffer, length);
|
||||||
|
|
||||||
|
return FMOD_OK;
|
||||||
|
}
|
||||||
131
indra/llaudio/llaudioengine_fmodex.h
Normal file
131
indra/llaudio/llaudioengine_fmodex.h
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
/**
|
||||||
|
* @file audioengine_FMODEX.h
|
||||||
|
* @brief Definition of LLAudioEngine class abstracting the audio
|
||||||
|
* support as a FMOD 3D implementation
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2002&license=viewergpl$
|
||||||
|
*
|
||||||
|
* Copyright (c) 2002-2009, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||||
|
* to you under the terms of the GNU General Public License, version 2.0
|
||||||
|
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||||
|
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||||
|
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||||
|
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||||
|
*
|
||||||
|
* There are special exceptions to the terms and conditions of the GPL as
|
||||||
|
* it is applied to this Source Code. View the full text of the exception
|
||||||
|
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||||
|
* online at
|
||||||
|
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||||
|
*
|
||||||
|
* By copying, modifying or distributing this software, you acknowledge
|
||||||
|
* that you have read and understood your obligations described above,
|
||||||
|
* and agree to abide by those obligations.
|
||||||
|
*
|
||||||
|
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||||
|
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||||
|
* COMPLETENESS OR PERFORMANCE.
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LL_AUDIOENGINE_FMODEX_H
|
||||||
|
#define LL_AUDIOENGINE_FMODEX_H
|
||||||
|
|
||||||
|
#include "llaudioengine.h"
|
||||||
|
#include "lllistener_fmod.h"
|
||||||
|
#include "llwindgen.h"
|
||||||
|
|
||||||
|
//Stubs
|
||||||
|
class LLAudioStreamManagerFMODEX;
|
||||||
|
namespace FMOD
|
||||||
|
{
|
||||||
|
class System;
|
||||||
|
class Channel;
|
||||||
|
class Sound;
|
||||||
|
class DSP;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Interfaces
|
||||||
|
class LLAudioEngine_FMODEX : public LLAudioEngine
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LLAudioEngine_FMODEX(bool enable_profiler);
|
||||||
|
virtual ~LLAudioEngine_FMODEX();
|
||||||
|
|
||||||
|
// initialization/startup/shutdown
|
||||||
|
virtual bool init(const S32 num_channels, void *user_data);
|
||||||
|
virtual std::string getDriverName(bool verbose);
|
||||||
|
virtual void allocateListener();
|
||||||
|
|
||||||
|
virtual void shutdown();
|
||||||
|
|
||||||
|
/*virtual*/ bool initWind();
|
||||||
|
/*virtual*/ void cleanupWind();
|
||||||
|
|
||||||
|
/*virtual*/void updateWind(LLVector3 direction, F32 camera_height_above_water);
|
||||||
|
|
||||||
|
typedef F32 MIXBUFFERFORMAT;
|
||||||
|
|
||||||
|
FMOD::System *getSystem() const {return mSystem;}
|
||||||
|
protected:
|
||||||
|
/*virtual*/ LLAudioBuffer *createBuffer(); // Get a free buffer, or flush an existing one if you have to.
|
||||||
|
/*virtual*/ LLAudioChannel *createChannel(); // Create a new audio channel.
|
||||||
|
|
||||||
|
/*virtual*/ void setInternalGain(F32 gain);
|
||||||
|
|
||||||
|
bool mInited;
|
||||||
|
|
||||||
|
LLWindGen<MIXBUFFERFORMAT> *mWindGen;
|
||||||
|
|
||||||
|
FMOD::DSP *mWindDSP;
|
||||||
|
FMOD::System *mSystem;
|
||||||
|
bool mEnableProfiler;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class LLAudioChannelFMODEX : public LLAudioChannel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LLAudioChannelFMODEX(FMOD::System *audioengine);
|
||||||
|
virtual ~LLAudioChannelFMODEX();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/*virtual*/ void play();
|
||||||
|
/*virtual*/ void playSynced(LLAudioChannel *channelp);
|
||||||
|
/*virtual*/ void cleanup();
|
||||||
|
/*virtual*/ bool isPlaying();
|
||||||
|
|
||||||
|
/*virtual*/ bool updateBuffer();
|
||||||
|
/*virtual*/ void update3DPosition();
|
||||||
|
/*virtual*/ void updateLoop();
|
||||||
|
|
||||||
|
void set3DMode(bool use3d);
|
||||||
|
protected:
|
||||||
|
FMOD::System *getSystem() const {return mSystemp;}
|
||||||
|
FMOD::System *mSystemp;
|
||||||
|
FMOD::Channel *mChannelp;
|
||||||
|
S32 mLastSamplePos;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class LLAudioBufferFMODEX : public LLAudioBuffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LLAudioBufferFMODEX(FMOD::System *audioengine);
|
||||||
|
virtual ~LLAudioBufferFMODEX();
|
||||||
|
|
||||||
|
/*virtual*/ bool loadWAV(const std::string& filename);
|
||||||
|
/*virtual*/ U32 getLength();
|
||||||
|
friend class LLAudioChannelFMODEX;
|
||||||
|
protected:
|
||||||
|
FMOD::System *getSystem() const {return mSystemp;}
|
||||||
|
FMOD::System *mSystemp;
|
||||||
|
FMOD::Sound *getSound() const{ return mSoundp; }
|
||||||
|
FMOD::Sound *mSoundp;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // LL_AUDIOENGINE_FMODEX_H
|
||||||
132
indra/llaudio/lllistener_fmodex.cpp
Normal file
132
indra/llaudio/lllistener_fmodex.cpp
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
/**
|
||||||
|
* @file listener_fmod.cpp
|
||||||
|
* @brief implementation of LISTENER class abstracting the audio
|
||||||
|
* support as a FMOD 3D implementation (windows only)
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2002&license=viewergpl$
|
||||||
|
*
|
||||||
|
* Copyright (c) 2002-2009, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||||
|
* to you under the terms of the GNU General Public License, version 2.0
|
||||||
|
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||||
|
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||||
|
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||||
|
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||||
|
*
|
||||||
|
* There are special exceptions to the terms and conditions of the GPL as
|
||||||
|
* it is applied to this Source Code. View the full text of the exception
|
||||||
|
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||||
|
* online at
|
||||||
|
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||||
|
*
|
||||||
|
* By copying, modifying or distributing this software, you acknowledge
|
||||||
|
* that you have read and understood your obligations described above,
|
||||||
|
* and agree to abide by those obligations.
|
||||||
|
*
|
||||||
|
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||||
|
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||||
|
* COMPLETENESS OR PERFORMANCE.
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "linden_common.h"
|
||||||
|
#include "llaudioengine.h"
|
||||||
|
#include "lllistener_fmodex.h"
|
||||||
|
#include "fmod.hpp"
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
// constructor
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
LLListener_FMODEX::LLListener_FMODEX(FMOD::System *system)
|
||||||
|
{
|
||||||
|
mSystem = system;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
LLListener_FMODEX::~LLListener_FMODEX()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
void LLListener_FMODEX::init(void)
|
||||||
|
{
|
||||||
|
// do inherited
|
||||||
|
LLListener::init();
|
||||||
|
mDopplerFactor = 1.0f;
|
||||||
|
mRolloffFactor = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
void LLListener_FMODEX::translate(LLVector3 offset)
|
||||||
|
{
|
||||||
|
LLListener::translate(offset);
|
||||||
|
|
||||||
|
mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mPosition.mV, NULL, (FMOD_VECTOR*)mListenAt.mV, (FMOD_VECTOR*)mListenUp.mV);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
void LLListener_FMODEX::setPosition(LLVector3 pos)
|
||||||
|
{
|
||||||
|
LLListener::setPosition(pos);
|
||||||
|
|
||||||
|
mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mPosition.mV, NULL, (FMOD_VECTOR*)mListenAt.mV, (FMOD_VECTOR*)mListenUp.mV);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
void LLListener_FMODEX::setVelocity(LLVector3 vel)
|
||||||
|
{
|
||||||
|
LLListener::setVelocity(vel);
|
||||||
|
|
||||||
|
mSystem->set3DListenerAttributes(0, NULL, (FMOD_VECTOR*)mVelocity.mV, (FMOD_VECTOR*)mListenAt.mV, (FMOD_VECTOR*)mListenUp.mV);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
void LLListener_FMODEX::orient(LLVector3 up, LLVector3 at)
|
||||||
|
{
|
||||||
|
LLListener::orient(up, at);
|
||||||
|
|
||||||
|
// Welcome to the transition between right and left
|
||||||
|
// (coordinate systems, that is)
|
||||||
|
// Leaving the at vector alone results in a L/R reversal
|
||||||
|
// since DX is left-handed and we (LL, OpenGL, OpenAL) are right-handed
|
||||||
|
at = -at;
|
||||||
|
|
||||||
|
mSystem->set3DListenerAttributes(0, NULL, NULL, (FMOD_VECTOR*)at.mV, (FMOD_VECTOR*)up.mV);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
void LLListener_FMODEX::commitDeferredChanges()
|
||||||
|
{
|
||||||
|
mSystem->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LLListener_FMODEX::setRolloffFactor(F32 factor)
|
||||||
|
{
|
||||||
|
mRolloffFactor = factor;
|
||||||
|
mSystem->set3DSettings(mDopplerFactor, 1.f, mRolloffFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
F32 LLListener_FMODEX::getRolloffFactor()
|
||||||
|
{
|
||||||
|
return mRolloffFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LLListener_FMODEX::setDopplerFactor(F32 factor)
|
||||||
|
{
|
||||||
|
mDopplerFactor = factor;
|
||||||
|
mSystem->set3DSettings(mDopplerFactor, 1.f, mRolloffFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
F32 LLListener_FMODEX::getDopplerFactor()
|
||||||
|
{
|
||||||
|
return mDopplerFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* @file llversionserver.h
|
* @file listener_fmod.h
|
||||||
* @brief
|
* @brief Description of LISTENER class abstracting the audio support
|
||||||
|
* as an FMOD 3D implementation (windows and Linux)
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2002&license=viewergpl$
|
* $LicenseInfo:firstyear=2002&license=viewergpl$
|
||||||
*
|
*
|
||||||
@@ -30,15 +31,41 @@
|
|||||||
* $/LicenseInfo$
|
* $/LicenseInfo$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LL_LLVERSIONSERVER_H
|
#ifndef LL_LISTENER_FMODEX_H
|
||||||
#define LL_LLVERSIONSERVER_H
|
#define LL_LISTENER_FMODEX_H
|
||||||
|
|
||||||
const S32 LL_VERSION_MAJOR = 1;
|
#include "lllistener.h"
|
||||||
const S32 LL_VERSION_MINOR = 27;
|
|
||||||
const S32 LL_VERSION_PATCH = 0;
|
|
||||||
const S32 LL_VERSION_BUILD = 132845;
|
|
||||||
|
|
||||||
const char * const LL_CHANNEL = "Second Life Server";
|
//Stubs
|
||||||
|
namespace FMOD
|
||||||
|
{
|
||||||
|
class System;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Interfaces
|
||||||
|
class LLListener_FMODEX : public LLListener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LLListener_FMODEX(FMOD::System *system);
|
||||||
|
virtual ~LLListener_FMODEX();
|
||||||
|
virtual void init();
|
||||||
|
|
||||||
|
virtual void translate(LLVector3 offset);
|
||||||
|
virtual void setPosition(LLVector3 pos);
|
||||||
|
virtual void setVelocity(LLVector3 vel);
|
||||||
|
virtual void orient(LLVector3 up, LLVector3 at);
|
||||||
|
virtual void commitDeferredChanges();
|
||||||
|
|
||||||
|
virtual void setDopplerFactor(F32 factor);
|
||||||
|
virtual F32 getDopplerFactor();
|
||||||
|
virtual void setRolloffFactor(F32 factor);
|
||||||
|
virtual F32 getRolloffFactor();
|
||||||
|
protected:
|
||||||
|
FMOD::System *mSystem;
|
||||||
|
F32 mDopplerFactor;
|
||||||
|
F32 mRolloffFactor;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@@ -36,6 +36,8 @@
|
|||||||
|
|
||||||
#include "stdtypes.h" // from llcommon
|
#include "stdtypes.h" // from llcommon
|
||||||
|
|
||||||
|
class LLSD;
|
||||||
|
|
||||||
// Entirely abstract. Based exactly on the historic API.
|
// Entirely abstract. Based exactly on the historic API.
|
||||||
class LLStreamingAudioInterface
|
class LLStreamingAudioInterface
|
||||||
{
|
{
|
||||||
@@ -51,6 +53,11 @@ class LLStreamingAudioInterface
|
|||||||
virtual void setGain(F32 vol) = 0;
|
virtual void setGain(F32 vol) = 0;
|
||||||
virtual F32 getGain() = 0;
|
virtual F32 getGain() = 0;
|
||||||
virtual std::string getURL() = 0;
|
virtual std::string getURL() = 0;
|
||||||
|
|
||||||
|
virtual bool supportsMetaData() = 0;
|
||||||
|
virtual const LLSD *getMetaData() = 0;
|
||||||
|
virtual bool supportsWaveData() = 0;
|
||||||
|
virtual bool getWaveData(float* arr, S32 count, S32 stride = 1) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // LL_STREAMINGAUDIO_H
|
#endif // LL_STREAMINGAUDIO_H
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ public:
|
|||||||
const std::string& getURL() { return mInternetStreamURL; }
|
const std::string& getURL() { return mInternetStreamURL; }
|
||||||
|
|
||||||
int getOpenState();
|
int getOpenState();
|
||||||
|
|
||||||
|
FSOUND_STREAM* getStream() { return mInternetStream; }
|
||||||
protected:
|
protected:
|
||||||
FSOUND_STREAM* mInternetStream;
|
FSOUND_STREAM* mInternetStream;
|
||||||
bool mReady;
|
bool mReady;
|
||||||
@@ -66,7 +68,8 @@ protected:
|
|||||||
LLStreamingAudio_FMOD::LLStreamingAudio_FMOD() :
|
LLStreamingAudio_FMOD::LLStreamingAudio_FMOD() :
|
||||||
mCurrentInternetStreamp(NULL),
|
mCurrentInternetStreamp(NULL),
|
||||||
mFMODInternetStreamChannel(-1),
|
mFMODInternetStreamChannel(-1),
|
||||||
mGain(1.0f)
|
mGain(1.0f),
|
||||||
|
mMetaData(NULL)
|
||||||
{
|
{
|
||||||
// Number of milliseconds of audio to buffer for the audio card.
|
// Number of milliseconds of audio to buffer for the audio card.
|
||||||
// Must be larger than the usual Second Life frame stutter time.
|
// Must be larger than the usual Second Life frame stutter time.
|
||||||
@@ -87,6 +90,17 @@ LLStreamingAudio_FMOD::~LLStreamingAudio_FMOD()
|
|||||||
// nothing interesting/safe to do.
|
// nothing interesting/safe to do.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signed char F_CALLBACKAPI MetaDataCallback(char *name, char *value, void *userdata)
|
||||||
|
{
|
||||||
|
std::string szName(name);
|
||||||
|
if(szName == "TITLE" || szName=="TIT2" || szName=="Title")
|
||||||
|
(*(LLSD*)userdata)["TITLE"] = value;
|
||||||
|
if(szName == "ARTIST" || szName=="TPE1" || szName =="WM/AlbumTitle")
|
||||||
|
(*(LLSD*)userdata)["ARTIST"] = value;
|
||||||
|
else
|
||||||
|
(*(LLSD*)userdata)[std::string(name)] = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void LLStreamingAudio_FMOD::start(const std::string& url)
|
void LLStreamingAudio_FMOD::start(const std::string& url)
|
||||||
{
|
{
|
||||||
@@ -104,6 +118,10 @@ void LLStreamingAudio_FMOD::start(const std::string& url)
|
|||||||
llinfos << "Starting internet stream: " << url << llendl;
|
llinfos << "Starting internet stream: " << url << llendl;
|
||||||
mCurrentInternetStreamp = new LLAudioStreamManagerFMOD(url);
|
mCurrentInternetStreamp = new LLAudioStreamManagerFMOD(url);
|
||||||
mURL = url;
|
mURL = url;
|
||||||
|
if(mCurrentInternetStreamp->getStream())
|
||||||
|
{
|
||||||
|
mMetaData = new LLSD;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -154,6 +172,10 @@ void LLStreamingAudio_FMOD::update()
|
|||||||
// Reset volume to previously set volume
|
// Reset volume to previously set volume
|
||||||
setGain(getGain());
|
setGain(getGain());
|
||||||
FSOUND_SetPaused(mFMODInternetStreamChannel, false);
|
FSOUND_SetPaused(mFMODInternetStreamChannel, false);
|
||||||
|
if(mCurrentInternetStreamp->getStream() && mMetaData)
|
||||||
|
{
|
||||||
|
FSOUND_Stream_Net_SetMetadataCallback(mCurrentInternetStreamp->getStream(),&MetaDataCallback, mMetaData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,11 +206,17 @@ void LLStreamingAudio_FMOD::update()
|
|||||||
// buffering
|
// buffering
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLStreamingAudio_FMOD::stop()
|
void LLStreamingAudio_FMOD::stop()
|
||||||
{
|
{
|
||||||
|
if(mMetaData)
|
||||||
|
{
|
||||||
|
if(mCurrentInternetStreamp && mCurrentInternetStreamp->getStream())
|
||||||
|
FSOUND_Stream_Net_SetMetadataCallback(mCurrentInternetStreamp->getStream(),NULL,NULL);
|
||||||
|
delete mMetaData;
|
||||||
|
mMetaData = NULL;
|
||||||
|
}
|
||||||
if (mFMODInternetStreamChannel != -1)
|
if (mFMODInternetStreamChannel != -1)
|
||||||
{
|
{
|
||||||
FSOUND_SetPaused(mFMODInternetStreamChannel, true);
|
FSOUND_SetPaused(mFMODInternetStreamChannel, true);
|
||||||
|
|||||||
@@ -55,6 +55,10 @@ class LLStreamingAudio_FMOD : public LLStreamingAudioInterface
|
|||||||
/*virtual*/ F32 getGain();
|
/*virtual*/ F32 getGain();
|
||||||
/*virtual*/ std::string getURL();
|
/*virtual*/ std::string getURL();
|
||||||
|
|
||||||
|
/*virtual*/ bool supportsMetaData(){return true;}
|
||||||
|
/*virtual*/ const LLSD *getMetaData(){return mMetaData;} //return NULL if not playing.
|
||||||
|
/*virtual*/ bool supportsWaveData(){return false;}
|
||||||
|
/*virtual*/ bool getWaveData(float* arr, S32 count, S32 stride = 1){return false;}
|
||||||
private:
|
private:
|
||||||
LLAudioStreamManagerFMOD *mCurrentInternetStreamp;
|
LLAudioStreamManagerFMOD *mCurrentInternetStreamp;
|
||||||
int mFMODInternetStreamChannel;
|
int mFMODInternetStreamChannel;
|
||||||
@@ -62,6 +66,8 @@ private:
|
|||||||
|
|
||||||
std::string mURL;
|
std::string mURL;
|
||||||
F32 mGain;
|
F32 mGain;
|
||||||
|
|
||||||
|
LLSD *mMetaData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
450
indra/llaudio/llstreamingaudio_fmodex.cpp
Normal file
450
indra/llaudio/llstreamingaudio_fmodex.cpp
Normal file
@@ -0,0 +1,450 @@
|
|||||||
|
/**
|
||||||
|
* @file streamingaudio_fmod.cpp
|
||||||
|
* @brief LLStreamingAudio_FMODEX implementation
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2009&license=viewergpl$
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||||
|
* to you under the terms of the GNU General Public License, version 2.0
|
||||||
|
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||||
|
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||||
|
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||||
|
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||||
|
*
|
||||||
|
* There are special exceptions to the terms and conditions of the GPL as
|
||||||
|
* it is applied to this Source Code. View the full text of the exception
|
||||||
|
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||||
|
* online at
|
||||||
|
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||||
|
*
|
||||||
|
* By copying, modifying or distributing this software, you acknowledge
|
||||||
|
* that you have read and understood your obligations described above,
|
||||||
|
* and agree to abide by those obligations.
|
||||||
|
*
|
||||||
|
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||||
|
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||||
|
* COMPLETENESS OR PERFORMANCE.
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "linden_common.h"
|
||||||
|
|
||||||
|
#include "llmath.h"
|
||||||
|
|
||||||
|
#include "fmod.hpp"
|
||||||
|
#include "fmod_errors.h"
|
||||||
|
|
||||||
|
#include "llstreamingaudio_fmodex.h"
|
||||||
|
|
||||||
|
|
||||||
|
class LLAudioStreamManagerFMODEX
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LLAudioStreamManagerFMODEX(FMOD::System *system, const std::string& url);
|
||||||
|
FMOD::Channel* startStream();
|
||||||
|
bool stopStream(); // Returns true if the stream was successfully stopped.
|
||||||
|
bool ready();
|
||||||
|
|
||||||
|
const std::string& getURL() { return mInternetStreamURL; }
|
||||||
|
|
||||||
|
FMOD_OPENSTATE getOpenState();
|
||||||
|
protected:
|
||||||
|
FMOD::System* mSystem;
|
||||||
|
FMOD::Channel* mStreamChannel;
|
||||||
|
FMOD::Sound* mInternetStream;
|
||||||
|
bool mReady;
|
||||||
|
|
||||||
|
std::string mInternetStreamURL;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
// Internet Streaming
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
LLStreamingAudio_FMODEX::LLStreamingAudio_FMODEX(FMOD::System *system) :
|
||||||
|
mSystem(system),
|
||||||
|
mCurrentInternetStreamp(NULL),
|
||||||
|
mFMODInternetStreamChannelp(NULL),
|
||||||
|
mGain(1.0f),
|
||||||
|
mMetaData(NULL)
|
||||||
|
{
|
||||||
|
// Number of milliseconds of audio to buffer for the audio card.
|
||||||
|
// Must be larger than the usual Second Life frame stutter time.
|
||||||
|
const U32 buffer_seconds = 5; //sec
|
||||||
|
const U32 estimated_bitrate = 128; //kbit/sec
|
||||||
|
mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES);
|
||||||
|
|
||||||
|
// Here's where we set the size of the network buffer and some buffering
|
||||||
|
// parameters. In this case we want a network buffer of 16k, we want it
|
||||||
|
// to prebuffer 40% of that when we first connect, and we want it
|
||||||
|
// to rebuffer 80% of that whenever we encounter a buffer underrun.
|
||||||
|
|
||||||
|
// Leave the net buffer properties at the default.
|
||||||
|
//FSOUND_Stream_Net_SetBufferProperties(20000, 40, 80);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LLStreamingAudio_FMODEX::~LLStreamingAudio_FMODEX()
|
||||||
|
{
|
||||||
|
// nothing interesting/safe to do.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LLStreamingAudio_FMODEX::start(const std::string& url)
|
||||||
|
{
|
||||||
|
//if (!mInited)
|
||||||
|
//{
|
||||||
|
// llwarns << "startInternetStream before audio initialized" << llendl;
|
||||||
|
// return;
|
||||||
|
//}
|
||||||
|
|
||||||
|
// "stop" stream but don't clear url, etc. in case url == mInternetStreamURL
|
||||||
|
stop();
|
||||||
|
|
||||||
|
if (!url.empty())
|
||||||
|
{
|
||||||
|
llinfos << "Starting internet stream: " << url << llendl;
|
||||||
|
mCurrentInternetStreamp = new LLAudioStreamManagerFMODEX(mSystem,url);
|
||||||
|
mURL = url;
|
||||||
|
mMetaData = new LLSD;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
llinfos << "Set internet stream to null" << llendl;
|
||||||
|
mURL.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LLStreamingAudio_FMODEX::update()
|
||||||
|
{
|
||||||
|
// Kill dead internet streams, if possible
|
||||||
|
std::list<LLAudioStreamManagerFMODEX *>::iterator iter;
|
||||||
|
for (iter = mDeadStreams.begin(); iter != mDeadStreams.end();)
|
||||||
|
{
|
||||||
|
LLAudioStreamManagerFMODEX *streamp = *iter;
|
||||||
|
if (streamp->stopStream())
|
||||||
|
{
|
||||||
|
llinfos << "Closed dead stream" << llendl;
|
||||||
|
delete streamp;
|
||||||
|
mDeadStreams.erase(iter++);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't do anything if there are no streams playing
|
||||||
|
if (!mCurrentInternetStreamp)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMOD_OPENSTATE open_state = mCurrentInternetStreamp->getOpenState();
|
||||||
|
|
||||||
|
if (open_state == FMOD_OPENSTATE_READY)
|
||||||
|
{
|
||||||
|
// Stream is live
|
||||||
|
|
||||||
|
// start the stream if it's ready
|
||||||
|
if (!mFMODInternetStreamChannelp &&
|
||||||
|
(mFMODInternetStreamChannelp = mCurrentInternetStreamp->startStream()))
|
||||||
|
{
|
||||||
|
// Reset volume to previously set volume
|
||||||
|
setGain(getGain());
|
||||||
|
mFMODInternetStreamChannelp->setPaused(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(open_state == FMOD_OPENSTATE_ERROR)
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mFMODInternetStreamChannelp)
|
||||||
|
{
|
||||||
|
if(!mMetaData)
|
||||||
|
mMetaData = new LLSD;
|
||||||
|
|
||||||
|
FMOD::Sound *sound = NULL;
|
||||||
|
|
||||||
|
if(mFMODInternetStreamChannelp->getCurrentSound(&sound) == FMOD_OK && sound)
|
||||||
|
{
|
||||||
|
FMOD_TAG tag;
|
||||||
|
S32 tagcount, dirtytagcount;
|
||||||
|
if(sound->getNumTags(&tagcount, &dirtytagcount) == FMOD_OK && dirtytagcount)
|
||||||
|
{
|
||||||
|
mMetaData->clear();
|
||||||
|
|
||||||
|
for(S32 i = 0; i < tagcount; ++i)
|
||||||
|
{
|
||||||
|
if(sound->getTag(NULL, i, &tag)!=FMOD_OK)
|
||||||
|
continue;
|
||||||
|
std::string name = tag.name;
|
||||||
|
switch(tag.type) //Crappy tag translate table.
|
||||||
|
{
|
||||||
|
case(FMOD_TAGTYPE_ID3V2):
|
||||||
|
if(name == "TIT2") name = "TITLE";
|
||||||
|
else if(name == "TPE1") name = "ARTIST";
|
||||||
|
break;
|
||||||
|
case(FMOD_TAGTYPE_ASF):
|
||||||
|
if(name == "Title") name = "TITLE";
|
||||||
|
else if(name == "WM/AlbumArtist") name = "ARTIST";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch(tag.datatype)
|
||||||
|
{
|
||||||
|
case(FMOD_TAGDATATYPE_INT):
|
||||||
|
(*mMetaData)[name]=*(LLSD::Integer*)(tag.data);
|
||||||
|
llinfos << tag.name << ": " << *(int*)(tag.data) << llendl;
|
||||||
|
break;
|
||||||
|
case(FMOD_TAGDATATYPE_FLOAT):
|
||||||
|
(*mMetaData)[name]=*(LLSD::Float*)(tag.data);
|
||||||
|
llinfos << tag.name << ": " << *(float*)(tag.data) << llendl;
|
||||||
|
break;
|
||||||
|
case(FMOD_TAGDATATYPE_STRING):
|
||||||
|
{
|
||||||
|
std::string out = rawstr_to_utf8(std::string((char*)tag.data,tag.datalen));
|
||||||
|
(*mMetaData)[name]=out;
|
||||||
|
llinfos << tag.name << ": " << out << llendl;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case(FMOD_TAGDATATYPE_STRING_UTF16):
|
||||||
|
{
|
||||||
|
std::string out((char*)tag.data,tag.datalen);
|
||||||
|
(*mMetaData)[std::string(tag.name)]=out;
|
||||||
|
llinfos << tag.name << ": " << out << llendl;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case(FMOD_TAGDATATYPE_STRING_UTF16BE):
|
||||||
|
{
|
||||||
|
std::string out((char*)tag.data,tag.datalen);
|
||||||
|
U16* buf = (U16*)out.c_str();
|
||||||
|
for(U32 j = 0; j < out.size()/2; ++j)
|
||||||
|
(((buf[j] & 0xff)<<8) | ((buf[j] & 0xff00)>>8));
|
||||||
|
(*mMetaData)[std::string(tag.name)]=out;
|
||||||
|
llinfos << tag.name << ": " << out << llendl;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLStreamingAudio_FMODEX::stop()
|
||||||
|
{
|
||||||
|
if(mMetaData)
|
||||||
|
{
|
||||||
|
delete mMetaData;
|
||||||
|
mMetaData = NULL;
|
||||||
|
}
|
||||||
|
if (mFMODInternetStreamChannelp)
|
||||||
|
{
|
||||||
|
mFMODInternetStreamChannelp->setPaused(true);
|
||||||
|
mFMODInternetStreamChannelp->setPriority(0);
|
||||||
|
mFMODInternetStreamChannelp = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mCurrentInternetStreamp)
|
||||||
|
{
|
||||||
|
llinfos << "Stopping internet stream: " << mCurrentInternetStreamp->getURL() << llendl;
|
||||||
|
if (mCurrentInternetStreamp->stopStream())
|
||||||
|
{
|
||||||
|
delete mCurrentInternetStreamp;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
llwarns << "Pushing stream to dead list: " << mCurrentInternetStreamp->getURL() << llendl;
|
||||||
|
mDeadStreams.push_back(mCurrentInternetStreamp);
|
||||||
|
}
|
||||||
|
mCurrentInternetStreamp = NULL;
|
||||||
|
//mURL.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLStreamingAudio_FMODEX::pause(int pauseopt)
|
||||||
|
{
|
||||||
|
if (pauseopt < 0)
|
||||||
|
{
|
||||||
|
pauseopt = mCurrentInternetStreamp ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pauseopt)
|
||||||
|
{
|
||||||
|
if (mCurrentInternetStreamp)
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
start(getURL());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// A stream is "playing" if it has been requested to start. That
|
||||||
|
// doesn't necessarily mean audio is coming out of the speakers.
|
||||||
|
int LLStreamingAudio_FMODEX::isPlaying()
|
||||||
|
{
|
||||||
|
if (mCurrentInternetStreamp)
|
||||||
|
{
|
||||||
|
return 1; // Active and playing
|
||||||
|
}
|
||||||
|
else if (!mURL.empty())
|
||||||
|
{
|
||||||
|
return 2; // "Paused"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
F32 LLStreamingAudio_FMODEX::getGain()
|
||||||
|
{
|
||||||
|
return mGain;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string LLStreamingAudio_FMODEX::getURL()
|
||||||
|
{
|
||||||
|
return mURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LLStreamingAudio_FMODEX::setGain(F32 vol)
|
||||||
|
{
|
||||||
|
mGain = vol;
|
||||||
|
|
||||||
|
if (mFMODInternetStreamChannelp)
|
||||||
|
{
|
||||||
|
vol = llclamp(vol * vol, 0.f, 1.f); //should vol be squared here?
|
||||||
|
|
||||||
|
mFMODInternetStreamChannelp->setVolume(vol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ bool LLStreamingAudio_FMODEX::getWaveData(float* arr, S32 count, S32 stride/*=1*/)
|
||||||
|
{
|
||||||
|
if(!mFMODInternetStreamChannelp || !mCurrentInternetStreamp)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
static std::vector<float> local_array(count); //Have to have an extra buffer to mix channels. Bleh.
|
||||||
|
if(count > (S32)local_array.size()) //Expand the array if needed. Try to minimize allocation calls, so don't ever shrink.
|
||||||
|
local_array.resize(count);
|
||||||
|
|
||||||
|
if( mFMODInternetStreamChannelp->getWaveData(&local_array[0],count,0) == FMOD_OK &&
|
||||||
|
mFMODInternetStreamChannelp->getWaveData(&arr[0],count,1) == FMOD_OK )
|
||||||
|
{
|
||||||
|
for(S32 i = count-1;i>=0;i-=stride)
|
||||||
|
{
|
||||||
|
arr[i] += local_array[i];
|
||||||
|
arr[i] *= .5f;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////
|
||||||
|
// manager of possibly-multiple internet audio streams
|
||||||
|
|
||||||
|
LLAudioStreamManagerFMODEX::LLAudioStreamManagerFMODEX(FMOD::System *system, const std::string& url) :
|
||||||
|
mSystem(system),
|
||||||
|
mStreamChannel(NULL),
|
||||||
|
mInternetStream(NULL),
|
||||||
|
mReady(false)
|
||||||
|
{
|
||||||
|
mInternetStreamURL = url;
|
||||||
|
|
||||||
|
FMOD_CREATESOUNDEXINFO exinfo;
|
||||||
|
memset(&exinfo,0,sizeof(exinfo));
|
||||||
|
exinfo.cbsize = sizeof(exinfo);
|
||||||
|
exinfo.suggestedsoundtype = FMOD_SOUND_TYPE_MPEG; //Hint to speed up loading.
|
||||||
|
|
||||||
|
FMOD_RESULT result = mSystem->createStream(url.c_str(), FMOD_2D | FMOD_NONBLOCKING | FMOD_IGNORETAGS, &exinfo, &mInternetStream);
|
||||||
|
|
||||||
|
if (result!= FMOD_OK)
|
||||||
|
{
|
||||||
|
llwarns << "Couldn't open fmod stream, error "
|
||||||
|
<< FMOD_ErrorString(result)
|
||||||
|
<< llendl;
|
||||||
|
mReady = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mReady = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMOD::Channel *LLAudioStreamManagerFMODEX::startStream()
|
||||||
|
{
|
||||||
|
// We need a live and opened stream before we try and play it.
|
||||||
|
if (!mInternetStream || getOpenState() != FMOD_OPENSTATE_READY)
|
||||||
|
{
|
||||||
|
llwarns << "No internet stream to start playing!" << llendl;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mStreamChannel)
|
||||||
|
return mStreamChannel; //Already have a channel for this stream.
|
||||||
|
|
||||||
|
mSystem->playSound(FMOD_CHANNEL_FREE, mInternetStream, true, &mStreamChannel);
|
||||||
|
return mStreamChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LLAudioStreamManagerFMODEX::stopStream()
|
||||||
|
{
|
||||||
|
if (mInternetStream)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
bool close = true;
|
||||||
|
switch (getOpenState())
|
||||||
|
{
|
||||||
|
case FMOD_OPENSTATE_CONNECTING:
|
||||||
|
close = false;
|
||||||
|
break;
|
||||||
|
/*case FSOUND_STREAM_NET_NOTCONNECTED:
|
||||||
|
case FSOUND_STREAM_NET_BUFFERING:
|
||||||
|
case FSOUND_STREAM_NET_READY:
|
||||||
|
case FSOUND_STREAM_NET_ERROR:*/
|
||||||
|
default:
|
||||||
|
close = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (close)
|
||||||
|
{
|
||||||
|
mInternetStream->release();
|
||||||
|
mStreamChannel = NULL;
|
||||||
|
mInternetStream = NULL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FMOD_OPENSTATE LLAudioStreamManagerFMODEX::getOpenState()
|
||||||
|
{
|
||||||
|
FMOD_OPENSTATE state;
|
||||||
|
mInternetStream->getOpenState(&state,NULL,NULL,NULL);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
83
indra/llaudio/llstreamingaudio_fmodex.h
Normal file
83
indra/llaudio/llstreamingaudio_fmodex.h
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
* @file streamingaudio_fmod.h
|
||||||
|
* @author Tofu Linden
|
||||||
|
* @brief Definition of LLStreamingAudio_FMOD implementation
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2009&license=viewergpl$
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||||
|
* to you under the terms of the GNU General Public License, version 2.0
|
||||||
|
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||||
|
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||||
|
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||||
|
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||||
|
*
|
||||||
|
* There are special exceptions to the terms and conditions of the GPL as
|
||||||
|
* it is applied to this Source Code. View the full text of the exception
|
||||||
|
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||||
|
* online at
|
||||||
|
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||||
|
*
|
||||||
|
* By copying, modifying or distributing this software, you acknowledge
|
||||||
|
* that you have read and understood your obligations described above,
|
||||||
|
* and agree to abide by those obligations.
|
||||||
|
*
|
||||||
|
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||||
|
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||||
|
* COMPLETENESS OR PERFORMANCE.
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LL_STREAMINGAUDIO_FMOD_H
|
||||||
|
#define LL_STREAMINGAUDIO_FMOD_H
|
||||||
|
|
||||||
|
#include "stdtypes.h" // from llcommon
|
||||||
|
|
||||||
|
#include "llstreamingaudio.h"
|
||||||
|
|
||||||
|
//Stubs
|
||||||
|
class LLAudioStreamManagerFMODEX;
|
||||||
|
namespace FMOD
|
||||||
|
{
|
||||||
|
class System;
|
||||||
|
class Channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Interfaces
|
||||||
|
class LLStreamingAudio_FMODEX : public LLStreamingAudioInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LLStreamingAudio_FMODEX(FMOD::System *system);
|
||||||
|
/*virtual*/ ~LLStreamingAudio_FMODEX();
|
||||||
|
|
||||||
|
/*virtual*/ void start(const std::string& url);
|
||||||
|
/*virtual*/ void stop();
|
||||||
|
/*virtual*/ void pause(int pause);
|
||||||
|
/*virtual*/ void update();
|
||||||
|
/*virtual*/ int isPlaying();
|
||||||
|
/*virtual*/ void setGain(F32 vol);
|
||||||
|
/*virtual*/ F32 getGain();
|
||||||
|
/*virtual*/ std::string getURL();
|
||||||
|
|
||||||
|
/*virtual*/ bool supportsMetaData(){return true;}
|
||||||
|
/*virtual*/ const LLSD *getMetaData(){return mMetaData;} //return NULL if not playing.
|
||||||
|
/*virtual*/ bool supportsWaveData(){return true;}
|
||||||
|
/*virtual*/ bool getWaveData(float* arr, S32 count, S32 stride = 1);
|
||||||
|
private:
|
||||||
|
FMOD::System *mSystem;
|
||||||
|
|
||||||
|
LLAudioStreamManagerFMODEX *mCurrentInternetStreamp;
|
||||||
|
FMOD::Channel *mFMODInternetStreamChannelp;
|
||||||
|
std::list<LLAudioStreamManagerFMODEX *> mDeadStreams;
|
||||||
|
|
||||||
|
std::string mURL;
|
||||||
|
F32 mGain;
|
||||||
|
|
||||||
|
LLSD *mMetaData;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // LL_STREAMINGAUDIO_FMOD_H
|
||||||
@@ -133,10 +133,10 @@ BOOL decode_vorbis_file(LLVFS *vfs, const LLUUID &in_uuid, char *out_fname)
|
|||||||
return(FALSE);
|
return(FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
//**********************************
|
// **********************************
|
||||||
LLAPRFile outfile ;
|
LLAPRFile outfile ;
|
||||||
outfile.open(out_fname,LL_APR_WPB);
|
outfile.open(out_fname,LL_APR_WPB);
|
||||||
//**********************************
|
// **********************************
|
||||||
if (!outfile.getFileHandle())
|
if (!outfile.getFileHandle())
|
||||||
{
|
{
|
||||||
llwarning("unable to open vorbis destination file for writing",0);
|
llwarning("unable to open vorbis destination file for writing",0);
|
||||||
@@ -308,9 +308,9 @@ BOOL decode_vorbis_file(LLVFS *vfs, const LLUUID &in_uuid, char *out_fname)
|
|||||||
|
|
||||||
outfile.seek(SEEK_END,-fade_length*2);
|
outfile.seek(SEEK_END,-fade_length*2);
|
||||||
outfile.write(pcmout,2*fade_length); //write back xfaded last 16 samples
|
outfile.write(pcmout,2*fade_length); //write back xfaded last 16 samples
|
||||||
//*******************
|
// *******************
|
||||||
outfile.close();
|
outfile.close();
|
||||||
//*******************
|
// *******************
|
||||||
|
|
||||||
if ((36 == data_length) || (!(eof)))
|
if ((36 == data_length) || (!(eof)))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -87,10 +87,10 @@ S32 check_for_invalid_wav_formats(const std::string& in_fname, std::string& erro
|
|||||||
|
|
||||||
error_msg.clear();
|
error_msg.clear();
|
||||||
|
|
||||||
//********************************
|
// ********************************
|
||||||
LLAPRFile infile ;
|
LLAPRFile infile ;
|
||||||
infile.open(in_fname,LL_APR_RB);
|
infile.open(in_fname,LL_APR_RB);
|
||||||
//********************************
|
// ********************************
|
||||||
if (!infile.getFileHandle())
|
if (!infile.getFileHandle())
|
||||||
{
|
{
|
||||||
error_msg = "CannotUploadSoundFile";
|
error_msg = "CannotUploadSoundFile";
|
||||||
@@ -159,9 +159,9 @@ S32 check_for_invalid_wav_formats(const std::string& in_fname, std::string& erro
|
|||||||
file_pos += (chunk_length + 8);
|
file_pos += (chunk_length + 8);
|
||||||
chunk_length = 0;
|
chunk_length = 0;
|
||||||
}
|
}
|
||||||
//****************
|
// ****************
|
||||||
infile.close();
|
infile.close();
|
||||||
//****************
|
// ****************
|
||||||
|
|
||||||
if (!uncompressed_pcm)
|
if (!uncompressed_pcm)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
#define WINDGEN_H
|
#define WINDGEN_H
|
||||||
|
|
||||||
#include "llcommon.h"
|
#include "llcommon.h"
|
||||||
|
#include "llrand.h"
|
||||||
|
|
||||||
template <class MIXBUFFERFORMAT_T>
|
template <class MIXBUFFERFORMAT_T>
|
||||||
class LLWindGen
|
class LLWindGen
|
||||||
@@ -60,6 +61,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
const U32 getInputSamplingRate() { return mInputSamplingRate; }
|
const U32 getInputSamplingRate() { return mInputSamplingRate; }
|
||||||
|
const F32 getNextSample();
|
||||||
|
const F32 getClampedSample(bool clamp, F32 sample);
|
||||||
|
|
||||||
// newbuffer = the buffer passed from the previous DSP unit.
|
// newbuffer = the buffer passed from the previous DSP unit.
|
||||||
// numsamples = length in samples-per-channel at this mix time.
|
// numsamples = length in samples-per-channel at this mix time.
|
||||||
@@ -95,7 +98,7 @@ public:
|
|||||||
|
|
||||||
// Start with white noise
|
// Start with white noise
|
||||||
// This expression is fragile, rearrange it and it will break!
|
// This expression is fragile, rearrange it and it will break!
|
||||||
next_sample = (F32)rand() * (1.0f / (F32)(RAND_MAX / (U16_MAX / 8))) + (F32)(S16_MIN / 8);
|
next_sample = getNextSample();
|
||||||
|
|
||||||
// Apply a pinking filter
|
// Apply a pinking filter
|
||||||
// Magic numbers taken from PKE method at http://www.firstpr.com.au/dsp/pink-noise/
|
// Magic numbers taken from PKE method at http://www.firstpr.com.au/dsp/pink-noise/
|
||||||
@@ -132,23 +135,13 @@ public:
|
|||||||
for (U8 i=mSubSamples; i && numsamples; --i, --numsamples)
|
for (U8 i=mSubSamples; i && numsamples; --i, --numsamples)
|
||||||
{
|
{
|
||||||
mLastSample = mLastSample + delta;
|
mLastSample = mLastSample + delta;
|
||||||
S32 sample_right = (S32)(mLastSample * mCurrentPanGainR);
|
MIXBUFFERFORMAT_T sample_right = (MIXBUFFERFORMAT_T)getClampedSample(clip, mLastSample * mCurrentPanGainR);
|
||||||
S32 sample_left = (S32)mLastSample - sample_right;
|
MIXBUFFERFORMAT_T sample_left = (MIXBUFFERFORMAT_T)getClampedSample(clip, mLastSample - (F32)sample_right);
|
||||||
|
|
||||||
if (!clip)
|
*cursamplep = sample_left;
|
||||||
{
|
++cursamplep;
|
||||||
*cursamplep = (MIXBUFFERFORMAT_T)sample_left;
|
*cursamplep = sample_right;
|
||||||
++cursamplep;
|
++cursamplep;
|
||||||
*cursamplep = (MIXBUFFERFORMAT_T)sample_right;
|
|
||||||
++cursamplep;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*cursamplep = (MIXBUFFERFORMAT_T)llclamp(sample_left, (S32)S16_MIN, (S32)S16_MAX);
|
|
||||||
++cursamplep;
|
|
||||||
*cursamplep = (MIXBUFFERFORMAT_T)llclamp(sample_right, (S32)S16_MIN, (S32)S16_MAX);
|
|
||||||
++cursamplep;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,4 +172,9 @@ private:
|
|||||||
F32 mLastSample;
|
F32 mLastSample;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<class T> inline const F32 LLWindGen<T>::getNextSample() { return (F32)rand() * (1.0f / (F32)(RAND_MAX / (U16_MAX / 8))) + (F32)(S16_MIN / 8); }
|
||||||
|
template<> inline const F32 LLWindGen<F32>::getNextSample() { return ll_frand()-.5f; }
|
||||||
|
template<class T> inline const F32 LLWindGen<T>::getClampedSample(bool clamp, F32 sample) { return clamp ? (F32)llclamp((S32)sample,(S32)S16_MIN,(S32)S16_MAX) : sample; }
|
||||||
|
template<> inline const F32 LLWindGen<F32>::getClampedSample(bool clamp, F32 sample) { return sample; }
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -195,15 +195,19 @@ void LLCharacter::requestStopMotion( LLMotion* motion)
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// updateMotions()
|
// updateMotions()
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
static LLFastTimer::DeclareTimer FTM_UPDATE_ANIMATION("Update Animation");
|
||||||
|
static LLFastTimer::DeclareTimer FTM_UPDATE_HIDDEN_ANIMATION("Update Hidden Anim");
|
||||||
|
|
||||||
void LLCharacter::updateMotions(e_update_t update_type)
|
void LLCharacter::updateMotions(e_update_t update_type)
|
||||||
{
|
{
|
||||||
LLFastTimer t(LLFastTimer::FTM_UPDATE_ANIMATION);
|
|
||||||
if (update_type == HIDDEN_UPDATE)
|
if (update_type == HIDDEN_UPDATE)
|
||||||
{
|
{
|
||||||
|
LLFastTimer t(FTM_UPDATE_HIDDEN_ANIMATION);
|
||||||
mMotionController.updateMotionsMinimal();
|
mMotionController.updateMotionsMinimal();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
LLFastTimer t(FTM_UPDATE_ANIMATION);
|
||||||
// unpause if the number of outstanding pause requests has dropped to the initial one
|
// unpause if the number of outstanding pause requests has dropped to the initial one
|
||||||
if (mMotionController.isPaused() && mPauseRequest->getNumRefs() == 1)
|
if (mMotionController.isPaused() && mPauseRequest->getNumRefs() == 1)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -56,9 +56,7 @@ LLJoint::LLJoint()
|
|||||||
mUpdateXform = TRUE;
|
mUpdateXform = TRUE;
|
||||||
mJointNum = -1;
|
mJointNum = -1;
|
||||||
touch();
|
touch();
|
||||||
#if MESH_ENABLED
|
|
||||||
mResetAfterRestoreOldXform = false;
|
mResetAfterRestoreOldXform = false;
|
||||||
#endif //MESH_ENABLED
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -242,7 +240,6 @@ void LLJoint::setPosition( const LLVector3& pos )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if MESH_ENABLED
|
|
||||||
//--------------------------------------------------------------------
|
//--------------------------------------------------------------------
|
||||||
// setPosition()
|
// setPosition()
|
||||||
//--------------------------------------------------------------------
|
//--------------------------------------------------------------------
|
||||||
@@ -278,7 +275,6 @@ void LLJoint::restoreToDefaultXform( void )
|
|||||||
mXform = mDefaultXform;
|
mXform = mDefaultXform;
|
||||||
setPosition( mXform.getPosition() );
|
setPosition( mXform.getPosition() );
|
||||||
}
|
}
|
||||||
#endif //MESH_ENABLED
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------
|
//--------------------------------------------------------------------
|
||||||
// getWorldPosition()
|
// getWorldPosition()
|
||||||
|
|||||||
@@ -86,19 +86,14 @@ protected:
|
|||||||
|
|
||||||
// explicit transformation members
|
// explicit transformation members
|
||||||
LLXformMatrix mXform;
|
LLXformMatrix mXform;
|
||||||
#if MESH_ENABLED
|
|
||||||
LLXformMatrix mOldXform;
|
LLXformMatrix mOldXform;
|
||||||
LLXformMatrix mDefaultXform;
|
LLXformMatrix mDefaultXform;
|
||||||
|
|
||||||
LLUUID mId;
|
LLUUID mId;
|
||||||
#endif //MESH_ENABLED
|
|
||||||
public:
|
public:
|
||||||
U32 mDirtyFlags;
|
U32 mDirtyFlags;
|
||||||
BOOL mUpdateXform;
|
BOOL mUpdateXform;
|
||||||
|
|
||||||
#if MESH_ENABLED
|
|
||||||
BOOL mResetAfterRestoreOldXform;
|
BOOL mResetAfterRestoreOldXform;
|
||||||
#endif //MESH_ENABLED
|
|
||||||
|
|
||||||
// describes the skin binding pose
|
// describes the skin binding pose
|
||||||
LLVector3 mSkinOffset;
|
LLVector3 mSkinOffset;
|
||||||
@@ -188,8 +183,6 @@ public:
|
|||||||
S32 getJointNum() const { return mJointNum; }
|
S32 getJointNum() const { return mJointNum; }
|
||||||
void setJointNum(S32 joint_num) { mJointNum = joint_num; }
|
void setJointNum(S32 joint_num) { mJointNum = joint_num; }
|
||||||
|
|
||||||
#if MESH_ENABLED
|
|
||||||
|
|
||||||
void restoreOldXform( void );
|
void restoreOldXform( void );
|
||||||
void restoreToDefaultXform( void );
|
void restoreToDefaultXform( void );
|
||||||
void setDefaultFromCurrentXform( void );
|
void setDefaultFromCurrentXform( void );
|
||||||
@@ -204,7 +197,6 @@ public:
|
|||||||
const BOOL doesJointNeedToBeReset( void ) const { return mResetAfterRestoreOldXform; }
|
const BOOL doesJointNeedToBeReset( void ) const { return mResetAfterRestoreOldXform; }
|
||||||
//Setter for joint reset flag
|
//Setter for joint reset flag
|
||||||
void setJointToBeReset( BOOL val ) { mResetAfterRestoreOldXform = val; }
|
void setJointToBeReset( BOOL val ) { mResetAfterRestoreOldXform = val; }
|
||||||
#endif //MESH_ENABLED
|
|
||||||
|
|
||||||
// <edit>
|
// <edit>
|
||||||
std::string exportString(U32 tabs = 0);
|
std::string exportString(U32 tabs = 0);
|
||||||
|
|||||||
@@ -637,9 +637,9 @@ void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_ty
|
|||||||
motionp->fadeIn();
|
motionp->fadeIn();
|
||||||
}
|
}
|
||||||
|
|
||||||
//**********************
|
// **********************
|
||||||
// MOTION INACTIVE
|
// MOTION INACTIVE
|
||||||
//**********************
|
// **********************
|
||||||
if (motionp->isStopped() && mAnimTime > motionp->getStopTime() + motionp->getEaseOutDuration())
|
if (motionp->isStopped() && mAnimTime > motionp->getStopTime() + motionp->getEaseOutDuration())
|
||||||
{
|
{
|
||||||
// this motion has gone on too long, deactivate it
|
// this motion has gone on too long, deactivate it
|
||||||
@@ -659,9 +659,9 @@ void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_ty
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//**********************
|
// **********************
|
||||||
// MOTION EASE OUT
|
// MOTION EASE OUT
|
||||||
//**********************
|
// **********************
|
||||||
else if (motionp->isStopped() && mAnimTime > motionp->getStopTime())
|
else if (motionp->isStopped() && mAnimTime > motionp->getStopTime())
|
||||||
{
|
{
|
||||||
// is this the first iteration in the ease out phase?
|
// is this the first iteration in the ease out phase?
|
||||||
@@ -684,9 +684,9 @@ void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_ty
|
|||||||
update_result = motionp->onUpdate(mAnimTime - motionp->mActivationTimestamp, last_joint_signature);
|
update_result = motionp->onUpdate(mAnimTime - motionp->mActivationTimestamp, last_joint_signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
//**********************
|
// **********************
|
||||||
// MOTION ACTIVE
|
// MOTION ACTIVE
|
||||||
//**********************
|
// **********************
|
||||||
else if (mAnimTime > motionp->mActivationTimestamp + motionp->getEaseInDuration())
|
else if (mAnimTime > motionp->mActivationTimestamp + motionp->getEaseInDuration())
|
||||||
{
|
{
|
||||||
posep->setWeight(motionp->getFadeWeight());
|
posep->setWeight(motionp->getFadeWeight());
|
||||||
@@ -707,9 +707,9 @@ void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_ty
|
|||||||
update_result = motionp->onUpdate(mAnimTime - motionp->mActivationTimestamp, last_joint_signature);
|
update_result = motionp->onUpdate(mAnimTime - motionp->mActivationTimestamp, last_joint_signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
//**********************
|
// **********************
|
||||||
// MOTION EASE IN
|
// MOTION EASE IN
|
||||||
//**********************
|
// **********************
|
||||||
else if (mAnimTime >= motionp->mActivationTimestamp)
|
else if (mAnimTime >= motionp->mActivationTimestamp)
|
||||||
{
|
{
|
||||||
if (mLastTime < motionp->mActivationTimestamp)
|
if (mLastTime < motionp->mActivationTimestamp)
|
||||||
|
|||||||
@@ -15,8 +15,11 @@ include_directories(
|
|||||||
)
|
)
|
||||||
|
|
||||||
set(llcommon_SOURCE_FILES
|
set(llcommon_SOURCE_FILES
|
||||||
|
aiframetimer.cpp
|
||||||
imageids.cpp
|
imageids.cpp
|
||||||
indra_constants.cpp
|
indra_constants.cpp
|
||||||
|
llallocator.cpp
|
||||||
|
llallocator_heap_profile.cpp
|
||||||
llapp.cpp
|
llapp.cpp
|
||||||
llapr.cpp
|
llapr.cpp
|
||||||
llaprpool.cpp
|
llaprpool.cpp
|
||||||
@@ -26,16 +29,23 @@ set(llcommon_SOURCE_FILES
|
|||||||
llbase64.cpp
|
llbase64.cpp
|
||||||
llcommon.cpp
|
llcommon.cpp
|
||||||
llcommonutils.cpp
|
llcommonutils.cpp
|
||||||
|
llcoros.cpp
|
||||||
llcrc.cpp
|
llcrc.cpp
|
||||||
llcriticaldamp.cpp
|
llcriticaldamp.cpp
|
||||||
llcursortypes.cpp
|
llcursortypes.cpp
|
||||||
lldate.cpp
|
lldate.cpp
|
||||||
|
lldependencies.cpp
|
||||||
lldictionary.cpp
|
lldictionary.cpp
|
||||||
llerror.cpp
|
llerror.cpp
|
||||||
llerrorthread.cpp
|
llerrorthread.cpp
|
||||||
llevent.cpp
|
llevent.cpp
|
||||||
|
lleventapi.cpp
|
||||||
|
lleventcoro.cpp
|
||||||
|
lleventdispatcher.cpp
|
||||||
|
lleventfilter.cpp
|
||||||
|
llevents.cpp
|
||||||
lleventtimer.cpp
|
lleventtimer.cpp
|
||||||
llfasttimer.cpp
|
llfasttimer_class.cpp
|
||||||
llfile.cpp
|
llfile.cpp
|
||||||
llfindlocale.cpp
|
llfindlocale.cpp
|
||||||
llfixedbuffer.cpp
|
llfixedbuffer.cpp
|
||||||
@@ -51,9 +61,11 @@ set(llcommon_SOURCE_FILES
|
|||||||
llmd5.cpp
|
llmd5.cpp
|
||||||
llmemory.cpp
|
llmemory.cpp
|
||||||
llmemorystream.cpp
|
llmemorystream.cpp
|
||||||
|
llmemtype.cpp
|
||||||
llmetrics.cpp
|
llmetrics.cpp
|
||||||
llmortician.cpp
|
llmortician.cpp
|
||||||
lloptioninterface.cpp
|
lloptioninterface.cpp
|
||||||
|
llptrto.cpp
|
||||||
llprocesslauncher.cpp
|
llprocesslauncher.cpp
|
||||||
llprocessor.cpp
|
llprocessor.cpp
|
||||||
llqueuedthread.cpp
|
llqueuedthread.cpp
|
||||||
@@ -79,6 +91,7 @@ set(llcommon_SOURCE_FILES
|
|||||||
lluri.cpp
|
lluri.cpp
|
||||||
lluuid.cpp
|
lluuid.cpp
|
||||||
llworkerthread.cpp
|
llworkerthread.cpp
|
||||||
|
ll_template_cast.h
|
||||||
metaclass.cpp
|
metaclass.cpp
|
||||||
metaproperty.cpp
|
metaproperty.cpp
|
||||||
reflective.cpp
|
reflective.cpp
|
||||||
@@ -89,6 +102,7 @@ set(llcommon_SOURCE_FILES
|
|||||||
set(llcommon_HEADER_FILES
|
set(llcommon_HEADER_FILES
|
||||||
CMakeLists.txt
|
CMakeLists.txt
|
||||||
|
|
||||||
|
aiframetimer.h
|
||||||
aithreadsafe.h
|
aithreadsafe.h
|
||||||
bitpack.h
|
bitpack.h
|
||||||
ctype_workaround.h
|
ctype_workaround.h
|
||||||
@@ -98,6 +112,8 @@ set(llcommon_HEADER_FILES
|
|||||||
linden_common.h
|
linden_common.h
|
||||||
linked_lists.h
|
linked_lists.h
|
||||||
llaccountingcost.h
|
llaccountingcost.h
|
||||||
|
llallocator.h
|
||||||
|
llallocator_heap_profile.h
|
||||||
llagentconstants.h
|
llagentconstants.h
|
||||||
llavatarname.h
|
llavatarname.h
|
||||||
llapp.h
|
llapp.h
|
||||||
@@ -113,6 +129,7 @@ set(llcommon_HEADER_FILES
|
|||||||
llclickaction.h
|
llclickaction.h
|
||||||
llcommon.h
|
llcommon.h
|
||||||
llcommonutils.h
|
llcommonutils.h
|
||||||
|
llcoros.h
|
||||||
llcrc.h
|
llcrc.h
|
||||||
llcriticaldamp.h
|
llcriticaldamp.h
|
||||||
llcursortypes.h
|
llcursortypes.h
|
||||||
@@ -120,6 +137,7 @@ set(llcommon_HEADER_FILES
|
|||||||
lldarrayptr.h
|
lldarrayptr.h
|
||||||
lldate.h
|
lldate.h
|
||||||
lldefs.h
|
lldefs.h
|
||||||
|
lldependencies.h
|
||||||
lldeleteutils.h
|
lldeleteutils.h
|
||||||
lldepthstack.h
|
lldepthstack.h
|
||||||
lldictionary.h
|
lldictionary.h
|
||||||
@@ -132,10 +150,16 @@ set(llcommon_HEADER_FILES
|
|||||||
llerrorlegacy.h
|
llerrorlegacy.h
|
||||||
llerrorthread.h
|
llerrorthread.h
|
||||||
llevent.h
|
llevent.h
|
||||||
|
lleventapi.h
|
||||||
|
lleventcoro.h
|
||||||
|
lleventdispatcher.h
|
||||||
|
lleventfilter.h
|
||||||
|
llevents.h
|
||||||
lleventemitter.h
|
lleventemitter.h
|
||||||
llextendedstatus.h
|
llextendedstatus.h
|
||||||
lleventtimer.h
|
lleventtimer.h
|
||||||
llfasttimer.h
|
llfasttimer.h
|
||||||
|
llfasttimer_class.h
|
||||||
llfile.h
|
llfile.h
|
||||||
llfindlocale.h
|
llfindlocale.h
|
||||||
llfixedbuffer.h
|
llfixedbuffer.h
|
||||||
@@ -171,6 +195,7 @@ set(llcommon_HEADER_FILES
|
|||||||
llprocessor.h
|
llprocessor.h
|
||||||
llptrskiplist.h
|
llptrskiplist.h
|
||||||
llptrskipmap.h
|
llptrskipmap.h
|
||||||
|
llptrto.h
|
||||||
llqueuedthread.h
|
llqueuedthread.h
|
||||||
llrand.h
|
llrand.h
|
||||||
llrefcount.h
|
llrefcount.h
|
||||||
@@ -203,8 +228,7 @@ set(llcommon_HEADER_FILES
|
|||||||
lluri.h
|
lluri.h
|
||||||
lluuid.h
|
lluuid.h
|
||||||
lluuidhashmap.h
|
lluuidhashmap.h
|
||||||
llversionserver.h
|
llversionviewer.h.in
|
||||||
llversionviewer.h
|
|
||||||
llworkerthread.h
|
llworkerthread.h
|
||||||
metaclass.h
|
metaclass.h
|
||||||
metaclasst.h
|
metaclasst.h
|
||||||
|
|||||||
180
indra/llcommon/aiframetimer.cpp
Normal file
180
indra/llcommon/aiframetimer.cpp
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
/**
|
||||||
|
* @file aiframetimer.cpp
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011, Aleric Inglewood.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* There are special exceptions to the terms and conditions of the GPL as
|
||||||
|
* it is applied to this Source Code. View the full text of the exception
|
||||||
|
* in the file doc/FLOSS-exception.txt in this software distribution.
|
||||||
|
*
|
||||||
|
* CHANGELOG
|
||||||
|
* and additional copyright holders.
|
||||||
|
*
|
||||||
|
* 06/08/2011
|
||||||
|
* - Initial version, written by Aleric Inglewood @ SL
|
||||||
|
*/
|
||||||
|
|
||||||
|
// An AIFrameTimer object provides a callback API for timer events.
|
||||||
|
//
|
||||||
|
// Typical usage:
|
||||||
|
//
|
||||||
|
// // Any thread.
|
||||||
|
// AIFrameTimer timer;
|
||||||
|
//
|
||||||
|
// ...
|
||||||
|
// // Any thread (after successful construction is guaranteed).
|
||||||
|
// timer.create(5.5, boost::bind(&the_callback, <optional params>)); // Call the_callback(<optional params>) in 5.5 seconds.
|
||||||
|
//
|
||||||
|
// The callback function is always called by the main thread and should therefore
|
||||||
|
// be light weight.
|
||||||
|
//
|
||||||
|
// If timer.cancel() is called before the timer expires, then the callback
|
||||||
|
// function isn't called. If cancel() is called by another thread than the
|
||||||
|
// main thread, then it is possible that the callback function is called
|
||||||
|
// even while still inside cancel(), but as soon as cancel() returned it
|
||||||
|
// is guarenteed that the callback function won't be called anymore.
|
||||||
|
// Hence, if the callback function is a member of some object then
|
||||||
|
// cancel() must be called before the destruction of that object (ie from
|
||||||
|
// it's destructor). Calling cancel() multiple times is not a problem.
|
||||||
|
// Note: if cancel() is called while the callback function is being
|
||||||
|
// called then cancel() will block until the callback function returned.
|
||||||
|
//
|
||||||
|
// The timer object can be reused (by calling create() again), but
|
||||||
|
// only after either the callback function was called, or after cancel()
|
||||||
|
// returned. Most notably, you can call create() again from inside the
|
||||||
|
// callback function to "restart" the timer.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "linden_common.h"
|
||||||
|
#include "aiframetimer.h"
|
||||||
|
|
||||||
|
static F64 const NEVER = 1e16; // 317 million years.
|
||||||
|
|
||||||
|
F64 AIFrameTimer::sNextExpiration;
|
||||||
|
AIFrameTimer::timer_list_type AIFrameTimer::sTimerList;
|
||||||
|
LLMutex AIFrameTimer::sMutex;
|
||||||
|
|
||||||
|
// Notes on thread-safety of AIRunningFrameTimer (continued from aiframetimer.h)
|
||||||
|
//
|
||||||
|
// Most notably, the constructor and init() should be called as follows:
|
||||||
|
// 1) The object is constructed (AIRunningFrameTimer::AIRunningFrameTimer).
|
||||||
|
// 2) The lock is obtained.
|
||||||
|
// 3) The object is inserted in the list (operator<(AIRunningFrameTimer const&, AIRunningFrameTimer const&)).
|
||||||
|
// 4) The object is initialized (AIRunningFrameTimer::init).
|
||||||
|
// 5) The lock is released.
|
||||||
|
// This assures that the object is not yet shared at the moment that it is initialized.
|
||||||
|
|
||||||
|
void AIFrameTimer::create(F64 expiration, signal_type::slot_type const& slot)
|
||||||
|
{
|
||||||
|
AIRunningFrameTimer new_timer(expiration, this);
|
||||||
|
sMutex.lock();
|
||||||
|
llassert(mHandle.mRunningTimer == sTimerList.end()); // Create may only be called when the timer isn't already running.
|
||||||
|
mHandle.init(sTimerList.insert(new_timer), slot);
|
||||||
|
sNextExpiration = sTimerList.begin()->expiration();
|
||||||
|
sMutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AIFrameTimer::cancel(void)
|
||||||
|
{
|
||||||
|
// In order to stop us from returning from cancel() while
|
||||||
|
// the callback function is being called (which is done
|
||||||
|
// in AIFrameTimer::handleExpiration after obtaining the
|
||||||
|
// mHandle.mMutex lock), we start with trying to obtain
|
||||||
|
// it here and as such wait till the callback function
|
||||||
|
// returned.
|
||||||
|
mHandle.mMutex.lock();
|
||||||
|
// Next we have to grab this lock in order to stop
|
||||||
|
// AIFrameTimer::handleExpiration from even entering
|
||||||
|
// in the case we manage to get it first.
|
||||||
|
sMutex.lock();
|
||||||
|
if (mHandle.mRunningTimer != sTimerList.end())
|
||||||
|
{
|
||||||
|
sTimerList.erase(mHandle.mRunningTimer);
|
||||||
|
mHandle.mRunningTimer = sTimerList.end();
|
||||||
|
sNextExpiration = sTimerList.empty() ? NEVER : sTimerList.begin()->expiration();
|
||||||
|
}
|
||||||
|
sMutex.unlock();
|
||||||
|
mHandle.mMutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AIFrameTimer::handleExpiration(F64 current_frame_time)
|
||||||
|
{
|
||||||
|
sMutex.lock();
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
if (sTimerList.empty())
|
||||||
|
{
|
||||||
|
// No running timers left.
|
||||||
|
sNextExpiration = NEVER;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
timer_list_type::iterator running_timer = sTimerList.begin();
|
||||||
|
sNextExpiration = running_timer->expiration();
|
||||||
|
if (sNextExpiration > current_frame_time)
|
||||||
|
{
|
||||||
|
// Didn't expire yet.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtain handle of running timer through the associated AIFrameTimer object.
|
||||||
|
// Note that if the AIFrameTimer object was destructed (when running_timer->getTimer()
|
||||||
|
// would return an invalid pointer) then it called cancel(), so we can't be here.
|
||||||
|
Handle& handle(running_timer->getTimer()->mHandle);
|
||||||
|
llassert_always(running_timer == handle.mRunningTimer);
|
||||||
|
|
||||||
|
// We're going to erase this timer, so stop cancel() from doing the same.
|
||||||
|
handle.mRunningTimer = sTimerList.end();
|
||||||
|
|
||||||
|
// We keep handle.mMutex during the callback to prevent the thread that
|
||||||
|
// owns the AIFrameTimer from deleting the callback function while we
|
||||||
|
// call it: in order to do so it first has to call cancel(), which will
|
||||||
|
// block until we release this mutex again, or we won't call the callback
|
||||||
|
// function here because the trylock fails.
|
||||||
|
//
|
||||||
|
// Assuming the main thread arrived here, there are two possible states
|
||||||
|
// for the other thread that tries to delete the callback function,
|
||||||
|
// right after calling the cancel() function too:
|
||||||
|
//
|
||||||
|
// 1. It hasn't obtained the first lock yet, we obtain the handle.mMutex
|
||||||
|
// lock and the other thread will stall on the first line of cancel().
|
||||||
|
// After do_callback returns, the other thread will do nothing because
|
||||||
|
// handle.mRunningTimer equals sTimerList.end(), exit the function and
|
||||||
|
// (possibly) delete the callback object, but that is ok as we already
|
||||||
|
// returned from the callback function.
|
||||||
|
//
|
||||||
|
// 2. It already called cancel() and hangs on the second line trying to
|
||||||
|
// obtain sMutex.lock(). The trylock below fails and we never call the
|
||||||
|
// callback function. We erase the running timer here and release sMutex
|
||||||
|
// at the end, after which the other thread does nothing because
|
||||||
|
// handle.mRunningTimer equals sTimerList.end(), exits the function and
|
||||||
|
// (possibly) deletes the callback object.
|
||||||
|
//
|
||||||
|
// Note that if the other thread actually obtained the sMutex then we
|
||||||
|
// can't be here: this is still inside the critical area of sMutex.
|
||||||
|
if (handle.mMutex.tryLock()) // If this fails then another thread is in the process of cancelling this timer, so do nothing.
|
||||||
|
{
|
||||||
|
sMutex.unlock();
|
||||||
|
running_timer->do_callback(); // May not throw exceptions.
|
||||||
|
sMutex.lock();
|
||||||
|
handle.mMutex.unlock(); // Allow other thread to return from cancel() and possibly delete the callback object.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erase the timer from the running list.
|
||||||
|
sTimerList.erase(running_timer);
|
||||||
|
}
|
||||||
|
sMutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
155
indra/llcommon/aiframetimer.h
Normal file
155
indra/llcommon/aiframetimer.h
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
/**
|
||||||
|
* @file aiframetimer.h
|
||||||
|
* @brief Implementation of AIFrameTimer.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011, Aleric Inglewood.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* There are special exceptions to the terms and conditions of the GPL as
|
||||||
|
* it is applied to this Source Code. View the full text of the exception
|
||||||
|
* in the file doc/FLOSS-exception.txt in this software distribution.
|
||||||
|
*
|
||||||
|
* CHANGELOG
|
||||||
|
* and additional copyright holders.
|
||||||
|
*
|
||||||
|
* 05/08/2011
|
||||||
|
* Initial version, written by Aleric Inglewood @ SL
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AIFRAMETIMER_H
|
||||||
|
#define AIFRAMETIMER_H
|
||||||
|
|
||||||
|
#include "llframetimer.h"
|
||||||
|
#include "llthread.h"
|
||||||
|
#include <boost/signals2.hpp>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
class LL_COMMON_API AIFrameTimer
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
typedef boost::signals2::signal<void (void)> signal_type;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Use separate struct for this object because it is non-copyable.
|
||||||
|
struct Signal {
|
||||||
|
signal_type mSignal;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Notes on Thread-Safety
|
||||||
|
//
|
||||||
|
// This is the type of the objects stored in AIFrameTimer::sTimerList, and as such leans
|
||||||
|
// for it's thread-safety on the same lock as is used for that std::multiset as follows.
|
||||||
|
// An arbitrary thread can create, insert and initialize this object. Other threads can
|
||||||
|
// not access it until that has completed.
|
||||||
|
//
|
||||||
|
// After creation two threads can access it: the thread that created it (owns the
|
||||||
|
// AIFrameTimer object, which has an mHandle that points to this object), or the main
|
||||||
|
// thread by finding it in sTimerList.
|
||||||
|
//
|
||||||
|
// See aiframetimer.cpp for more notes.
|
||||||
|
class AIRunningFrameTimer {
|
||||||
|
private:
|
||||||
|
F64 mExpire; // Time at which the timer expires, in seconds since application start (compared to LLFrameTimer::sFrameTime).
|
||||||
|
AIFrameTimer* mTimer; // The actual timer.
|
||||||
|
// Can be mutable, since only the mExpire is used for ordering this object in the multiset AIFrameTimer::sTimerList.
|
||||||
|
mutable Signal* mCallback; // Pointer to callback struct, or NULL when the object wasn't added to sTimerList yet.
|
||||||
|
|
||||||
|
public:
|
||||||
|
AIRunningFrameTimer(F64 expiration, AIFrameTimer* timer) : mExpire(LLFrameTimer::getElapsedSeconds() + expiration), mCallback(NULL), mTimer(timer) { }
|
||||||
|
~AIRunningFrameTimer() { delete mCallback; }
|
||||||
|
|
||||||
|
// This function is called after the final object was added to sTimerList (where it is initialized in-place).
|
||||||
|
void init(signal_type::slot_type const& slot) const
|
||||||
|
{
|
||||||
|
// We may only call init() once.
|
||||||
|
llassert(!mCallback);
|
||||||
|
mCallback = new Signal;
|
||||||
|
mCallback->mSignal.connect(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Order AIFrameTimer::sTimerList so that the timer that expires first is up front.
|
||||||
|
friend bool operator<(AIRunningFrameTimer const& ft1, AIRunningFrameTimer const& ft2) { return ft1.mExpire < ft2.mExpire; }
|
||||||
|
|
||||||
|
void do_callback(void) const { mCallback->mSignal(); }
|
||||||
|
F64 expiration(void) const { return mExpire; }
|
||||||
|
AIFrameTimer* getTimer(void) const { return mTimer; }
|
||||||
|
|
||||||
|
#if LL_DEBUG
|
||||||
|
// May not copy this object after it was initialized.
|
||||||
|
AIRunningFrameTimer(AIRunningFrameTimer const& running_frame_timer) :
|
||||||
|
mExpire(running_frame_timer.mExpire), mCallback(running_frame_timer.mCallback), mTimer(running_frame_timer.mTimer)
|
||||||
|
{ llassert(!mCallback); }
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::multiset<AIRunningFrameTimer> timer_list_type;
|
||||||
|
|
||||||
|
static LLMutex sMutex; // Mutex for the two global variables below.
|
||||||
|
static timer_list_type sTimerList; // List with all running timers.
|
||||||
|
static F64 sNextExpiration; // Cache of smallest value in sTimerList.
|
||||||
|
friend class LLFrameTimer; // Access to sNextExpiration.
|
||||||
|
|
||||||
|
class Handle {
|
||||||
|
public:
|
||||||
|
timer_list_type::iterator mRunningTimer; // Points to the running timer, or to sTimerList.end() when not running.
|
||||||
|
// Access to this iterator is protected by the AIFrameTimer::sMutex!
|
||||||
|
LLMutex mMutex; // A mutex used to protect us from deletion of the callback object while
|
||||||
|
// calling the callback function in the case of simultaneous expiration
|
||||||
|
// and cancellation by the thread owning the AIFrameTimer (by calling
|
||||||
|
// AIFrameTimer::cancel).
|
||||||
|
|
||||||
|
// Constructor for a not-running timer.
|
||||||
|
Handle(void) : mRunningTimer(sTimerList.end()) { }
|
||||||
|
|
||||||
|
// Actual initialization used by AIFrameTimer::create.
|
||||||
|
void init(timer_list_type::iterator const& running_timer, signal_type::slot_type const& slot)
|
||||||
|
{
|
||||||
|
// Locking AIFrameTimer::sMutex is not neccessary here, because we're creating
|
||||||
|
// the object and no other thread knows of mRunningTimer at this point.
|
||||||
|
mRunningTimer = running_timer;
|
||||||
|
mRunningTimer->init(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// LLMutex has no assignment operator.
|
||||||
|
Handle& operator=(Handle const&) { return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
|
Handle mHandle;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Construct an AIFrameTimer that is not running.
|
||||||
|
AIFrameTimer(void) { }
|
||||||
|
|
||||||
|
// Construction of a running AIFrameTimer with expiration time expiration in seconds, and callback slot.
|
||||||
|
AIFrameTimer(F64 expiration, signal_type::slot_type const& slot) { create(expiration, slot); }
|
||||||
|
|
||||||
|
// Destructing the AIFrameTimer object terminates the running timer (if still running).
|
||||||
|
// Note that cancel() must have returned BEFORE anything is destructed that would disallow the callback function to be called.
|
||||||
|
// So, if the AIFrameTimer is a member of an object whose callback function is called then cancel() has
|
||||||
|
// to be called manually (or from the destructor of THAT object), before that object is destructed.
|
||||||
|
// Cancel may be called multiple times.
|
||||||
|
~AIFrameTimer() { cancel(); }
|
||||||
|
|
||||||
|
void create(F64 expiration, signal_type::slot_type const& slot);
|
||||||
|
void cancel(void);
|
||||||
|
|
||||||
|
bool isRunning(void) const { bool running; sMutex.lock(); running = mHandle.mRunningTimer != sTimerList.end(); sMutex.unlock(); return running; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void handleExpiration(F64 current_frame_time);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -124,7 +124,7 @@ public:
|
|||||||
|
|
||||||
// NOTE: This next two funtions are only included here
|
// NOTE: This next two funtions are only included here
|
||||||
// for those too familiar with the LLLinkedList template class.
|
// for those too familiar with the LLLinkedList template class.
|
||||||
// They are depreciated. resetList() is unecessary while
|
// They are deprecated. resetList() is unecessary while
|
||||||
// getCurrentData() is identical to getNextData() and has
|
// getCurrentData() is identical to getNextData() and has
|
||||||
// a misleading name.
|
// a misleading name.
|
||||||
//
|
//
|
||||||
@@ -604,7 +604,7 @@ BOOL LLDoubleLinkedList<DATA_TYPE>::checkData(const DATA_TYPE *data)
|
|||||||
|
|
||||||
// NOTE: This next two funtions are only included here
|
// NOTE: This next two funtions are only included here
|
||||||
// for those too familiar with the LLLinkedList template class.
|
// for those too familiar with the LLLinkedList template class.
|
||||||
// They are depreciated. resetList() is unecessary while
|
// They are deprecated. resetList() is unecessary while
|
||||||
// getCurrentData() is identical to getNextData() and has
|
// getCurrentData() is identical to getNextData() and has
|
||||||
// a misleading name.
|
// a misleading name.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -153,11 +153,6 @@ const char LAND_LAYER_CODE = 'L';
|
|||||||
const char WATER_LAYER_CODE = 'W';
|
const char WATER_LAYER_CODE = 'W';
|
||||||
const char WIND_LAYER_CODE = '7';
|
const char WIND_LAYER_CODE = '7';
|
||||||
const char CLOUD_LAYER_CODE = '8';
|
const char CLOUD_LAYER_CODE = '8';
|
||||||
// Extended land layer for Aurora Sim
|
|
||||||
const char AURORA_LAND_LAYER_CODE = 'M';
|
|
||||||
const char AURORA_WATER_LAYER_CODE = 'X';
|
|
||||||
const char AURORA_WIND_LAYER_CODE = '9';
|
|
||||||
const char AURORA_CLOUD_LAYER_CODE = ':';
|
|
||||||
|
|
||||||
// keys
|
// keys
|
||||||
// Bit masks for various keyboard modifier keys.
|
// Bit masks for various keyboard modifier keys.
|
||||||
|
|||||||
177
indra/llcommon/ll_template_cast.h
Normal file
177
indra/llcommon/ll_template_cast.h
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
/**
|
||||||
|
* @file ll_template_cast.h
|
||||||
|
* @author Nat Goodspeed
|
||||||
|
* @date 2009-11-21
|
||||||
|
* @brief Define ll_template_cast function
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation;
|
||||||
|
* version 2.1 of the License only.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if ! defined(LL_LL_TEMPLATE_CAST_H)
|
||||||
|
#define LL_LL_TEMPLATE_CAST_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation for ll_template_cast() (q.v.).
|
||||||
|
*
|
||||||
|
* Default implementation: trying to cast two completely unrelated types
|
||||||
|
* returns 0. Typically you'd specify T and U as pointer types, but in fact T
|
||||||
|
* can be any type that can be initialized with 0.
|
||||||
|
*/
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ll_template_cast_impl
|
||||||
|
{
|
||||||
|
T operator()(U)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ll_template_cast<T>(some_value) is for use in a template function when
|
||||||
|
* some_value might be of arbitrary type, but you want to recognize type T
|
||||||
|
* specially.
|
||||||
|
*
|
||||||
|
* It's designed for use with pointer types. Example:
|
||||||
|
* @code
|
||||||
|
* struct SpecialClass
|
||||||
|
* {
|
||||||
|
* void someMethod(const std::string&) const;
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* template <class REALCLASS>
|
||||||
|
* void somefunc(const REALCLASS& instance)
|
||||||
|
* {
|
||||||
|
* const SpecialClass* ptr = ll_template_cast<const SpecialClass*>(&instance);
|
||||||
|
* if (ptr)
|
||||||
|
* {
|
||||||
|
* ptr->someMethod("Call method only available on SpecialClass");
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Why is this better than dynamic_cast<>? Because unless OtherClass is
|
||||||
|
* polymorphic, the following won't even compile (gcc 4.0.1):
|
||||||
|
* @code
|
||||||
|
* OtherClass other;
|
||||||
|
* SpecialClass* ptr = dynamic_cast<SpecialClass*>(&other);
|
||||||
|
* @endcode
|
||||||
|
* to say nothing of this:
|
||||||
|
* @code
|
||||||
|
* void function(int);
|
||||||
|
* SpecialClass* ptr = dynamic_cast<SpecialClass*>(&function);
|
||||||
|
* @endcode
|
||||||
|
* ll_template_cast handles these kinds of cases by returning 0.
|
||||||
|
*/
|
||||||
|
template <typename T, typename U>
|
||||||
|
T ll_template_cast(U value)
|
||||||
|
{
|
||||||
|
return ll_template_cast_impl<T, U>()(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation for ll_template_cast() (q.v.).
|
||||||
|
*
|
||||||
|
* Implementation for identical types: return same value.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
struct ll_template_cast_impl<T, T>
|
||||||
|
{
|
||||||
|
T operator()(T value)
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LL_TEMPLATE_CONVERTIBLE(dest, source) asserts that, for a value @c s of
|
||||||
|
* type @c source, <tt>ll_template_cast<dest>(s)</tt> will return @c s --
|
||||||
|
* presuming that @c source can be converted to @c dest by the normal rules of
|
||||||
|
* C++.
|
||||||
|
*
|
||||||
|
* By default, <tt>ll_template_cast<dest>(s)</tt> will return 0 unless @c s's
|
||||||
|
* type is literally identical to @c dest. (This is because of the
|
||||||
|
* straightforward application of template specialization rules.) That can
|
||||||
|
* lead to surprising results, e.g.:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* Foo myFoo;
|
||||||
|
* const Foo* fooptr = ll_template_cast<const Foo*>(&myFoo);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Here @c fooptr will be 0 because <tt>&myFoo</tt> is of type <tt>Foo*</tt>
|
||||||
|
* -- @em not <tt>const Foo*</tt>. (Declaring <tt>const Foo myFoo;</tt> would
|
||||||
|
* force the compiler to do the right thing.)
|
||||||
|
*
|
||||||
|
* More disappointingly:
|
||||||
|
* @code
|
||||||
|
* struct Base {};
|
||||||
|
* struct Subclass: public Base {};
|
||||||
|
* Subclass object;
|
||||||
|
* Base* ptr = ll_template_cast<Base*>(&object);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Here @c ptr will be 0 because <tt>&object</tt> is of type
|
||||||
|
* <tt>Subclass*</tt> rather than <tt>Base*</tt>. We @em want this cast to
|
||||||
|
* succeed, but without our help ll_template_cast can't recognize it.
|
||||||
|
*
|
||||||
|
* The following would suffice:
|
||||||
|
* @code
|
||||||
|
* LL_TEMPLATE_CONVERTIBLE(Base*, Subclass*);
|
||||||
|
* ...
|
||||||
|
* Base* ptr = ll_template_cast<Base*>(&object);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* However, as noted earlier, this is easily fooled:
|
||||||
|
* @code
|
||||||
|
* const Base* ptr = ll_template_cast<const Base*>(&object);
|
||||||
|
* @endcode
|
||||||
|
* would still produce 0 because we haven't yet seen:
|
||||||
|
* @code
|
||||||
|
* LL_TEMPLATE_CONVERTIBLE(const Base*, Subclass*);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @TODO
|
||||||
|
* This macro should use Boost type_traits facilities for stripping and
|
||||||
|
* re-adding @c const and @c volatile qualifiers so that invoking
|
||||||
|
* LL_TEMPLATE_CONVERTIBLE(dest, source) will automatically generate all
|
||||||
|
* permitted permutations. It's really not fair to the coder to require
|
||||||
|
* separate:
|
||||||
|
* @code
|
||||||
|
* LL_TEMPLATE_CONVERTIBLE(Base*, Subclass*);
|
||||||
|
* LL_TEMPLATE_CONVERTIBLE(const Base*, Subclass*);
|
||||||
|
* LL_TEMPLATE_CONVERTIBLE(const Base*, const Subclass*);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* (Naturally we omit <tt>LL_TEMPLATE_CONVERTIBLE(Base*, const Subclass*)</tt>
|
||||||
|
* because that's not permitted by normal C++ assignment anyway.)
|
||||||
|
*/
|
||||||
|
#define LL_TEMPLATE_CONVERTIBLE(DEST, SOURCE) \
|
||||||
|
template <> \
|
||||||
|
struct ll_template_cast_impl<DEST, SOURCE> \
|
||||||
|
{ \
|
||||||
|
DEST operator()(SOURCE wrapper) \
|
||||||
|
{ \
|
||||||
|
return wrapper; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* ! defined(LL_LL_TEMPLATE_CAST_H) */
|
||||||
134
indra/llcommon/llallocator.cpp
Normal file
134
indra/llcommon/llallocator.cpp
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
/**
|
||||||
|
* @file llallocator.cpp
|
||||||
|
* @brief Implementation of the LLAllocator class.
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation;
|
||||||
|
* version 2.1 of the License only.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "linden_common.h"
|
||||||
|
#include "llallocator.h"
|
||||||
|
|
||||||
|
#if LL_USE_TCMALLOC
|
||||||
|
|
||||||
|
#include "google/heap-profiler.h"
|
||||||
|
#include "google/commandlineflags_public.h"
|
||||||
|
|
||||||
|
DECLARE_bool(heap_profile_use_stack_trace);
|
||||||
|
//DECLARE_double(tcmalloc_release_rate);
|
||||||
|
|
||||||
|
// static
|
||||||
|
void LLAllocator::pushMemType(S32 type)
|
||||||
|
{
|
||||||
|
if(isProfiling())
|
||||||
|
{
|
||||||
|
PushMemType(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
S32 LLAllocator::popMemType()
|
||||||
|
{
|
||||||
|
if (isProfiling())
|
||||||
|
{
|
||||||
|
return PopMemType();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLAllocator::setProfilingEnabled(bool should_enable)
|
||||||
|
{
|
||||||
|
// NULL disables dumping to disk
|
||||||
|
static char const * const PREFIX = NULL;
|
||||||
|
if(should_enable)
|
||||||
|
{
|
||||||
|
HeapProfilerSetUseStackTrace(false);
|
||||||
|
HeapProfilerStart(PREFIX);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HeapProfilerStop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
bool LLAllocator::isProfiling()
|
||||||
|
{
|
||||||
|
return IsHeapProfilerRunning();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LLAllocator::getRawProfile()
|
||||||
|
{
|
||||||
|
// *TODO - fix google-perftools to accept an buffer to avoid this
|
||||||
|
// malloc-copy-free cycle.
|
||||||
|
char * buffer = GetHeapProfile();
|
||||||
|
std::string ret = buffer;
|
||||||
|
free(buffer);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // LL_USE_TCMALLOC
|
||||||
|
|
||||||
|
//
|
||||||
|
// stub implementations for when tcmalloc is disabled
|
||||||
|
//
|
||||||
|
|
||||||
|
// static
|
||||||
|
void LLAllocator::pushMemType(S32 type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
S32 LLAllocator::popMemType()
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLAllocator::setProfilingEnabled(bool should_enable)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
bool LLAllocator::isProfiling()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LLAllocator::getRawProfile()
|
||||||
|
{
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // LL_USE_TCMALLOC
|
||||||
|
|
||||||
|
LLAllocatorHeapProfile const & LLAllocator::getProfile()
|
||||||
|
{
|
||||||
|
mProf.mLines.clear();
|
||||||
|
|
||||||
|
// *TODO - avoid making all these extra copies of things...
|
||||||
|
std::string prof_text = getRawProfile();
|
||||||
|
//std::cout << prof_text << std::endl;
|
||||||
|
mProf.parse(prof_text);
|
||||||
|
return mProf;
|
||||||
|
}
|
||||||
57
indra/llcommon/llallocator.h
Normal file
57
indra/llcommon/llallocator.h
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* @file llallocator.h
|
||||||
|
* @brief Declaration of the LLAllocator class.
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation;
|
||||||
|
* version 2.1 of the License only.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LL_LLALLOCATOR_H
|
||||||
|
#define LL_LLALLOCATOR_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "llmemtype.h"
|
||||||
|
#include "llallocator_heap_profile.h"
|
||||||
|
|
||||||
|
class LL_COMMON_API LLAllocator {
|
||||||
|
friend class LLMemoryView;
|
||||||
|
friend class LLMemType;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void pushMemType(S32 type);
|
||||||
|
static S32 popMemType();
|
||||||
|
|
||||||
|
public:
|
||||||
|
void setProfilingEnabled(bool should_enable);
|
||||||
|
|
||||||
|
static bool isProfiling();
|
||||||
|
|
||||||
|
LLAllocatorHeapProfile const & getProfile();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string getRawProfile();
|
||||||
|
|
||||||
|
private:
|
||||||
|
LLAllocatorHeapProfile mProf;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LL_LLALLOCATOR_H
|
||||||
147
indra/llcommon/llallocator_heap_profile.cpp
Normal file
147
indra/llcommon/llallocator_heap_profile.cpp
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
/**
|
||||||
|
* @file llallocator_heap_profile.cpp
|
||||||
|
* @brief Implementation of the parser for tcmalloc heap profile data.
|
||||||
|
* @author Brad Kittenbrink
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation;
|
||||||
|
* version 2.1 of the License only.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "linden_common.h"
|
||||||
|
#include "llallocator_heap_profile.h"
|
||||||
|
|
||||||
|
#if LL_MSVC
|
||||||
|
// disable warning about boost::lexical_cast returning uninitialized data
|
||||||
|
// when it fails to parse the string
|
||||||
|
#pragma warning (disable:4701)
|
||||||
|
#pragma warning (disable:4702)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/split.hpp>
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
#include <boost/range/iterator_range.hpp>
|
||||||
|
|
||||||
|
static const std::string HEAP_PROFILE_MAGIC_STR = "heap profile:";
|
||||||
|
|
||||||
|
static bool is_separator(char c)
|
||||||
|
{
|
||||||
|
return isspace(c) || c == '[' || c == ']' || c == ':';
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLAllocatorHeapProfile::parse(std::string const & prof_text)
|
||||||
|
{
|
||||||
|
// a typedef for handling a token in the string buffer
|
||||||
|
// it's a begin/end pair of string::const_iterators
|
||||||
|
typedef boost::iterator_range<std::string::const_iterator> range_t;
|
||||||
|
|
||||||
|
mLines.clear();
|
||||||
|
|
||||||
|
if(prof_text.compare(0, HEAP_PROFILE_MAGIC_STR.length(), HEAP_PROFILE_MAGIC_STR) != 0)
|
||||||
|
{
|
||||||
|
// *TODO - determine if there should be some better error state than
|
||||||
|
// mLines being empty. -brad
|
||||||
|
llwarns << "invalid heap profile data passed into parser." << llendl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector< range_t > prof_lines;
|
||||||
|
|
||||||
|
std::string::const_iterator prof_begin = prof_text.begin() + HEAP_PROFILE_MAGIC_STR.length();
|
||||||
|
|
||||||
|
range_t prof_range(prof_begin, prof_text.end());
|
||||||
|
boost::algorithm::split(prof_lines,
|
||||||
|
prof_range,
|
||||||
|
boost::bind(std::equal_to<llwchar>(), '\n', _1));
|
||||||
|
|
||||||
|
std::vector< range_t >::const_iterator i;
|
||||||
|
for(i = prof_lines.begin(); i != prof_lines.end() && !i->empty(); ++i)
|
||||||
|
{
|
||||||
|
range_t const & line_text = *i;
|
||||||
|
|
||||||
|
std::vector<range_t> line_elems;
|
||||||
|
|
||||||
|
boost::algorithm::split(line_elems,
|
||||||
|
line_text,
|
||||||
|
is_separator);
|
||||||
|
|
||||||
|
std::vector< range_t >::iterator j;
|
||||||
|
j = line_elems.begin();
|
||||||
|
|
||||||
|
while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens
|
||||||
|
llassert_always(j != line_elems.end());
|
||||||
|
U32 live_count = boost::lexical_cast<U32>(*j);
|
||||||
|
++j;
|
||||||
|
|
||||||
|
while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens
|
||||||
|
llassert_always(j != line_elems.end());
|
||||||
|
U64 live_size = boost::lexical_cast<U64>(*j);
|
||||||
|
++j;
|
||||||
|
|
||||||
|
while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens
|
||||||
|
llassert_always(j != line_elems.end());
|
||||||
|
U32 tot_count = boost::lexical_cast<U32>(*j);
|
||||||
|
++j;
|
||||||
|
|
||||||
|
while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens
|
||||||
|
llassert_always(j != line_elems.end());
|
||||||
|
U64 tot_size = boost::lexical_cast<U64>(*j);
|
||||||
|
++j;
|
||||||
|
|
||||||
|
while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens
|
||||||
|
llassert(j != line_elems.end());
|
||||||
|
if (j != line_elems.end())
|
||||||
|
{
|
||||||
|
++j; // skip the '@'
|
||||||
|
|
||||||
|
mLines.push_back(line(live_count, live_size, tot_count, tot_size));
|
||||||
|
line & current_line = mLines.back();
|
||||||
|
|
||||||
|
for(; j != line_elems.end(); ++j)
|
||||||
|
{
|
||||||
|
if(!j->empty())
|
||||||
|
{
|
||||||
|
U32 marker = boost::lexical_cast<U32>(*j);
|
||||||
|
current_line.mTrace.push_back(marker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// *TODO - parse MAPPED_LIBRARIES section here if we're ever interested in it
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLAllocatorHeapProfile::dump(std::ostream & out) const
|
||||||
|
{
|
||||||
|
lines_t::const_iterator i;
|
||||||
|
for(i = mLines.begin(); i != mLines.end(); ++i)
|
||||||
|
{
|
||||||
|
out << i->mLiveCount << ": " << i->mLiveSize << '[' << i->mTotalCount << ": " << i->mTotalSize << "] @";
|
||||||
|
|
||||||
|
stack_trace::const_iterator j;
|
||||||
|
for(j = i->mTrace.begin(); j != i->mTrace.end(); ++j)
|
||||||
|
{
|
||||||
|
out << ' ' << *j;
|
||||||
|
}
|
||||||
|
out << '\n';
|
||||||
|
}
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
|
||||||
71
indra/llcommon/llallocator_heap_profile.h
Normal file
71
indra/llcommon/llallocator_heap_profile.h
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* @file llallocator_heap_profile.h
|
||||||
|
* @brief Declaration of the parser for tcmalloc heap profile data.
|
||||||
|
* @author Brad Kittenbrink
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation;
|
||||||
|
* version 2.1 of the License only.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LL_LLALLOCATOR_HEAP_PROFILE_H
|
||||||
|
#define LL_LLALLOCATOR_HEAP_PROFILE_H
|
||||||
|
|
||||||
|
#include "stdtypes.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class LLAllocatorHeapProfile
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef int stack_marker;
|
||||||
|
|
||||||
|
typedef std::vector<stack_marker> stack_trace;
|
||||||
|
|
||||||
|
struct line {
|
||||||
|
line(U32 live_count, U64 live_size, U32 tot_count, U64 tot_size) :
|
||||||
|
mLiveSize(live_size),
|
||||||
|
mTotalSize(tot_size),
|
||||||
|
mLiveCount(live_count),
|
||||||
|
mTotalCount(tot_count)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
U64 mLiveSize, mTotalSize;
|
||||||
|
U32 mLiveCount, mTotalCount;
|
||||||
|
stack_trace mTrace;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::vector<line> lines_t;
|
||||||
|
|
||||||
|
LLAllocatorHeapProfile()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse(std::string const & prof_text);
|
||||||
|
|
||||||
|
void dump(std::ostream & out) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
lines_t mLines;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // LL_LLALLOCATOR_HEAP_PROFILE_H
|
||||||
@@ -401,6 +401,18 @@ bool LLApp::isExiting()
|
|||||||
return isQuitting() || isError();
|
return isQuitting() || isError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LLApp::disableCrashlogger()
|
||||||
|
{
|
||||||
|
// Disable Breakpad exception handler.
|
||||||
|
sDisableCrashlogger = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
bool LLApp::isCrashloggerDisabled()
|
||||||
|
{
|
||||||
|
return (sDisableCrashlogger == TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
#if !LL_WINDOWS
|
#if !LL_WINDOWS
|
||||||
// static
|
// static
|
||||||
U32 LLApp::getSigChildCount()
|
U32 LLApp::getSigChildCount()
|
||||||
@@ -734,7 +746,7 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
|
|||||||
llwarns << "Signal handler - Flagging error status and waiting for shutdown" << llendl;
|
llwarns << "Signal handler - Flagging error status and waiting for shutdown" << llendl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(LLApp::sDisableCrashlogger) //Don't gracefully handle any signals crash and core for a gdb post mortum
|
if (LLApp::isCrashloggerDisabled()) // Don't gracefully handle any signal, crash and core for a gdb post mortem
|
||||||
{
|
{
|
||||||
clear_signals();
|
clear_signals();
|
||||||
llwarns << "Fatal signal received, not handling the crash here, passing back to operating system" << llendl;
|
llwarns << "Fatal signal received, not handling the crash here, passing back to operating system" << llendl;
|
||||||
|
|||||||
@@ -191,6 +191,11 @@ public:
|
|||||||
//
|
//
|
||||||
virtual bool mainLoop() = 0; // Override for the application main loop. Needs to at least gracefully notice the QUITTING state and exit.
|
virtual bool mainLoop() = 0; // Override for the application main loop. Needs to at least gracefully notice the QUITTING state and exit.
|
||||||
|
|
||||||
|
//
|
||||||
|
// Crash logging
|
||||||
|
//
|
||||||
|
void disableCrashlogger(); // Let the OS handle the crashes
|
||||||
|
static bool isCrashloggerDisabled(); // Get the here above set value
|
||||||
|
|
||||||
//
|
//
|
||||||
// Application status
|
// Application status
|
||||||
@@ -214,9 +219,6 @@ public:
|
|||||||
//
|
//
|
||||||
void setErrorHandler(LLAppErrorHandler handler);
|
void setErrorHandler(LLAppErrorHandler handler);
|
||||||
void setSyncErrorHandler(LLAppErrorHandler handler);
|
void setSyncErrorHandler(LLAppErrorHandler handler);
|
||||||
|
|
||||||
static BOOL sDisableCrashlogger; // Let the OS handle crashes for us.
|
|
||||||
|
|
||||||
#if !LL_WINDOWS
|
#if !LL_WINDOWS
|
||||||
//
|
//
|
||||||
// Child process handling (Unix only for now)
|
// Child process handling (Unix only for now)
|
||||||
@@ -245,6 +247,7 @@ protected:
|
|||||||
static void setStatus(EAppStatus status); // Use this to change the application status.
|
static void setStatus(EAppStatus status); // Use this to change the application status.
|
||||||
static EAppStatus sStatus; // Reflects current application status
|
static EAppStatus sStatus; // Reflects current application status
|
||||||
static BOOL sErrorThreadRunning; // Set while the error thread is running
|
static BOOL sErrorThreadRunning; // Set while the error thread is running
|
||||||
|
static BOOL sDisableCrashlogger; // Let the OS handle crashes for us.
|
||||||
|
|
||||||
#if !LL_WINDOWS
|
#if !LL_WINDOWS
|
||||||
static LLAtomicU32* sSigChildCount; // Number of SIGCHLDs received.
|
static LLAtomicU32* sSigChildCount; // Number of SIGCHLDs received.
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ S32 LLAPRFile::seek(apr_seek_where_t where, S32 offset)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
//*******************************************************************************************************************************
|
// *******************************************************************************************************************************
|
||||||
//static components of LLAPRFile
|
//static components of LLAPRFile
|
||||||
//
|
//
|
||||||
|
|
||||||
@@ -504,5 +504,5 @@ bool LLAPRFile::removeDir(const std::string& dirname)
|
|||||||
}
|
}
|
||||||
//
|
//
|
||||||
//end of static components of LLAPRFile
|
//end of static components of LLAPRFile
|
||||||
//*******************************************************************************************************************************
|
// *******************************************************************************************************************************
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ public:
|
|||||||
apr_file_t* getFileHandle() {return mFile;}
|
apr_file_t* getFileHandle() {return mFile;}
|
||||||
|
|
||||||
//
|
//
|
||||||
//*******************************************************************************************************************************
|
// *******************************************************************************************************************************
|
||||||
//static components
|
//static components
|
||||||
//
|
//
|
||||||
private:
|
private:
|
||||||
@@ -185,7 +185,7 @@ public:
|
|||||||
// Returns bytes read/written, 0 if read/write fails:
|
// Returns bytes read/written, 0 if read/write fails:
|
||||||
static S32 readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes);
|
static S32 readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes);
|
||||||
static S32 writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes);
|
static S32 writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes);
|
||||||
//*******************************************************************************************************************************
|
// *******************************************************************************************************************************
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -214,17 +214,16 @@ LLAssetType::EType LLAssetType::lookupHumanReadable(const std::string& readable_
|
|||||||
bool LLAssetType::lookupCanLink(EType asset_type)
|
bool LLAssetType::lookupCanLink(EType asset_type)
|
||||||
{
|
{
|
||||||
//Check that enabling all these other types as linkable doesn't break things.
|
//Check that enabling all these other types as linkable doesn't break things.
|
||||||
/*const LLAssetDictionary *dict = LLAssetDictionary::getInstance();
|
const LLAssetDictionary *dict = LLAssetDictionary::getInstance();
|
||||||
const AssetEntry *entry = dict->lookup(asset_type);
|
const AssetEntry *entry = dict->lookup(asset_type);
|
||||||
if (entry)
|
if (entry)
|
||||||
{
|
{
|
||||||
return entry->mCanLink;
|
return entry->mCanLink;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
*/
|
|
||||||
|
|
||||||
return (asset_type == AT_CLOTHING || asset_type == AT_OBJECT || asset_type == AT_CATEGORY ||
|
/*return (asset_type == AT_CLOTHING || asset_type == AT_OBJECT || asset_type == AT_CATEGORY ||
|
||||||
asset_type == AT_BODYPART || asset_type == AT_GESTURE);
|
asset_type == AT_BODYPART || asset_type == AT_GESTURE);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
|||||||
154
indra/llcommon/llcoros.cpp
Normal file
154
indra/llcommon/llcoros.cpp
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
/**
|
||||||
|
* @file llcoros.cpp
|
||||||
|
* @author Nat Goodspeed
|
||||||
|
* @date 2009-06-03
|
||||||
|
* @brief Implementation for llcoros.
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation;
|
||||||
|
* version 2.1 of the License only.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Precompiled header
|
||||||
|
#include "linden_common.h"
|
||||||
|
// associated header
|
||||||
|
#include "llcoros.h"
|
||||||
|
// STL headers
|
||||||
|
// std headers
|
||||||
|
// external library headers
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
// other Linden headers
|
||||||
|
#include "llevents.h"
|
||||||
|
#include "llerror.h"
|
||||||
|
#include "stringize.h"
|
||||||
|
|
||||||
|
LLCoros::LLCoros()
|
||||||
|
{
|
||||||
|
// Register our cleanup() method for "mainloop" ticks
|
||||||
|
LLEventPumps::instance().obtain("mainloop").listen(
|
||||||
|
"LLCoros", boost::bind(&LLCoros::cleanup, this, _1));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LLCoros::cleanup(const LLSD&)
|
||||||
|
{
|
||||||
|
// Walk the mCoros map, checking and removing completed coroutines.
|
||||||
|
for (CoroMap::iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; )
|
||||||
|
{
|
||||||
|
// Has this coroutine exited (normal return, exception, exit() call)
|
||||||
|
// since last tick?
|
||||||
|
if (mi->second->exited())
|
||||||
|
{
|
||||||
|
LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL;
|
||||||
|
// The erase() call will invalidate its passed iterator value --
|
||||||
|
// so increment mi FIRST -- but pass its original value to
|
||||||
|
// erase(). This is what postincrement is all about.
|
||||||
|
mCoros.erase(mi++);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Still live, just skip this entry as if incrementing at the top
|
||||||
|
// of the loop as usual.
|
||||||
|
++mi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LLCoros::generateDistinctName(const std::string& prefix) const
|
||||||
|
{
|
||||||
|
// Allowing empty name would make getName()'s not-found return ambiguous.
|
||||||
|
if (prefix.empty())
|
||||||
|
{
|
||||||
|
LL_ERRS("LLCoros") << "LLCoros::launch(): pass non-empty name string" << LL_ENDL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the specified name isn't already in the map, just use that.
|
||||||
|
std::string name(prefix);
|
||||||
|
|
||||||
|
// Find the lowest numeric suffix that doesn't collide with an existing
|
||||||
|
// entry. Start with 2 just to make it more intuitive for any interested
|
||||||
|
// parties: e.g. "joe", "joe2", "joe3"...
|
||||||
|
for (int i = 2; ; name = STRINGIZE(prefix << i++))
|
||||||
|
{
|
||||||
|
if (mCoros.find(name) == mCoros.end())
|
||||||
|
{
|
||||||
|
LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL;
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LLCoros::kill(const std::string& name)
|
||||||
|
{
|
||||||
|
CoroMap::iterator found = mCoros.find(name);
|
||||||
|
if (found == mCoros.end())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Because this is a boost::ptr_map, erasing the map entry also destroys
|
||||||
|
// the referenced heap object, in this case the boost::coroutine object,
|
||||||
|
// which will terminate the coroutine.
|
||||||
|
mCoros.erase(found);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LLCoros::getNameByID(const void* self_id) const
|
||||||
|
{
|
||||||
|
// Walk the existing coroutines, looking for one from which the 'self_id'
|
||||||
|
// passed to us comes.
|
||||||
|
for (CoroMap::const_iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; ++mi)
|
||||||
|
{
|
||||||
|
namespace coro_private = boost::coroutines::detail;
|
||||||
|
if (static_cast<void*>(coro_private::coroutine_accessor::get_impl(const_cast<coro&>(*mi->second)).get())
|
||||||
|
== self_id)
|
||||||
|
{
|
||||||
|
return mi->first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* MUST BE LAST
|
||||||
|
*****************************************************************************/
|
||||||
|
// Turn off MSVC optimizations for just LLCoros::launchImpl() -- see
|
||||||
|
// DEV-32777. But MSVC doesn't support push/pop for optimization flags as it
|
||||||
|
// does for warning suppression, and we really don't want to force
|
||||||
|
// optimization ON for other code even in Debug or RelWithDebInfo builds.
|
||||||
|
|
||||||
|
#if LL_MSVC
|
||||||
|
// work around broken optimizations
|
||||||
|
#pragma warning(disable: 4748)
|
||||||
|
#pragma optimize("", off)
|
||||||
|
#endif // LL_MSVC
|
||||||
|
|
||||||
|
std::string LLCoros::launchImpl(const std::string& prefix, coro* newCoro)
|
||||||
|
{
|
||||||
|
std::string name(generateDistinctName(prefix));
|
||||||
|
mCoros.insert(name, newCoro);
|
||||||
|
/* Run the coroutine until its first wait, then return here */
|
||||||
|
(*newCoro)(std::nothrow);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LL_MSVC
|
||||||
|
// reenable optimizations
|
||||||
|
#pragma optimize("", on)
|
||||||
|
#endif // LL_MSVC
|
||||||
166
indra/llcommon/llcoros.h
Normal file
166
indra/llcommon/llcoros.h
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
/**
|
||||||
|
* @file llcoros.h
|
||||||
|
* @author Nat Goodspeed
|
||||||
|
* @date 2009-06-02
|
||||||
|
* @brief Manage running boost::coroutine instances
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation;
|
||||||
|
* version 2.1 of the License only.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if ! defined(LL_LLCOROS_H)
|
||||||
|
#define LL_LLCOROS_H
|
||||||
|
|
||||||
|
#include <boost/coroutine/coroutine.hpp>
|
||||||
|
#include "llsingleton.h"
|
||||||
|
#include <boost/ptr_container/ptr_map.hpp>
|
||||||
|
#include <string>
|
||||||
|
#include <boost/preprocessor/repetition/enum_params.hpp>
|
||||||
|
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
|
||||||
|
#include <boost/preprocessor/iteration/local.hpp>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registry of named Boost.Coroutine instances
|
||||||
|
*
|
||||||
|
* The Boost.Coroutine library supports the general case of a coroutine
|
||||||
|
* accepting arbitrary parameters and yielding multiple (sets of) results. For
|
||||||
|
* such use cases, it's natural for the invoking code to retain the coroutine
|
||||||
|
* instance: the consumer repeatedly calls into the coroutine, perhaps passing
|
||||||
|
* new parameter values, prompting it to yield its next result.
|
||||||
|
*
|
||||||
|
* Our typical coroutine usage is different, though. For us, coroutines
|
||||||
|
* provide an alternative to the @c Responder pattern. Our typical coroutine
|
||||||
|
* has @c void return, invoked in fire-and-forget mode: the handler for some
|
||||||
|
* user gesture launches the coroutine and promptly returns to the main loop.
|
||||||
|
* The coroutine initiates some action that will take multiple frames (e.g. a
|
||||||
|
* capability request), waits for its result, processes it and silently steals
|
||||||
|
* away.
|
||||||
|
*
|
||||||
|
* This usage poses two (related) problems:
|
||||||
|
*
|
||||||
|
* # Who should own the coroutine instance? If it's simply local to the
|
||||||
|
* handler code that launches it, return from the handler will destroy the
|
||||||
|
* coroutine object, terminating the coroutine.
|
||||||
|
* # Once the coroutine terminates, in whatever way, who's responsible for
|
||||||
|
* cleaning up the coroutine object?
|
||||||
|
*
|
||||||
|
* LLCoros is a Singleton collection of currently-active coroutine instances.
|
||||||
|
* Each has a name. You ask LLCoros to launch a new coroutine with a suggested
|
||||||
|
* name prefix; from your prefix it generates a distinct name, registers the
|
||||||
|
* new coroutine and returns the actual name.
|
||||||
|
*
|
||||||
|
* The name can be used to kill off the coroutine prematurely, if needed. It
|
||||||
|
* can also provide diagnostic info: we can look up the name of the
|
||||||
|
* currently-running coroutine.
|
||||||
|
*
|
||||||
|
* Finally, the next frame ("mainloop" event) after the coroutine terminates,
|
||||||
|
* LLCoros will notice its demise and destroy it.
|
||||||
|
*/
|
||||||
|
class LL_COMMON_API LLCoros: public LLSingleton<LLCoros>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Canonical boost::coroutines::coroutine signature we use
|
||||||
|
typedef boost::coroutines::coroutine<void()> coro;
|
||||||
|
/// Canonical 'self' type
|
||||||
|
typedef coro::self self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and start running a new coroutine with specified name. The name
|
||||||
|
* string you pass is a suggestion; it will be tweaked for uniqueness. The
|
||||||
|
* actual name is returned to you.
|
||||||
|
*
|
||||||
|
* Usage looks like this, for (e.g.) two coroutine parameters:
|
||||||
|
* @code
|
||||||
|
* class MyClass
|
||||||
|
* {
|
||||||
|
* public:
|
||||||
|
* ...
|
||||||
|
* // Do NOT NOT NOT accept reference params other than 'self'!
|
||||||
|
* // Pass by value only!
|
||||||
|
* void myCoroutineMethod(LLCoros::self& self, std::string, LLSD);
|
||||||
|
* ...
|
||||||
|
* };
|
||||||
|
* ...
|
||||||
|
* std::string name = LLCoros::instance().launch(
|
||||||
|
* "mycoro", boost::bind(&MyClass::myCoroutineMethod, this, _1,
|
||||||
|
* "somestring", LLSD(17));
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Your function/method must accept LLCoros::self& as its first parameter.
|
||||||
|
* It can accept any other parameters you want -- but ONLY BY VALUE!
|
||||||
|
* Other reference parameters are a BAD IDEA! You Have Been Warned. See
|
||||||
|
* DEV-32777 comments for an explanation.
|
||||||
|
*
|
||||||
|
* Pass a callable that accepts the single LLCoros::self& parameter. It
|
||||||
|
* may work to pass a free function whose only parameter is 'self'; for
|
||||||
|
* all other cases use boost::bind(). Of course, for a non-static class
|
||||||
|
* method, the first parameter must be the class instance. Use the
|
||||||
|
* placeholder _1 for the 'self' parameter. Any other parameters should be
|
||||||
|
* passed via the bind() expression.
|
||||||
|
*
|
||||||
|
* launch() tweaks the suggested name so it won't collide with any
|
||||||
|
* existing coroutine instance, creates the coroutine instance, registers
|
||||||
|
* it with the tweaked name and runs it until its first wait. At that
|
||||||
|
* point it returns the tweaked name.
|
||||||
|
*/
|
||||||
|
template <typename CALLABLE>
|
||||||
|
std::string launch(const std::string& prefix, const CALLABLE& callable)
|
||||||
|
{
|
||||||
|
return launchImpl(prefix, new coro(callable));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abort a running coroutine by name. Normally, when a coroutine either
|
||||||
|
* runs to completion or terminates with an exception, LLCoros quietly
|
||||||
|
* cleans it up. This is for use only when you must explicitly interrupt
|
||||||
|
* one prematurely. Returns @c true if the specified name was found and
|
||||||
|
* still running at the time.
|
||||||
|
*/
|
||||||
|
bool kill(const std::string& name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* From within a coroutine, pass its @c self object to look up the
|
||||||
|
* (tweaked) name string by which this coroutine is registered. Returns
|
||||||
|
* the empty string if not found (e.g. if the coroutine was launched by
|
||||||
|
* hand rather than using LLCoros::launch()).
|
||||||
|
*/
|
||||||
|
template <typename COROUTINE_SELF>
|
||||||
|
std::string getName(const COROUTINE_SELF& self) const
|
||||||
|
{
|
||||||
|
return getNameByID(self.get_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// getName() by self.get_id()
|
||||||
|
std::string getNameByID(const void* self_id) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class LLSingleton<LLCoros>;
|
||||||
|
LLCoros();
|
||||||
|
std::string launchImpl(const std::string& prefix, coro* newCoro);
|
||||||
|
std::string generateDistinctName(const std::string& prefix) const;
|
||||||
|
bool cleanup(const LLSD&);
|
||||||
|
|
||||||
|
typedef boost::ptr_map<std::string, coro> CoroMap;
|
||||||
|
CoroMap mCoros;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* ! defined(LL_LLCOROS_H) */
|
||||||
@@ -43,6 +43,7 @@
|
|||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "llfasttimer.h"
|
||||||
#include "lltimer.h"
|
#include "lltimer.h"
|
||||||
#include "llstring.h"
|
#include "llstring.h"
|
||||||
|
|
||||||
@@ -94,9 +95,11 @@ std::string LLDate::asRFC1123() const
|
|||||||
return toHTTPDateString (std::string ("%A, %d %b %Y %H:%M:%S GMT"));
|
return toHTTPDateString (std::string ("%A, %d %b %Y %H:%M:%S GMT"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LLFastTimer::DeclareTimer FT_DATE_FORMAT("Date Format");
|
||||||
|
|
||||||
std::string LLDate::toHTTPDateString (std::string fmt) const
|
std::string LLDate::toHTTPDateString (std::string fmt) const
|
||||||
{
|
{
|
||||||
|
LLFastTimer ft1(FT_DATE_FORMAT);
|
||||||
|
|
||||||
time_t locSeconds = (time_t) mSecondsSinceEpoch;
|
time_t locSeconds = (time_t) mSecondsSinceEpoch;
|
||||||
struct tm * gmt = gmtime (&locSeconds);
|
struct tm * gmt = gmtime (&locSeconds);
|
||||||
@@ -105,7 +108,7 @@ std::string LLDate::toHTTPDateString (std::string fmt) const
|
|||||||
|
|
||||||
std::string LLDate::toHTTPDateString (tm * gmt, std::string fmt)
|
std::string LLDate::toHTTPDateString (tm * gmt, std::string fmt)
|
||||||
{
|
{
|
||||||
// Return Epoch UTC date
|
LLFastTimer ft1(FT_DATE_FORMAT);
|
||||||
|
|
||||||
// avoid calling setlocale() unnecessarily - it's expensive.
|
// avoid calling setlocale() unnecessarily - it's expensive.
|
||||||
static std::string prev_locale = "";
|
static std::string prev_locale = "";
|
||||||
|
|||||||
103
indra/llcommon/lldependencies.cpp
Normal file
103
indra/llcommon/lldependencies.cpp
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
/**
|
||||||
|
* @file lldependencies.cpp
|
||||||
|
* @author Nat Goodspeed
|
||||||
|
* @date 2008-09-17
|
||||||
|
* @brief Implementation for lldependencies.
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation;
|
||||||
|
* version 2.1 of the License only.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Precompiled header
|
||||||
|
#include "linden_common.h"
|
||||||
|
// associated header
|
||||||
|
#include "lldependencies.h"
|
||||||
|
// STL headers
|
||||||
|
#include <map>
|
||||||
|
#include <sstream>
|
||||||
|
// std headers
|
||||||
|
// external library headers
|
||||||
|
#include <boost/graph/graph_traits.hpp> // for boost::graph_traits
|
||||||
|
#include <boost/graph/adjacency_list.hpp>
|
||||||
|
#include <boost/graph/topological_sort.hpp>
|
||||||
|
#include <boost/graph/exception.hpp>
|
||||||
|
// other Linden headers
|
||||||
|
|
||||||
|
LLDependenciesBase::VertexList LLDependenciesBase::topo_sort(int vertices, const EdgeList& edges) const
|
||||||
|
{
|
||||||
|
// Construct a Boost Graph Library graph according to the constraints
|
||||||
|
// we've collected. It seems as though we ought to be able to capture
|
||||||
|
// the uniqueness of vertex keys using a setS of vertices with a
|
||||||
|
// string property -- but I don't yet understand adjacency_list well
|
||||||
|
// enough to get there. All the examples I've seen so far use integers
|
||||||
|
// for vertices.
|
||||||
|
// Define the Graph type. Use a vector for vertices so we can use the
|
||||||
|
// default topological_sort vertex lookup by int index. Use a set for
|
||||||
|
// edges because the same dependency may be stated twice: Node "a" may
|
||||||
|
// specify that it must precede "b", while "b" may also state that it
|
||||||
|
// must follow "a".
|
||||||
|
typedef boost::adjacency_list<boost::setS, boost::vecS, boost::directedS,
|
||||||
|
boost::no_property> Graph;
|
||||||
|
// Instantiate the graph. Without vertex properties, we need say no
|
||||||
|
// more about vertices than the total number.
|
||||||
|
Graph g(edges.begin(), edges.end(), vertices);
|
||||||
|
// topo sort
|
||||||
|
typedef boost::graph_traits<Graph>::vertex_descriptor VertexDesc;
|
||||||
|
typedef std::vector<VertexDesc> SortedList;
|
||||||
|
SortedList sorted;
|
||||||
|
// note that it throws not_a_dag if it finds a cycle
|
||||||
|
try
|
||||||
|
{
|
||||||
|
boost::topological_sort(g, std::back_inserter(sorted));
|
||||||
|
}
|
||||||
|
catch (const boost::not_a_dag& e)
|
||||||
|
{
|
||||||
|
// translate to the exception we define
|
||||||
|
std::ostringstream out;
|
||||||
|
out << "LLDependencies cycle: " << e.what() << '\n';
|
||||||
|
// Omit independent nodes: display only those that might contribute to
|
||||||
|
// the cycle.
|
||||||
|
describe(out, false);
|
||||||
|
throw Cycle(out.str());
|
||||||
|
}
|
||||||
|
// A peculiarity of boost::topological_sort() is that it emits results in
|
||||||
|
// REVERSE topological order: to get the result you want, you must
|
||||||
|
// traverse the SortedList using reverse iterators.
|
||||||
|
return VertexList(sorted.rbegin(), sorted.rend());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& LLDependenciesBase::describe(std::ostream& out, bool full) const
|
||||||
|
{
|
||||||
|
// Should never encounter this base-class implementation; may mean that
|
||||||
|
// the KEY type doesn't have a suitable ostream operator<<().
|
||||||
|
out << "<no description available>";
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LLDependenciesBase::describe(bool full) const
|
||||||
|
{
|
||||||
|
// Just use the ostream-based describe() on a std::ostringstream. The
|
||||||
|
// implementation is here mostly so that we can avoid #include <sstream>
|
||||||
|
// in the header file.
|
||||||
|
std::ostringstream out;
|
||||||
|
describe(out, full);
|
||||||
|
return out.str();
|
||||||
|
}
|
||||||
799
indra/llcommon/lldependencies.h
Normal file
799
indra/llcommon/lldependencies.h
Normal file
@@ -0,0 +1,799 @@
|
|||||||
|
/**
|
||||||
|
* @file lldependencies.h
|
||||||
|
* @author Nat Goodspeed
|
||||||
|
* @date 2008-09-17
|
||||||
|
* @brief LLDependencies: a generic mechanism for expressing "b must follow a,
|
||||||
|
* but precede c"
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation;
|
||||||
|
* version 2.1 of the License only.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if ! defined(LL_LLDEPENDENCIES_H)
|
||||||
|
#define LL_LLDEPENDENCIES_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
#include <map>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <iosfwd>
|
||||||
|
#include <boost/iterator/transform_iterator.hpp>
|
||||||
|
#include <boost/iterator/indirect_iterator.hpp>
|
||||||
|
#include <boost/range/iterator_range.hpp>
|
||||||
|
#include <boost/function.hpp>
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* Utilities
|
||||||
|
*****************************************************************************/
|
||||||
|
/**
|
||||||
|
* generic range transformer: given a range acceptable to Boost.Range (e.g. a
|
||||||
|
* standard container, an iterator pair, ...) and a unary function to apply to
|
||||||
|
* each element of the range, make a corresponding range that lazily applies
|
||||||
|
* that function to each element on dereferencing.
|
||||||
|
*/
|
||||||
|
template<typename FUNCTION, typename RANGE>
|
||||||
|
inline
|
||||||
|
boost::iterator_range<boost::transform_iterator<FUNCTION,
|
||||||
|
typename boost::range_const_iterator<RANGE>::type> >
|
||||||
|
make_transform_range(const RANGE& range, FUNCTION function)
|
||||||
|
{
|
||||||
|
// shorthand for the iterator type embedded in our return type
|
||||||
|
typedef boost::transform_iterator<FUNCTION, typename boost::range_const_iterator<RANGE>::type>
|
||||||
|
transform_iterator;
|
||||||
|
return boost::make_iterator_range(transform_iterator(boost::begin(range), function),
|
||||||
|
transform_iterator(boost::end(range), function));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// non-const version of make_transform_range()
|
||||||
|
template<typename FUNCTION, typename RANGE>
|
||||||
|
inline
|
||||||
|
boost::iterator_range<boost::transform_iterator<FUNCTION,
|
||||||
|
typename boost::range_iterator<RANGE>::type> >
|
||||||
|
make_transform_range(RANGE& range, FUNCTION function)
|
||||||
|
{
|
||||||
|
// shorthand for the iterator type embedded in our return type
|
||||||
|
typedef boost::transform_iterator<FUNCTION, typename boost::range_iterator<RANGE>::type>
|
||||||
|
transform_iterator;
|
||||||
|
return boost::make_iterator_range(transform_iterator(boost::begin(range), function),
|
||||||
|
transform_iterator(boost::end(range), function));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* From any range compatible with Boost.Range, instantiate any class capable
|
||||||
|
* of accepting an iterator pair.
|
||||||
|
*/
|
||||||
|
template<class TYPE>
|
||||||
|
struct instance_from_range: public TYPE
|
||||||
|
{
|
||||||
|
template<typename RANGE>
|
||||||
|
instance_from_range(RANGE range):
|
||||||
|
TYPE(boost::begin(range), boost::end(range))
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* LLDependencies
|
||||||
|
*****************************************************************************/
|
||||||
|
/**
|
||||||
|
* LLDependencies components that should not be reinstantiated for each KEY,
|
||||||
|
* NODE specialization
|
||||||
|
*/
|
||||||
|
class LL_COMMON_API LLDependenciesBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~LLDependenciesBase() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown by sort() if there's a cycle
|
||||||
|
*/
|
||||||
|
struct Cycle: public std::runtime_error
|
||||||
|
{
|
||||||
|
Cycle(const std::string& what): std::runtime_error(what) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide a short description of this LLDependencies instance on the
|
||||||
|
* specified output stream, assuming that its KEY type has an operator<<()
|
||||||
|
* that works with std::ostream.
|
||||||
|
*
|
||||||
|
* Pass @a full as @c false to omit any keys without dependency constraints.
|
||||||
|
*/
|
||||||
|
virtual std::ostream& describe(std::ostream& out, bool full=true) const;
|
||||||
|
|
||||||
|
/// describe() to a string
|
||||||
|
virtual std::string describe(bool full=true) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
typedef std::vector< std::pair<int, int> > EdgeList;
|
||||||
|
typedef std::vector<int> VertexList;
|
||||||
|
VertexList topo_sort(int vertices, const EdgeList& edges) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* refpair is specifically intended to capture a pair of references. This
|
||||||
|
* is better than std::pair<T1&, T2&> because some implementations of
|
||||||
|
* std::pair's ctor accept const references to the two types. If the
|
||||||
|
* types are themselves references, this results in an illegal reference-
|
||||||
|
* to-reference.
|
||||||
|
*/
|
||||||
|
template<typename T1, typename T2>
|
||||||
|
struct refpair
|
||||||
|
{
|
||||||
|
refpair(T1 value1, T2 value2):
|
||||||
|
first(value1),
|
||||||
|
second(value2)
|
||||||
|
{}
|
||||||
|
T1 first;
|
||||||
|
T2 second;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/// describe() helper: for most types, report the type as usual
|
||||||
|
template<typename T>
|
||||||
|
inline
|
||||||
|
std::ostream& LLDependencies_describe(std::ostream& out, const T& key)
|
||||||
|
{
|
||||||
|
out << key;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// specialize LLDependencies_describe() for std::string
|
||||||
|
template<>
|
||||||
|
inline
|
||||||
|
std::ostream& LLDependencies_describe(std::ostream& out, const std::string& key)
|
||||||
|
{
|
||||||
|
out << '"' << key << '"';
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* It's reasonable to use LLDependencies in a keys-only way, more or less like
|
||||||
|
* std::set. For that case, the default NODE type is an empty struct.
|
||||||
|
*/
|
||||||
|
struct LLDependenciesEmpty
|
||||||
|
{
|
||||||
|
LLDependenciesEmpty() {}
|
||||||
|
/**
|
||||||
|
* Give it a constructor accepting void* so caller can pass placeholder
|
||||||
|
* values such as NULL or 0 rather than having to write
|
||||||
|
* LLDependenciesEmpty().
|
||||||
|
*/
|
||||||
|
LLDependenciesEmpty(void*) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class manages abstract dependencies between node types of your
|
||||||
|
* choosing. As with a std::map, nodes are copied when add()ed, so the node
|
||||||
|
* type should be relatively lightweight; to manipulate dependencies between
|
||||||
|
* expensive objects, use a pointer type.
|
||||||
|
*
|
||||||
|
* For a given node, you may state the keys of nodes that must precede it
|
||||||
|
* and/or nodes that must follow it. The sort() method will produce an order
|
||||||
|
* that should work, or throw an exception if the constraints are impossible.
|
||||||
|
* We cache results to minimize the cost of repeated sort() calls.
|
||||||
|
*/
|
||||||
|
template<typename KEY = std::string,
|
||||||
|
typename NODE = LLDependenciesEmpty>
|
||||||
|
class LLDependencies: public LLDependenciesBase
|
||||||
|
{
|
||||||
|
typedef LLDependencies<KEY, NODE> self_type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internally, we bundle the client's NODE with its before/after keys.
|
||||||
|
*/
|
||||||
|
struct DepNode
|
||||||
|
{
|
||||||
|
typedef std::set<KEY> dep_set;
|
||||||
|
DepNode(const NODE& node_, const dep_set& after_, const dep_set& before_):
|
||||||
|
node(node_),
|
||||||
|
after(after_),
|
||||||
|
before(before_)
|
||||||
|
{}
|
||||||
|
NODE node;
|
||||||
|
dep_set after, before;
|
||||||
|
};
|
||||||
|
typedef std::map<KEY, DepNode> DepNodeMap;
|
||||||
|
typedef typename DepNodeMap::value_type DepNodeMapEntry;
|
||||||
|
|
||||||
|
/// We have various ways to get the dependencies for a given DepNode.
|
||||||
|
/// Rather than having to restate each one for 'after' and 'before'
|
||||||
|
/// separately, pass a dep_selector so we can apply each to either.
|
||||||
|
typedef boost::function<const typename DepNode::dep_set&(const DepNode&)> dep_selector;
|
||||||
|
|
||||||
|
public:
|
||||||
|
LLDependencies() {}
|
||||||
|
|
||||||
|
typedef KEY key_type;
|
||||||
|
typedef NODE node_type;
|
||||||
|
|
||||||
|
/// param type used to express lists of other node keys -- note that such
|
||||||
|
/// lists can be initialized with boost::assign::list_of()
|
||||||
|
typedef std::vector<KEY> KeyList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new node. State its dependencies on other nodes (which may not
|
||||||
|
* yet have been added) by listing the keys of nodes this new one must
|
||||||
|
* follow, and separately the keys of nodes this new one must precede.
|
||||||
|
*
|
||||||
|
* The node you pass is @em copied into an internal data structure. If you
|
||||||
|
* want to modify the node value after add()ing it, capture the returned
|
||||||
|
* NODE& reference.
|
||||||
|
*
|
||||||
|
* @note
|
||||||
|
* Actual dependency analysis is deferred to the sort() method, so
|
||||||
|
* you can add an arbitrary number of nodes without incurring analysis
|
||||||
|
* overhead for each. The flip side of this is that add()ing nodes that
|
||||||
|
* define a cycle leaves this object in a state in which sort() will
|
||||||
|
* always throw the Cycle exception.
|
||||||
|
*
|
||||||
|
* Two distinct use cases are anticipated:
|
||||||
|
* * The data used to load this object are completely known at compile
|
||||||
|
* time (e.g. LLEventPump listener names). A Cycle exception represents a
|
||||||
|
* bug which can be corrected by the coder. The program need neither catch
|
||||||
|
* Cycle nor attempt to salvage the state of this object.
|
||||||
|
* * The data are loaded at runtime, therefore the universe of
|
||||||
|
* dependencies cannot be known at compile time. The client code should
|
||||||
|
* catch Cycle.
|
||||||
|
* ** If a Cycle exception indicates fatally-flawed input data, this
|
||||||
|
* object can simply be discarded, possibly with the entire program run.
|
||||||
|
* ** If it is essential to restore this object to a working state, the
|
||||||
|
* simplest workaround is to remove() nodes in LIFO order.
|
||||||
|
* *** It may be useful to add functionality to this class to track the
|
||||||
|
* add() chronology, providing a pop() method to remove the most recently
|
||||||
|
* added node.
|
||||||
|
* *** It may further be useful to add a restore() method which would
|
||||||
|
* pop() until sort() no longer throws Cycle. This method would be
|
||||||
|
* expensive -- but it's not clear that client code could resolve the
|
||||||
|
* problem more cheaply.
|
||||||
|
*/
|
||||||
|
NODE& add(const KEY& key, const NODE& node = NODE(),
|
||||||
|
const KeyList& after = KeyList(),
|
||||||
|
const KeyList& before = KeyList())
|
||||||
|
{
|
||||||
|
// Get the passed-in lists as sets for equality comparison
|
||||||
|
typename DepNode::dep_set
|
||||||
|
after_set(after.begin(), after.end()),
|
||||||
|
before_set(before.begin(), before.end());
|
||||||
|
// Try to insert the new node; if it already exists, find the old
|
||||||
|
// node instead.
|
||||||
|
std::pair<typename DepNodeMap::iterator, bool> inserted =
|
||||||
|
mNodes.insert(typename DepNodeMap::value_type(key,
|
||||||
|
DepNode(node, after_set, before_set)));
|
||||||
|
if (! inserted.second) // bool indicating success of insert()
|
||||||
|
{
|
||||||
|
// We already have a node by this name. Have its dependencies
|
||||||
|
// changed? If the existing node's dependencies are identical, the
|
||||||
|
// result will be unchanged, so we can leave the cache intact.
|
||||||
|
// Regardless of inserted.second, inserted.first is the iterator
|
||||||
|
// to the newly-inserted (or existing) map entry. Of course, that
|
||||||
|
// entry's second is the DepNode of interest.
|
||||||
|
if (inserted.first->second.after != after_set ||
|
||||||
|
inserted.first->second.before != before_set)
|
||||||
|
{
|
||||||
|
// Dependencies have changed: clear the cached result.
|
||||||
|
mCache.clear();
|
||||||
|
// save the new dependencies
|
||||||
|
inserted.first->second.after = after_set;
|
||||||
|
inserted.first->second.before = before_set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // this node is new
|
||||||
|
{
|
||||||
|
// This will change results.
|
||||||
|
mCache.clear();
|
||||||
|
}
|
||||||
|
return inserted.first->second.node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the value of an iterator, showing both KEY and its NODE
|
||||||
|
typedef refpair<const KEY&, NODE&> value_type;
|
||||||
|
/// the value of a const_iterator
|
||||||
|
typedef refpair<const KEY&, const NODE&> const_value_type;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Extract functors
|
||||||
|
static value_type value_extract(DepNodeMapEntry& entry)
|
||||||
|
{
|
||||||
|
return value_type(entry.first, entry.second.node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const_value_type const_value_extract(const DepNodeMapEntry& entry)
|
||||||
|
{
|
||||||
|
return const_value_type(entry.first, entry.second.node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// All the iterator access methods return iterator ranges just to cut down
|
||||||
|
// on the friggin' boilerplate!!
|
||||||
|
|
||||||
|
/// generic mNodes range method
|
||||||
|
template<typename ITERATOR, typename FUNCTION>
|
||||||
|
boost::iterator_range<ITERATOR> generic_range(FUNCTION function)
|
||||||
|
{
|
||||||
|
return make_transform_range(mNodes, function);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generic mNodes const range method
|
||||||
|
template<typename ITERATOR, typename FUNCTION>
|
||||||
|
boost::iterator_range<ITERATOR> generic_range(FUNCTION function) const
|
||||||
|
{
|
||||||
|
return make_transform_range(mNodes, function);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// iterator over value_type entries
|
||||||
|
typedef boost::transform_iterator<boost::function<value_type(DepNodeMapEntry&)>,
|
||||||
|
typename DepNodeMap::iterator> iterator;
|
||||||
|
/// range over value_type entries
|
||||||
|
typedef boost::iterator_range<iterator> range;
|
||||||
|
|
||||||
|
/// iterate over value_type <i>in @c KEY order</i> rather than dependency order
|
||||||
|
range get_range()
|
||||||
|
{
|
||||||
|
return generic_range<iterator>(value_extract);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// iterator over const_value_type entries
|
||||||
|
typedef boost::transform_iterator<boost::function<const_value_type(const DepNodeMapEntry&)>,
|
||||||
|
typename DepNodeMap::const_iterator> const_iterator;
|
||||||
|
/// range over const_value_type entries
|
||||||
|
typedef boost::iterator_range<const_iterator> const_range;
|
||||||
|
|
||||||
|
/// iterate over const_value_type <i>in @c KEY order</i> rather than dependency order
|
||||||
|
const_range get_range() const
|
||||||
|
{
|
||||||
|
return generic_range<const_iterator>(const_value_extract);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// iterator over stored NODEs
|
||||||
|
typedef boost::transform_iterator<boost::function<NODE&(DepNodeMapEntry&)>,
|
||||||
|
typename DepNodeMap::iterator> node_iterator;
|
||||||
|
/// range over stored NODEs
|
||||||
|
typedef boost::iterator_range<node_iterator> node_range;
|
||||||
|
|
||||||
|
/// iterate over NODE <i>in @c KEY order</i> rather than dependency order
|
||||||
|
node_range get_node_range()
|
||||||
|
{
|
||||||
|
// First take a DepNodeMapEntry and extract a reference to its
|
||||||
|
// DepNode, then from that extract a reference to its NODE.
|
||||||
|
return generic_range<node_iterator>(
|
||||||
|
boost::bind<NODE&>(&DepNode::node,
|
||||||
|
boost::bind<DepNode&>(&DepNodeMapEntry::second, _1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// const iterator over stored NODEs
|
||||||
|
typedef boost::transform_iterator<boost::function<const NODE&(const DepNodeMapEntry&)>,
|
||||||
|
typename DepNodeMap::const_iterator> const_node_iterator;
|
||||||
|
/// const range over stored NODEs
|
||||||
|
typedef boost::iterator_range<const_node_iterator> const_node_range;
|
||||||
|
|
||||||
|
/// iterate over const NODE <i>in @c KEY order</i> rather than dependency order
|
||||||
|
const_node_range get_node_range() const
|
||||||
|
{
|
||||||
|
// First take a DepNodeMapEntry and extract a reference to its
|
||||||
|
// DepNode, then from that extract a reference to its NODE.
|
||||||
|
return generic_range<const_node_iterator>(
|
||||||
|
boost::bind<const NODE&>(&DepNode::node,
|
||||||
|
boost::bind<const DepNode&>(&DepNodeMapEntry::second, _1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// const iterator over stored KEYs
|
||||||
|
typedef boost::transform_iterator<boost::function<const KEY&(const DepNodeMapEntry&)>,
|
||||||
|
typename DepNodeMap::const_iterator> const_key_iterator;
|
||||||
|
/// const range over stored KEYs
|
||||||
|
typedef boost::iterator_range<const_key_iterator> const_key_range;
|
||||||
|
// We don't provide a non-const iterator over KEYs because they should be
|
||||||
|
// immutable, and in fact our underlying std::map won't give us non-const
|
||||||
|
// references.
|
||||||
|
|
||||||
|
/// iterate over const KEY <i>in @c KEY order</i> rather than dependency order
|
||||||
|
const_key_range get_key_range() const
|
||||||
|
{
|
||||||
|
// From a DepNodeMapEntry, extract a reference to its KEY.
|
||||||
|
return generic_range<const_key_iterator>(
|
||||||
|
boost::bind<const KEY&>(&DepNodeMapEntry::first, _1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find an existing NODE, or return NULL. We decided to avoid providing a
|
||||||
|
* method analogous to std::map::find(), for a couple of reasons:
|
||||||
|
*
|
||||||
|
* * For a find-by-key, getting back an iterator to the (key, value) pair
|
||||||
|
* is less than useful, since you already have the key in hand.
|
||||||
|
* * For a failed request, comparing to end() is problematic. First, we
|
||||||
|
* provide range accessors, so it's more code to get end(). Second, we
|
||||||
|
* provide a number of different ranges -- quick, to which one's end()
|
||||||
|
* should we compare the iterator returned by find()?
|
||||||
|
*
|
||||||
|
* The returned pointer is solely to allow expressing the not-found
|
||||||
|
* condition. LLDependencies still owns the found NODE.
|
||||||
|
*/
|
||||||
|
const NODE* get(const KEY& key) const
|
||||||
|
{
|
||||||
|
typename DepNodeMap::const_iterator found = mNodes.find(key);
|
||||||
|
if (found != mNodes.end())
|
||||||
|
{
|
||||||
|
return &found->second.node;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* non-const get()
|
||||||
|
*/
|
||||||
|
NODE* get(const KEY& key)
|
||||||
|
{
|
||||||
|
// Use const implementation, then cast away const-ness of return
|
||||||
|
return const_cast<NODE*>(const_cast<const self_type*>(this)->get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a node with specified key. This operation is the major reason
|
||||||
|
* we rebuild the graph on the fly instead of storing it.
|
||||||
|
*/
|
||||||
|
bool remove(const KEY& key)
|
||||||
|
{
|
||||||
|
typename DepNodeMap::iterator found = mNodes.find(key);
|
||||||
|
if (found != mNodes.end())
|
||||||
|
{
|
||||||
|
mNodes.erase(found);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// cached list of iterators
|
||||||
|
typedef std::vector<iterator> iterator_list;
|
||||||
|
typedef typename iterator_list::iterator iterator_list_iterator;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* The return type of the sort() method needs some explanation. Provide a
|
||||||
|
* public typedef to facilitate storing the result.
|
||||||
|
*
|
||||||
|
* * We will prepare mCache by looking up DepNodeMap iterators.
|
||||||
|
* * We want to return a range containing iterators that will walk mCache.
|
||||||
|
* * If we simply stored DepNodeMap iterators and returned
|
||||||
|
* (mCache.begin(), mCache.end()), dereferencing each iterator would
|
||||||
|
* obtain a DepNodeMap iterator.
|
||||||
|
* * We want the caller to loop over @c value_type: pair<KEY, NODE>.
|
||||||
|
* * This requires two transformations:
|
||||||
|
* ** mCache must contain @c LLDependencies::iterator so that
|
||||||
|
* dereferencing each entry will obtain an @c LLDependencies::value_type
|
||||||
|
* rather than a DepNodeMapEntry.
|
||||||
|
* ** We must wrap mCache's iterators in boost::indirect_iterator so that
|
||||||
|
* dereferencing one of our returned iterators will also dereference the
|
||||||
|
* iterator contained in mCache.
|
||||||
|
*/
|
||||||
|
typedef boost::iterator_range<boost::indirect_iterator<iterator_list_iterator> > sorted_range;
|
||||||
|
/// for convenience in looping over a sorted_range
|
||||||
|
typedef typename sorted_range::iterator sorted_iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Once we've loaded in the dependencies of interest, arrange them into an
|
||||||
|
* order that works -- or throw Cycle exception.
|
||||||
|
*
|
||||||
|
* Return an iterator range over (key, node) pairs that traverses them in
|
||||||
|
* the desired order.
|
||||||
|
*/
|
||||||
|
sorted_range sort() const
|
||||||
|
{
|
||||||
|
// Changes to mNodes cause us to clear our cache, so empty mCache
|
||||||
|
// means it's invalid and should be recomputed. However, if mNodes is
|
||||||
|
// also empty, then an empty mCache represents a valid order, so don't
|
||||||
|
// bother sorting.
|
||||||
|
if (mCache.empty() && ! mNodes.empty())
|
||||||
|
{
|
||||||
|
// Construct a map of node keys to distinct vertex numbers -- even for
|
||||||
|
// nodes mentioned only in before/after constraints, that haven't yet
|
||||||
|
// been explicitly added. Rely on std::map rejecting a second attempt
|
||||||
|
// to insert the same key. Use the map's size() as the vertex number
|
||||||
|
// to get a distinct value for each successful insertion.
|
||||||
|
typedef std::map<KEY, int> VertexMap;
|
||||||
|
VertexMap vmap;
|
||||||
|
// Nest each of these loops because !@#$%? MSVC warns us that its
|
||||||
|
// former broken behavior has finally been fixed -- and our builds
|
||||||
|
// treat warnings as errors.
|
||||||
|
{
|
||||||
|
for (typename DepNodeMap::const_iterator nmi = mNodes.begin(), nmend = mNodes.end();
|
||||||
|
nmi != nmend; ++nmi)
|
||||||
|
{
|
||||||
|
vmap.insert(typename VertexMap::value_type(nmi->first, vmap.size()));
|
||||||
|
for (typename DepNode::dep_set::const_iterator ai = nmi->second.after.begin(),
|
||||||
|
aend = nmi->second.after.end();
|
||||||
|
ai != aend; ++ai)
|
||||||
|
{
|
||||||
|
vmap.insert(typename VertexMap::value_type(*ai, vmap.size()));
|
||||||
|
}
|
||||||
|
for (typename DepNode::dep_set::const_iterator bi = nmi->second.before.begin(),
|
||||||
|
bend = nmi->second.before.end();
|
||||||
|
bi != bend; ++bi)
|
||||||
|
{
|
||||||
|
vmap.insert(typename VertexMap::value_type(*bi, vmap.size()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Define the edges. For this we must traverse mNodes again, mapping
|
||||||
|
// all the known key dependencies to integer pairs.
|
||||||
|
EdgeList edges;
|
||||||
|
{
|
||||||
|
for (typename DepNodeMap::const_iterator nmi = mNodes.begin(), nmend = mNodes.end();
|
||||||
|
nmi != nmend; ++nmi)
|
||||||
|
{
|
||||||
|
int thisnode = vmap[nmi->first];
|
||||||
|
// after dependencies: build edges from the named node to this one
|
||||||
|
for (typename DepNode::dep_set::const_iterator ai = nmi->second.after.begin(),
|
||||||
|
aend = nmi->second.after.end();
|
||||||
|
ai != aend; ++ai)
|
||||||
|
{
|
||||||
|
edges.push_back(EdgeList::value_type(vmap[*ai], thisnode));
|
||||||
|
}
|
||||||
|
// before dependencies: build edges from this node to the
|
||||||
|
// named one
|
||||||
|
for (typename DepNode::dep_set::const_iterator bi = nmi->second.before.begin(),
|
||||||
|
bend = nmi->second.before.end();
|
||||||
|
bi != bend; ++bi)
|
||||||
|
{
|
||||||
|
edges.push_back(EdgeList::value_type(thisnode, vmap[*bi]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Hide the gory details of our topological sort, since they shouldn't
|
||||||
|
// get reinstantiated for each distinct NODE type.
|
||||||
|
VertexList sorted(topo_sort(vmap.size(), edges));
|
||||||
|
// Build the reverse of vmap to look up the key for each vertex
|
||||||
|
// descriptor. vmap contains exactly one entry for each distinct key,
|
||||||
|
// and we're certain that the associated int values are distinct
|
||||||
|
// indexes. The fact that they're not in order is irrelevant.
|
||||||
|
KeyList vkeys(vmap.size());
|
||||||
|
for (typename VertexMap::const_iterator vmi = vmap.begin(), vmend = vmap.end();
|
||||||
|
vmi != vmend; ++vmi)
|
||||||
|
{
|
||||||
|
vkeys[vmi->second] = vmi->first;
|
||||||
|
}
|
||||||
|
// Walk the sorted output list, building the result into mCache so
|
||||||
|
// we'll have it next time someone asks.
|
||||||
|
mCache.clear();
|
||||||
|
for (VertexList::const_iterator svi = sorted.begin(), svend = sorted.end();
|
||||||
|
svi != svend; ++svi)
|
||||||
|
{
|
||||||
|
// We're certain that vkeys[*svi] exists. However, there might not
|
||||||
|
// yet be a corresponding entry in mNodes.
|
||||||
|
self_type* non_const_this(const_cast<self_type*>(this));
|
||||||
|
typename DepNodeMap::iterator found = non_const_this->mNodes.find(vkeys[*svi]);
|
||||||
|
if (found != non_const_this->mNodes.end())
|
||||||
|
{
|
||||||
|
// Make an iterator of appropriate type.
|
||||||
|
mCache.push_back(iterator(found, value_extract));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Whether or not we've just recomputed mCache, it should now contain
|
||||||
|
// the results we want. Return a range of indirect_iterators over it
|
||||||
|
// so that dereferencing a returned iterator will dereference the
|
||||||
|
// iterator stored in mCache and directly reference the (key, node)
|
||||||
|
// pair.
|
||||||
|
boost::indirect_iterator<iterator_list_iterator>
|
||||||
|
begin(mCache.begin()),
|
||||||
|
end(mCache.end());
|
||||||
|
return sorted_range(begin, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
using LLDependenciesBase::describe; // unhide virtual std::string describe(bool full=true) const;
|
||||||
|
|
||||||
|
/// Override base-class describe() with actual implementation
|
||||||
|
virtual std::ostream& describe(std::ostream& out, bool full=true) const
|
||||||
|
{
|
||||||
|
typename DepNodeMap::const_iterator dmi(mNodes.begin()), dmend(mNodes.end());
|
||||||
|
if (dmi != dmend)
|
||||||
|
{
|
||||||
|
std::string sep;
|
||||||
|
describe(out, sep, *dmi, full);
|
||||||
|
while (++dmi != dmend)
|
||||||
|
{
|
||||||
|
describe(out, sep, *dmi, full);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// describe() helper: report a DepNodeEntry
|
||||||
|
static std::ostream& describe(std::ostream& out, std::string& sep,
|
||||||
|
const DepNodeMapEntry& entry, bool full)
|
||||||
|
{
|
||||||
|
// If we were asked for a full report, describe every node regardless
|
||||||
|
// of whether it has dependencies. If we were asked to suppress
|
||||||
|
// independent nodes, describe this one if either after or before is
|
||||||
|
// non-empty.
|
||||||
|
if (full || (! entry.second.after.empty()) || (! entry.second.before.empty()))
|
||||||
|
{
|
||||||
|
out << sep;
|
||||||
|
sep = "\n";
|
||||||
|
if (! entry.second.after.empty())
|
||||||
|
{
|
||||||
|
out << "after ";
|
||||||
|
describe(out, entry.second.after);
|
||||||
|
out << " -> ";
|
||||||
|
}
|
||||||
|
LLDependencies_describe(out, entry.first);
|
||||||
|
if (! entry.second.before.empty())
|
||||||
|
{
|
||||||
|
out << " -> before ";
|
||||||
|
describe(out, entry.second.before);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// describe() helper: report a dep_set
|
||||||
|
static std::ostream& describe(std::ostream& out, const typename DepNode::dep_set& keys)
|
||||||
|
{
|
||||||
|
out << '(';
|
||||||
|
typename DepNode::dep_set::const_iterator ki(keys.begin()), kend(keys.end());
|
||||||
|
if (ki != kend)
|
||||||
|
{
|
||||||
|
LLDependencies_describe(out, *ki);
|
||||||
|
while (++ki != kend)
|
||||||
|
{
|
||||||
|
out << ", ";
|
||||||
|
LLDependencies_describe(out, *ki);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out << ')';
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterator over the before/after KEYs on which a given NODE depends
|
||||||
|
typedef typename DepNode::dep_set::const_iterator dep_iterator;
|
||||||
|
/// range over the before/after KEYs on which a given NODE depends
|
||||||
|
typedef boost::iterator_range<dep_iterator> dep_range;
|
||||||
|
|
||||||
|
/// dependencies access from key
|
||||||
|
dep_range get_dep_range_from_key(const KEY& key, const dep_selector& selector) const
|
||||||
|
{
|
||||||
|
typename DepNodeMap::const_iterator found = mNodes.find(key);
|
||||||
|
if (found != mNodes.end())
|
||||||
|
{
|
||||||
|
return dep_range(selector(found->second));
|
||||||
|
}
|
||||||
|
// We want to return an empty range. On some platforms a default-
|
||||||
|
// constructed range (e.g. dep_range()) does NOT suffice! The client
|
||||||
|
// is likely to try to iterate from boost::begin(range) to
|
||||||
|
// boost::end(range); yet these iterators might not be valid. Instead
|
||||||
|
// return a range over a valid, empty container.
|
||||||
|
static const typename DepNode::dep_set empty_deps;
|
||||||
|
return dep_range(empty_deps.begin(), empty_deps.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// dependencies access from any one of our key-order iterators
|
||||||
|
template<typename ITERATOR>
|
||||||
|
dep_range get_dep_range_from_xform(const ITERATOR& iterator, const dep_selector& selector) const
|
||||||
|
{
|
||||||
|
return dep_range(selector(iterator.base()->second));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// dependencies access from sorted_iterator
|
||||||
|
dep_range get_dep_range_from_sorted(const sorted_iterator& sortiter,
|
||||||
|
const dep_selector& selector) const
|
||||||
|
{
|
||||||
|
// sorted_iterator is a boost::indirect_iterator wrapping an mCache
|
||||||
|
// iterator, which we can obtain by sortiter.base(). Deferencing that
|
||||||
|
// gets us an mCache entry, an 'iterator' -- one of our traversal
|
||||||
|
// iterators -- on which we can use get_dep_range_from_xform().
|
||||||
|
return get_dep_range_from_xform(*sortiter.base(), selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a range over the after KEYs stored for the passed KEY or iterator,
|
||||||
|
* in <i>arbitrary order.</i> If you pass a nonexistent KEY, returns empty
|
||||||
|
* range -- same as a KEY with no after KEYs. Detect existence of a KEY
|
||||||
|
* using get() instead.
|
||||||
|
*/
|
||||||
|
template<typename KEY_OR_ITER>
|
||||||
|
dep_range get_after_range(const KEY_OR_ITER& key) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a range over the before KEYs stored for the passed KEY or iterator,
|
||||||
|
* in <i>arbitrary order.</i> If you pass a nonexistent KEY, returns empty
|
||||||
|
* range -- same as a KEY with no before KEYs. Detect existence of a KEY
|
||||||
|
* using get() instead.
|
||||||
|
*/
|
||||||
|
template<typename KEY_OR_ITER>
|
||||||
|
dep_range get_before_range(const KEY_OR_ITER& key) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DepNodeMap mNodes;
|
||||||
|
mutable iterator_list mCache;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functor to get a dep_range from a KEY or iterator -- generic case. If the
|
||||||
|
* passed value isn't one of our iterator specializations, assume it's
|
||||||
|
* convertible to the KEY type.
|
||||||
|
*/
|
||||||
|
template<typename KEY_ITER>
|
||||||
|
struct LLDependencies_dep_range_from
|
||||||
|
{
|
||||||
|
template<typename KEY, typename NODE, typename SELECTOR>
|
||||||
|
typename LLDependencies<KEY, NODE>::dep_range
|
||||||
|
operator()(const LLDependencies<KEY, NODE>& deps,
|
||||||
|
const KEY_ITER& key,
|
||||||
|
const SELECTOR& selector)
|
||||||
|
{
|
||||||
|
return deps.get_dep_range_from_key(key, selector);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Specialize LLDependencies_dep_range_from for our key-order iterators
|
||||||
|
template<typename FUNCTION, typename ITERATOR>
|
||||||
|
struct LLDependencies_dep_range_from< boost::transform_iterator<FUNCTION, ITERATOR> >
|
||||||
|
{
|
||||||
|
template<typename KEY, typename NODE, typename SELECTOR>
|
||||||
|
typename LLDependencies<KEY, NODE>::dep_range
|
||||||
|
operator()(const LLDependencies<KEY, NODE>& deps,
|
||||||
|
const boost::transform_iterator<FUNCTION, ITERATOR>& iter,
|
||||||
|
const SELECTOR& selector)
|
||||||
|
{
|
||||||
|
return deps.get_dep_range_from_xform(iter, selector);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Specialize LLDependencies_dep_range_from for sorted_iterator
|
||||||
|
template<typename BASEITER>
|
||||||
|
struct LLDependencies_dep_range_from< boost::indirect_iterator<BASEITER> >
|
||||||
|
{
|
||||||
|
template<typename KEY, typename NODE, typename SELECTOR>
|
||||||
|
typename LLDependencies<KEY, NODE>::dep_range
|
||||||
|
operator()(const LLDependencies<KEY, NODE>& deps,
|
||||||
|
const boost::indirect_iterator<BASEITER>& iter,
|
||||||
|
const SELECTOR& selector)
|
||||||
|
{
|
||||||
|
return deps.get_dep_range_from_sorted(iter, selector);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// generic get_after_range() implementation
|
||||||
|
template<typename KEY, typename NODE>
|
||||||
|
template<typename KEY_OR_ITER>
|
||||||
|
typename LLDependencies<KEY, NODE>::dep_range
|
||||||
|
LLDependencies<KEY, NODE>::get_after_range(const KEY_OR_ITER& key_iter) const
|
||||||
|
{
|
||||||
|
return LLDependencies_dep_range_from<KEY_OR_ITER>()(
|
||||||
|
*this,
|
||||||
|
key_iter,
|
||||||
|
boost::bind<const typename DepNode::dep_set&>(&DepNode::after, _1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generic get_before_range() implementation
|
||||||
|
template<typename KEY, typename NODE>
|
||||||
|
template<typename KEY_OR_ITER>
|
||||||
|
typename LLDependencies<KEY, NODE>::dep_range
|
||||||
|
LLDependencies<KEY, NODE>::get_before_range(const KEY_OR_ITER& key_iter) const
|
||||||
|
{
|
||||||
|
return LLDependencies_dep_range_from<KEY_OR_ITER>()(
|
||||||
|
*this,
|
||||||
|
key_iter,
|
||||||
|
boost::bind<const typename DepNode::dep_set&>(&DepNode::before, _1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* ! defined(LL_LLDEPENDENCIES_H) */
|
||||||
@@ -107,7 +107,14 @@ const int LL_ERR_PRICE_MISMATCH = -23018;
|
|||||||
|
|
||||||
#define llwarning(msg, num) llwarns << "Warning # " << num << ": " << msg << llendl;
|
#define llwarning(msg, num) llwarns << "Warning # " << num << ": " << msg << llendl;
|
||||||
|
|
||||||
#define llassert_always(func) if (LL_UNLIKELY(!(func))) llerrs << "ASSERT (" << #func << ")" << llendl;
|
#define liru_slashpos std::string(__FILE__).find_last_of("/\\")
|
||||||
|
#define liru_slashpos2 std::string(__FILE__).substr(0,liru_slashpos).find_last_of("/\\")
|
||||||
|
#define liru_assert_strip /*strip path down to lastlevel directory and filename for assert.*/\
|
||||||
|
(liru_slashpos == std::string::npos ? std::string(__FILE__)/*just filename, print as is*/\
|
||||||
|
: liru_slashpos2 == std::string::npos ? std::string(__FILE__)/*Apparently, we're in / or perhaps the top of the drive, print as is*/\
|
||||||
|
: std::string(__FILE__).substr(1+liru_slashpos2))/*print foo/bar.cpp or perhaps foo\bar.cpp*/
|
||||||
|
|
||||||
|
#define llassert_always(func) if (LL_UNLIKELY(!(func))) llerrs <<"\nASSERT(" #func ")\nfile:"<<liru_assert_strip<<" line:"<<__LINE__ << llendl;
|
||||||
|
|
||||||
#ifdef SHOW_ASSERT
|
#ifdef SHOW_ASSERT
|
||||||
#define llassert(func) llassert_always(func)
|
#define llassert(func) llassert_always(func)
|
||||||
|
|||||||
@@ -34,6 +34,8 @@
|
|||||||
|
|
||||||
#include "llevent.h"
|
#include "llevent.h"
|
||||||
|
|
||||||
|
using namespace LLOldEvents;
|
||||||
|
|
||||||
/************************************************
|
/************************************************
|
||||||
Events
|
Events
|
||||||
************************************************/
|
************************************************/
|
||||||
|
|||||||
@@ -35,9 +35,12 @@
|
|||||||
#define LL_EVENT_H
|
#define LL_EVENT_H
|
||||||
|
|
||||||
#include "llsd.h"
|
#include "llsd.h"
|
||||||
#include "llmemory.h"
|
#include "llpointer.h"
|
||||||
#include "llthread.h"
|
#include "llthread.h"
|
||||||
|
|
||||||
|
namespace LLOldEvents
|
||||||
|
{
|
||||||
|
|
||||||
class LLEventListener;
|
class LLEventListener;
|
||||||
class LLEvent;
|
class LLEvent;
|
||||||
class LLEventDispatcher;
|
class LLEventDispatcher;
|
||||||
@@ -194,4 +197,6 @@ public:
|
|||||||
LLSD mValue;
|
LLSD mValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // LLOldEvents
|
||||||
|
|
||||||
#endif // LL_EVENT_H
|
#endif // LL_EVENT_H
|
||||||
|
|||||||
77
indra/llcommon/lleventapi.cpp
Normal file
77
indra/llcommon/lleventapi.cpp
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
* @file lleventapi.cpp
|
||||||
|
* @author Nat Goodspeed
|
||||||
|
* @date 2009-11-10
|
||||||
|
* @brief Implementation for lleventapi.
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation;
|
||||||
|
* version 2.1 of the License only.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Precompiled header
|
||||||
|
#include "linden_common.h"
|
||||||
|
// associated header
|
||||||
|
#include "lleventapi.h"
|
||||||
|
// STL headers
|
||||||
|
// std headers
|
||||||
|
// external library headers
|
||||||
|
// other Linden headers
|
||||||
|
#include "llerror.h"
|
||||||
|
|
||||||
|
LLEventAPI::LLEventAPI(const std::string& name, const std::string& desc, const std::string& field):
|
||||||
|
lbase(name, field),
|
||||||
|
ibase(name),
|
||||||
|
mDesc(desc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LLEventAPI::~LLEventAPI()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LLEventAPI::Response::Response(const LLSD& seed, const LLSD& request, const LLSD::String& replyKey):
|
||||||
|
mResp(seed),
|
||||||
|
mReq(request),
|
||||||
|
mKey(replyKey)
|
||||||
|
{}
|
||||||
|
|
||||||
|
LLEventAPI::Response::~Response()
|
||||||
|
{
|
||||||
|
// When you instantiate a stack Response object, if the original
|
||||||
|
// request requested a reply, send it when we leave this block, no
|
||||||
|
// matter how.
|
||||||
|
sendReply(mResp, mReq, mKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLEventAPI::Response::warn(const std::string& warning)
|
||||||
|
{
|
||||||
|
LL_WARNS("LLEventAPI::Response") << warning << LL_ENDL;
|
||||||
|
mResp["warnings"].append(warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLEventAPI::Response::error(const std::string& error)
|
||||||
|
{
|
||||||
|
// Use LL_WARNS rather than LL_ERROR: we don't want the viewer to shut
|
||||||
|
// down altogether.
|
||||||
|
LL_WARNS("LLEventAPI::Response") << error << LL_ENDL;
|
||||||
|
|
||||||
|
mResp["error"] = error;
|
||||||
|
}
|
||||||
166
indra/llcommon/lleventapi.h
Normal file
166
indra/llcommon/lleventapi.h
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
/**
|
||||||
|
* @file lleventapi.h
|
||||||
|
* @author Nat Goodspeed
|
||||||
|
* @date 2009-10-28
|
||||||
|
* @brief LLEventAPI is the base class for every class that wraps a C++ API
|
||||||
|
* in an event API
|
||||||
|
* (see https://wiki.lindenlab.com/wiki/Incremental_Viewer_Automation/Event_API).
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation;
|
||||||
|
* version 2.1 of the License only.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if ! defined(LL_LLEVENTAPI_H)
|
||||||
|
#define LL_LLEVENTAPI_H
|
||||||
|
|
||||||
|
#include "lleventdispatcher.h"
|
||||||
|
#include "llinstancetracker.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LLEventAPI not only provides operation dispatch functionality, inherited
|
||||||
|
* from LLDispatchListener -- it also gives us event API introspection.
|
||||||
|
* Deriving from LLInstanceTracker lets us enumerate instances.
|
||||||
|
*/
|
||||||
|
class LL_COMMON_API LLEventAPI: public LLDispatchListener,
|
||||||
|
public LLInstanceTracker<LLEventAPI, std::string>
|
||||||
|
{
|
||||||
|
typedef LLDispatchListener lbase;
|
||||||
|
typedef LLInstanceTracker<LLEventAPI, std::string> ibase;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @param name LLEventPump name on which this LLEventAPI will listen. This
|
||||||
|
* also serves as the LLInstanceTracker instance key.
|
||||||
|
* @param desc Documentation string shown to a client trying to discover
|
||||||
|
* available event APIs.
|
||||||
|
* @param field LLSD::Map key used by LLDispatchListener to look up the
|
||||||
|
* subclass method to invoke [default "op"].
|
||||||
|
*/
|
||||||
|
LLEventAPI(const std::string& name, const std::string& desc, const std::string& field="op");
|
||||||
|
virtual ~LLEventAPI();
|
||||||
|
|
||||||
|
/// Get the string name of this LLEventAPI
|
||||||
|
std::string getName() const { return ibase::getKey(); }
|
||||||
|
/// Get the documentation string
|
||||||
|
std::string getDesc() const { return mDesc; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Publish only selected add() methods from LLEventDispatcher.
|
||||||
|
* Every LLEventAPI add() @em must have a description string.
|
||||||
|
*/
|
||||||
|
template <typename CALLABLE>
|
||||||
|
void add(const std::string& name,
|
||||||
|
const std::string& desc,
|
||||||
|
CALLABLE callable,
|
||||||
|
const LLSD& required=LLSD())
|
||||||
|
{
|
||||||
|
LLEventDispatcher::add(name, desc, callable, required);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate a Response object in any LLEventAPI subclass method that
|
||||||
|
* wants to guarantee a reply (if requested) will be sent on exit from the
|
||||||
|
* method. The reply will be sent if request.has(@a replyKey), default
|
||||||
|
* "reply". If specified, the value of request[replyKey] is the name of
|
||||||
|
* the LLEventPump on which to send the reply. Conventionally you might
|
||||||
|
* code something like:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* void MyEventAPI::someMethod(const LLSD& request)
|
||||||
|
* {
|
||||||
|
* // Send a reply event as long as request.has("reply")
|
||||||
|
* Response response(LLSD(), request);
|
||||||
|
* // ...
|
||||||
|
* // will be sent in reply event
|
||||||
|
* response["somekey"] = some_data;
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
class LL_COMMON_API Response
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Instantiating a Response object in an LLEventAPI subclass method
|
||||||
|
* ensures that, if desired, a reply event will be sent.
|
||||||
|
*
|
||||||
|
* @a seed is the initial reply LLSD that will be further decorated before
|
||||||
|
* being sent as the reply
|
||||||
|
*
|
||||||
|
* @a request is the incoming request LLSD; we particularly care about
|
||||||
|
* [replyKey] and ["reqid"]
|
||||||
|
*
|
||||||
|
* @a replyKey [default "reply"] is the string name of the LLEventPump
|
||||||
|
* on which the caller wants a reply. If <tt>(!
|
||||||
|
* request.has(replyKey))</tt>, no reply will be sent.
|
||||||
|
*/
|
||||||
|
Response(const LLSD& seed, const LLSD& request, const LLSD::String& replyKey="reply");
|
||||||
|
~Response();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code
|
||||||
|
* if (some condition)
|
||||||
|
* {
|
||||||
|
* response.warn("warnings are logged and collected in [\"warnings\"]");
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
void warn(const std::string& warning);
|
||||||
|
/**
|
||||||
|
* @code
|
||||||
|
* if (some condition isn't met)
|
||||||
|
* {
|
||||||
|
* // In a function returning void, you can validly 'return
|
||||||
|
* // expression' if the expression is itself of type void. But
|
||||||
|
* // returning is up to you; response.error() has no effect on
|
||||||
|
* // flow of control.
|
||||||
|
* return response.error("error message, logged and also sent as [\"error\"]");
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
void error(const std::string& error);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set other keys...
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* // set any attributes you want to be sent in the reply
|
||||||
|
* response["info"] = some_value;
|
||||||
|
* // ...
|
||||||
|
* response["ok"] = went_well;
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
LLSD& operator[](const LLSD::String& key) { return mResp[key]; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set the response to the given data
|
||||||
|
*/
|
||||||
|
void setResponse(LLSD const & response){ mResp = response; }
|
||||||
|
|
||||||
|
LLSD mResp, mReq;
|
||||||
|
LLSD::String mKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string mDesc;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* ! defined(LL_LLEVENTAPI_H) */
|
||||||
146
indra/llcommon/lleventcoro.cpp
Normal file
146
indra/llcommon/lleventcoro.cpp
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
/**
|
||||||
|
* @file lleventcoro.cpp
|
||||||
|
* @author Nat Goodspeed
|
||||||
|
* @date 2009-04-29
|
||||||
|
* @brief Implementation for lleventcoro.
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation;
|
||||||
|
* version 2.1 of the License only.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Precompiled header
|
||||||
|
#include "linden_common.h"
|
||||||
|
// associated header
|
||||||
|
#include "lleventcoro.h"
|
||||||
|
// STL headers
|
||||||
|
#include <map>
|
||||||
|
// std headers
|
||||||
|
// external library headers
|
||||||
|
// other Linden headers
|
||||||
|
#include "llsdserialize.h"
|
||||||
|
#include "llerror.h"
|
||||||
|
#include "llcoros.h"
|
||||||
|
|
||||||
|
std::string LLEventDetail::listenerNameForCoroImpl(const void* self_id)
|
||||||
|
{
|
||||||
|
// First, if this coroutine was launched by LLCoros::launch(), find that name.
|
||||||
|
std::string name(LLCoros::instance().getNameByID(self_id));
|
||||||
|
if (! name.empty())
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
// Apparently this coroutine wasn't launched by LLCoros::launch(). Check
|
||||||
|
// whether we have a memo for this self_id.
|
||||||
|
typedef std::map<const void*, std::string> MapType;
|
||||||
|
static MapType memo;
|
||||||
|
MapType::const_iterator found = memo.find(self_id);
|
||||||
|
if (found != memo.end())
|
||||||
|
{
|
||||||
|
// this coroutine instance has called us before, reuse same name
|
||||||
|
return found->second;
|
||||||
|
}
|
||||||
|
// this is the first time we've been called for this coroutine instance
|
||||||
|
name = LLEventPump::inventName("coro");
|
||||||
|
memo[self_id] = name;
|
||||||
|
LL_INFOS("LLEventCoro") << "listenerNameForCoroImpl(" << self_id << "): inventing coro name '"
|
||||||
|
<< name << "'" << LL_ENDL;
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLEventDetail::storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value)
|
||||||
|
{
|
||||||
|
if (rawPath.isUndefined())
|
||||||
|
{
|
||||||
|
// no-op case
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arrange to treat rawPath uniformly as an array. If it's not already an
|
||||||
|
// array, store it as the only entry in one.
|
||||||
|
LLSD path;
|
||||||
|
if (rawPath.isArray())
|
||||||
|
{
|
||||||
|
path = rawPath;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
path.append(rawPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to indicate a current destination -- but that current destination
|
||||||
|
// needs to change as we step through the path array. Where normally we'd
|
||||||
|
// use an LLSD& to capture a subscripted LLSD lvalue, this time we must
|
||||||
|
// instead use a pointer -- since it must be reassigned.
|
||||||
|
LLSD* pdest = &dest;
|
||||||
|
|
||||||
|
// Now loop through that array
|
||||||
|
for (LLSD::Integer i = 0; i < path.size(); ++i)
|
||||||
|
{
|
||||||
|
if (path[i].isString())
|
||||||
|
{
|
||||||
|
// *pdest is an LLSD map
|
||||||
|
pdest = &((*pdest)[path[i].asString()]);
|
||||||
|
}
|
||||||
|
else if (path[i].isInteger())
|
||||||
|
{
|
||||||
|
// *pdest is an LLSD array
|
||||||
|
pdest = &((*pdest)[path[i].asInteger()]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// What do we do with Real or Array or Map or ...?
|
||||||
|
// As it's a coder error -- not a user error -- rub the coder's
|
||||||
|
// face in it so it gets fixed.
|
||||||
|
LL_ERRS("lleventcoro") << "storeToLLSDPath(" << dest << ", " << rawPath << ", " << value
|
||||||
|
<< "): path[" << i << "] bad type " << path[i].type() << LL_ENDL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here *pdest is where we should store value.
|
||||||
|
*pdest = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
LLSD errorException(const LLEventWithID& result, const std::string& desc)
|
||||||
|
{
|
||||||
|
// If the result arrived on the error pump (pump 1), instead of
|
||||||
|
// returning it, deliver it via exception.
|
||||||
|
if (result.second)
|
||||||
|
{
|
||||||
|
throw LLErrorEvent(desc, result.first);
|
||||||
|
}
|
||||||
|
// That way, our caller knows a simple return must be from the reply
|
||||||
|
// pump (pump 0).
|
||||||
|
return result.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
LLSD errorLog(const LLEventWithID& result, const std::string& desc)
|
||||||
|
{
|
||||||
|
// If the result arrived on the error pump (pump 1), log it as a fatal
|
||||||
|
// error.
|
||||||
|
if (result.second)
|
||||||
|
{
|
||||||
|
LL_ERRS("errorLog") << desc << ":" << std::endl;
|
||||||
|
LLSDSerialize::toPrettyXML(result.first, LL_CONT);
|
||||||
|
LL_CONT << LL_ENDL;
|
||||||
|
}
|
||||||
|
// A simple return must therefore be from the reply pump (pump 0).
|
||||||
|
return result.first;
|
||||||
|
}
|
||||||
569
indra/llcommon/lleventcoro.h
Normal file
569
indra/llcommon/lleventcoro.h
Normal file
@@ -0,0 +1,569 @@
|
|||||||
|
/**
|
||||||
|
* @file lleventcoro.h
|
||||||
|
* @author Nat Goodspeed
|
||||||
|
* @date 2009-04-29
|
||||||
|
* @brief Utilities to interface between coroutines and events.
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation;
|
||||||
|
* version 2.1 of the License only.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if ! defined(LL_LLEVENTCORO_H)
|
||||||
|
#define LL_LLEVENTCORO_H
|
||||||
|
|
||||||
|
#include <boost/coroutine/coroutine.hpp>
|
||||||
|
#include <boost/coroutine/future.hpp>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
#include <string>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include "llevents.h"
|
||||||
|
#include "llerror.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like LLListenerOrPumpName, this is a class intended for parameter lists:
|
||||||
|
* accept a <tt>const LLEventPumpOrPumpName&</tt> and you can accept either an
|
||||||
|
* <tt>LLEventPump&</tt> or its string name. For a single parameter that could
|
||||||
|
* be either, it's not hard to overload the function -- but as soon as you
|
||||||
|
* want to accept two such parameters, this is cheaper than four overloads.
|
||||||
|
*/
|
||||||
|
class LLEventPumpOrPumpName
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Pass an actual LLEventPump&
|
||||||
|
LLEventPumpOrPumpName(LLEventPump& pump):
|
||||||
|
mPump(pump)
|
||||||
|
{}
|
||||||
|
/// Pass the string name of an LLEventPump
|
||||||
|
LLEventPumpOrPumpName(const std::string& pumpname):
|
||||||
|
mPump(LLEventPumps::instance().obtain(pumpname))
|
||||||
|
{}
|
||||||
|
/// Pass string constant name of an LLEventPump. This override must be
|
||||||
|
/// explicit, since otherwise passing <tt>const char*</tt> to a function
|
||||||
|
/// accepting <tt>const LLEventPumpOrPumpName&</tt> would require two
|
||||||
|
/// different implicit conversions: <tt>const char*</tt> -> <tt>const
|
||||||
|
/// std::string&</tt> -> <tt>const LLEventPumpOrPumpName&</tt>.
|
||||||
|
LLEventPumpOrPumpName(const char* pumpname):
|
||||||
|
mPump(LLEventPumps::instance().obtain(pumpname))
|
||||||
|
{}
|
||||||
|
/// Unspecified: "I choose not to identify an LLEventPump."
|
||||||
|
LLEventPumpOrPumpName() {}
|
||||||
|
operator LLEventPump& () const { return *mPump; }
|
||||||
|
LLEventPump& getPump() const { return *mPump; }
|
||||||
|
operator bool() const { return mPump; }
|
||||||
|
bool operator!() const { return ! mPump; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
boost::optional<LLEventPump&> mPump;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// This is an adapter for a signature like void LISTENER(const LLSD&), which
|
||||||
|
/// isn't a valid LLEventPump listener: such listeners should return bool.
|
||||||
|
template <typename LISTENER>
|
||||||
|
class LLVoidListener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LLVoidListener(const LISTENER& listener):
|
||||||
|
mListener(listener)
|
||||||
|
{}
|
||||||
|
bool operator()(const LLSD& event)
|
||||||
|
{
|
||||||
|
mListener(event);
|
||||||
|
// don't swallow the event, let other listeners see it
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
LISTENER mListener;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// LLVoidListener helper function to infer the type of the LISTENER
|
||||||
|
template <typename LISTENER>
|
||||||
|
LLVoidListener<LISTENER> voidlistener(const LISTENER& listener)
|
||||||
|
{
|
||||||
|
return LLVoidListener<LISTENER>(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace LLEventDetail
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* waitForEventOn() permits a coroutine to temporarily listen on an
|
||||||
|
* LLEventPump any number of times. We don't really want to have to ask
|
||||||
|
* the caller to label each such call with a distinct string; the whole
|
||||||
|
* point of waitForEventOn() is to present a nice sequential interface to
|
||||||
|
* the underlying LLEventPump-with-named-listeners machinery. So we'll use
|
||||||
|
* LLEventPump::inventName() to generate a distinct name for each
|
||||||
|
* temporary listener. On the other hand, because a given coroutine might
|
||||||
|
* call waitForEventOn() any number of times, we don't really want to
|
||||||
|
* consume an arbitrary number of generated inventName()s: that namespace,
|
||||||
|
* though large, is nonetheless finite. So we memoize an invented name for
|
||||||
|
* each distinct coroutine instance (each different 'self' object). We
|
||||||
|
* can't know the type of 'self', because it depends on the coroutine
|
||||||
|
* body's signature. So we cast its address to void*, looking for distinct
|
||||||
|
* pointer values. Yes, that means that an early coroutine could cache a
|
||||||
|
* value here, then be destroyed, only to be supplanted by a later
|
||||||
|
* coroutine (of the same or different type), and we'll end up
|
||||||
|
* "recognizing" the second one and reusing the listener name -- but
|
||||||
|
* that's okay, since it won't collide with any listener name used by the
|
||||||
|
* earlier coroutine since that earlier coroutine no longer exists.
|
||||||
|
*/
|
||||||
|
template <typename COROUTINE_SELF>
|
||||||
|
std::string listenerNameForCoro(COROUTINE_SELF& self)
|
||||||
|
{
|
||||||
|
return listenerNameForCoroImpl(self.get_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation for listenerNameForCoro()
|
||||||
|
LL_COMMON_API std::string listenerNameForCoroImpl(const void* self_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement behavior described for postAndWait()'s @a replyPumpNamePath
|
||||||
|
* parameter:
|
||||||
|
*
|
||||||
|
* * If <tt>path.isUndefined()</tt>, do nothing.
|
||||||
|
* * If <tt>path.isString()</tt>, @a dest is an LLSD map: store @a value
|
||||||
|
* into <tt>dest[path.asString()]</tt>.
|
||||||
|
* * If <tt>path.isInteger()</tt>, @a dest is an LLSD array: store @a
|
||||||
|
* value into <tt>dest[path.asInteger()]</tt>.
|
||||||
|
* * If <tt>path.isArray()</tt>, iteratively apply the rules above to step
|
||||||
|
* down through the structure of @a dest. The last array entry in @a
|
||||||
|
* path specifies the entry in the lowest-level structure in @a dest
|
||||||
|
* into which to store @a value.
|
||||||
|
*
|
||||||
|
* @note
|
||||||
|
* In the degenerate case in which @a path is an empty array, @a dest will
|
||||||
|
* @em become @a value rather than @em containing it.
|
||||||
|
*/
|
||||||
|
LL_COMMON_API void storeToLLSDPath(LLSD& dest, const LLSD& path, const LLSD& value);
|
||||||
|
} // namespace LLEventDetail
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post specified LLSD event on the specified LLEventPump, then wait for a
|
||||||
|
* response on specified other LLEventPump. This is more than mere
|
||||||
|
* convenience: the difference between this function and the sequence
|
||||||
|
* @code
|
||||||
|
* requestPump.post(myEvent);
|
||||||
|
* LLSD reply = waitForEventOn(self, replyPump);
|
||||||
|
* @endcode
|
||||||
|
* is that the sequence above fails if the reply is posted immediately on
|
||||||
|
* @a replyPump, that is, before <tt>requestPump.post()</tt> returns. In the
|
||||||
|
* sequence above, the running coroutine isn't even listening on @a replyPump
|
||||||
|
* until <tt>requestPump.post()</tt> returns and @c waitForEventOn() is
|
||||||
|
* entered. Therefore, the coroutine completely misses an immediate reply
|
||||||
|
* event, making it wait indefinitely.
|
||||||
|
*
|
||||||
|
* By contrast, postAndWait() listens on the @a replyPump @em before posting
|
||||||
|
* the specified LLSD event on the specified @a requestPump.
|
||||||
|
*
|
||||||
|
* @param self The @c self object passed into a coroutine
|
||||||
|
* @param event LLSD data to be posted on @a requestPump
|
||||||
|
* @param requestPump an LLEventPump on which to post @a event. Pass either
|
||||||
|
* the LLEventPump& or its string name. However, if you pass a
|
||||||
|
* default-constructed @c LLEventPumpOrPumpName, we skip the post() call.
|
||||||
|
* @param replyPump an LLEventPump on which postAndWait() will listen for a
|
||||||
|
* reply. Pass either the LLEventPump& or its string name. The calling
|
||||||
|
* coroutine will wait until that reply arrives. (If you're concerned about a
|
||||||
|
* reply that might not arrive, please see also LLEventTimeout.)
|
||||||
|
* @param replyPumpNamePath specifies the location within @a event in which to
|
||||||
|
* store <tt>replyPump.getName()</tt>. This is a strictly optional convenience
|
||||||
|
* feature; obviously you can store the name in @a event "by hand" if desired.
|
||||||
|
* @a replyPumpNamePath can be specified in any of four forms:
|
||||||
|
* * @c isUndefined() (default-constructed LLSD object): do nothing. This is
|
||||||
|
* the default behavior if you omit @a replyPumpNamePath.
|
||||||
|
* * @c isInteger(): @a event is an array. Store <tt>replyPump.getName()</tt>
|
||||||
|
* in <tt>event[replyPumpNamePath.asInteger()]</tt>.
|
||||||
|
* * @c isString(): @a event is a map. Store <tt>replyPump.getName()</tt> in
|
||||||
|
* <tt>event[replyPumpNamePath.asString()]</tt>.
|
||||||
|
* * @c isArray(): @a event has several levels of structure, e.g. map of
|
||||||
|
* maps, array of arrays, array of maps, map of arrays, ... Store
|
||||||
|
* <tt>replyPump.getName()</tt> in
|
||||||
|
* <tt>event[replyPumpNamePath[0]][replyPumpNamePath[1]]...</tt> In other
|
||||||
|
* words, examine each array entry in @a replyPumpNamePath in turn. If it's an
|
||||||
|
* <tt>LLSD::String</tt>, the current level of @a event is a map; step down to
|
||||||
|
* that map entry. If it's an <tt>LLSD::Integer</tt>, the current level of @a
|
||||||
|
* event is an array; step down to that array entry. The last array entry in
|
||||||
|
* @a replyPumpNamePath specifies the entry in the lowest-level structure in
|
||||||
|
* @a event into which to store <tt>replyPump.getName()</tt>.
|
||||||
|
*/
|
||||||
|
template <typename SELF>
|
||||||
|
LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump,
|
||||||
|
const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD())
|
||||||
|
{
|
||||||
|
// declare the future
|
||||||
|
boost::coroutines::future<LLSD> future(self);
|
||||||
|
// make a callback that will assign a value to the future, and listen on
|
||||||
|
// the specified LLEventPump with that callback
|
||||||
|
std::string listenerName(LLEventDetail::listenerNameForCoro(self));
|
||||||
|
LLTempBoundListener connection(
|
||||||
|
replyPump.getPump().listen(listenerName,
|
||||||
|
voidlistener(boost::coroutines::make_callback(future))));
|
||||||
|
// skip the "post" part if requestPump is default-constructed
|
||||||
|
if (requestPump)
|
||||||
|
{
|
||||||
|
// If replyPumpNamePath is non-empty, store the replyPump name in the
|
||||||
|
// request event.
|
||||||
|
LLSD modevent(event);
|
||||||
|
LLEventDetail::storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName());
|
||||||
|
LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
|
||||||
|
<< " posting to " << requestPump.getPump().getName()
|
||||||
|
<< LL_ENDL;
|
||||||
|
|
||||||
|
// *NOTE:Mani - Removed because modevent could contain user's hashed passwd.
|
||||||
|
// << ": " << modevent << LL_ENDL;
|
||||||
|
requestPump.getPump().post(modevent);
|
||||||
|
}
|
||||||
|
LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
|
||||||
|
<< " about to wait on LLEventPump " << replyPump.getPump().getName()
|
||||||
|
<< LL_ENDL;
|
||||||
|
// trying to dereference ("resolve") the future makes us wait for it
|
||||||
|
LLSD value(*future);
|
||||||
|
LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
|
||||||
|
<< " resuming with " << value << LL_ENDL;
|
||||||
|
// returning should disconnect the connection
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wait for the next event on the specified LLEventPump. Pass either the
|
||||||
|
/// LLEventPump& or its string name.
|
||||||
|
template <typename SELF>
|
||||||
|
LLSD waitForEventOn(SELF& self, const LLEventPumpOrPumpName& pump)
|
||||||
|
{
|
||||||
|
// This is now a convenience wrapper for postAndWait().
|
||||||
|
return postAndWait(self, LLSD(), LLEventPumpOrPumpName(), pump);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return type for two-pump variant of waitForEventOn()
|
||||||
|
typedef std::pair<LLSD, int> LLEventWithID;
|
||||||
|
|
||||||
|
namespace LLEventDetail
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This helper is specifically for the two-pump version of waitForEventOn().
|
||||||
|
* We use a single future object, but we want to listen on two pumps with it.
|
||||||
|
* Since we must still adapt from (the callable constructed by)
|
||||||
|
* boost::coroutines::make_callback() (void return) to provide an event
|
||||||
|
* listener (bool return), we've adapted LLVoidListener for the purpose. The
|
||||||
|
* basic idea is that we construct a distinct instance of WaitForEventOnHelper
|
||||||
|
* -- binding different instance data -- for each of the pumps. Then, when a
|
||||||
|
* pump delivers an LLSD value to either WaitForEventOnHelper, it can combine
|
||||||
|
* that LLSD with its discriminator to feed the future object.
|
||||||
|
*/
|
||||||
|
template <typename LISTENER>
|
||||||
|
class WaitForEventOnHelper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WaitForEventOnHelper(const LISTENER& listener, int discriminator):
|
||||||
|
mListener(listener),
|
||||||
|
mDiscrim(discriminator)
|
||||||
|
{}
|
||||||
|
// this signature is required for an LLEventPump listener
|
||||||
|
bool operator()(const LLSD& event)
|
||||||
|
{
|
||||||
|
// our future object is defined to accept LLEventWithID
|
||||||
|
mListener(LLEventWithID(event, mDiscrim));
|
||||||
|
// don't swallow the event, let other listeners see it
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
LISTENER mListener;
|
||||||
|
const int mDiscrim;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// WaitForEventOnHelper type-inference helper
|
||||||
|
template <typename LISTENER>
|
||||||
|
WaitForEventOnHelper<LISTENER> wfeoh(const LISTENER& listener, int discriminator)
|
||||||
|
{
|
||||||
|
return WaitForEventOnHelper<LISTENER>(listener, discriminator);
|
||||||
|
}
|
||||||
|
} // namespace LLEventDetail
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function waits for a reply on either of two specified LLEventPumps.
|
||||||
|
* Otherwise, it closely resembles postAndWait(); please see the documentation
|
||||||
|
* for that function for detailed parameter info.
|
||||||
|
*
|
||||||
|
* While we could have implemented the single-pump variant in terms of this
|
||||||
|
* one, there's enough added complexity here to make it worthwhile to give the
|
||||||
|
* single-pump variant its own straightforward implementation. Conversely,
|
||||||
|
* though we could use preprocessor logic to generate n-pump overloads up to
|
||||||
|
* BOOST_COROUTINE_WAIT_MAX, we don't foresee a use case. This two-pump
|
||||||
|
* overload exists because certain event APIs are defined in terms of a reply
|
||||||
|
* LLEventPump and an error LLEventPump.
|
||||||
|
*
|
||||||
|
* The LLEventWithID return value provides not only the received event, but
|
||||||
|
* the index of the pump on which it arrived (0 or 1).
|
||||||
|
*
|
||||||
|
* @note
|
||||||
|
* I'd have preferred to overload the name postAndWait() for both signatures.
|
||||||
|
* But consider the following ambiguous call:
|
||||||
|
* @code
|
||||||
|
* postAndWait(self, LLSD(), requestPump, replyPump, "someString");
|
||||||
|
* @endcode
|
||||||
|
* "someString" could be converted to either LLSD (@a replyPumpNamePath for
|
||||||
|
* the single-pump function) or LLEventOrPumpName (@a replyPump1 for two-pump
|
||||||
|
* function).
|
||||||
|
*
|
||||||
|
* It seems less burdensome to write postAndWait2() than to write either
|
||||||
|
* LLSD("someString") or LLEventOrPumpName("someString").
|
||||||
|
*/
|
||||||
|
template <typename SELF>
|
||||||
|
LLEventWithID postAndWait2(SELF& self, const LLSD& event,
|
||||||
|
const LLEventPumpOrPumpName& requestPump,
|
||||||
|
const LLEventPumpOrPumpName& replyPump0,
|
||||||
|
const LLEventPumpOrPumpName& replyPump1,
|
||||||
|
const LLSD& replyPump0NamePath=LLSD(),
|
||||||
|
const LLSD& replyPump1NamePath=LLSD())
|
||||||
|
{
|
||||||
|
// declare the future
|
||||||
|
boost::coroutines::future<LLEventWithID> future(self);
|
||||||
|
// either callback will assign a value to this future; listen on
|
||||||
|
// each specified LLEventPump with a callback
|
||||||
|
std::string name(LLEventDetail::listenerNameForCoro(self));
|
||||||
|
LLTempBoundListener connection0(
|
||||||
|
replyPump0.getPump().listen(name + "a",
|
||||||
|
LLEventDetail::wfeoh(boost::coroutines::make_callback(future), 0)));
|
||||||
|
LLTempBoundListener connection1(
|
||||||
|
replyPump1.getPump().listen(name + "b",
|
||||||
|
LLEventDetail::wfeoh(boost::coroutines::make_callback(future), 1)));
|
||||||
|
// skip the "post" part if requestPump is default-constructed
|
||||||
|
if (requestPump)
|
||||||
|
{
|
||||||
|
// If either replyPumpNamePath is non-empty, store the corresponding
|
||||||
|
// replyPump name in the request event.
|
||||||
|
LLSD modevent(event);
|
||||||
|
LLEventDetail::storeToLLSDPath(modevent, replyPump0NamePath,
|
||||||
|
replyPump0.getPump().getName());
|
||||||
|
LLEventDetail::storeToLLSDPath(modevent, replyPump1NamePath,
|
||||||
|
replyPump1.getPump().getName());
|
||||||
|
LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name
|
||||||
|
<< " posting to " << requestPump.getPump().getName()
|
||||||
|
<< ": " << modevent << LL_ENDL;
|
||||||
|
requestPump.getPump().post(modevent);
|
||||||
|
}
|
||||||
|
LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name
|
||||||
|
<< " about to wait on LLEventPumps " << replyPump0.getPump().getName()
|
||||||
|
<< ", " << replyPump1.getPump().getName() << LL_ENDL;
|
||||||
|
// trying to dereference ("resolve") the future makes us wait for it
|
||||||
|
LLEventWithID value(*future);
|
||||||
|
LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name
|
||||||
|
<< " resuming with (" << value.first << ", " << value.second << ")"
|
||||||
|
<< LL_ENDL;
|
||||||
|
// returning should disconnect both connections
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for the next event on either of two specified LLEventPumps.
|
||||||
|
*/
|
||||||
|
template <typename SELF>
|
||||||
|
LLEventWithID
|
||||||
|
waitForEventOn(SELF& self,
|
||||||
|
const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1)
|
||||||
|
{
|
||||||
|
// This is now a convenience wrapper for postAndWait2().
|
||||||
|
return postAndWait2(self, LLSD(), LLEventPumpOrPumpName(), pump0, pump1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper for the two-pump variant of waitForEventOn(), e.g.:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* LLSD reply = errorException(waitForEventOn(self, replyPump, errorPump),
|
||||||
|
* "error response from login.cgi");
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Examines an LLEventWithID, assuming that the second pump (pump 1) is
|
||||||
|
* listening for an error indication. If the incoming data arrived on pump 1,
|
||||||
|
* throw an LLErrorEvent exception. If the incoming data arrived on pump 0,
|
||||||
|
* just return it. Since a normal return can only be from pump 0, we no longer
|
||||||
|
* need the LLEventWithID's discriminator int; we can just return the LLSD.
|
||||||
|
*
|
||||||
|
* @note I'm not worried about introducing the (fairly generic) name
|
||||||
|
* errorException() into global namespace, because how many other overloads of
|
||||||
|
* the same name are going to accept an LLEventWithID parameter?
|
||||||
|
*/
|
||||||
|
LLSD errorException(const LLEventWithID& result, const std::string& desc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown by errorException(). We don't call this LLEventError
|
||||||
|
* because it's not an error in event processing: rather, this exception
|
||||||
|
* announces an event that bears error information (for some other API).
|
||||||
|
*/
|
||||||
|
class LL_COMMON_API LLErrorEvent: public std::runtime_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LLErrorEvent(const std::string& what, const LLSD& data):
|
||||||
|
std::runtime_error(what),
|
||||||
|
mData(data)
|
||||||
|
{}
|
||||||
|
virtual ~LLErrorEvent() throw() {}
|
||||||
|
|
||||||
|
LLSD getData() const { return mData; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
LLSD mData;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like errorException(), save that this trips a fatal error using LL_ERRS
|
||||||
|
* rather than throwing an exception.
|
||||||
|
*/
|
||||||
|
LL_COMMON_API LLSD errorLog(const LLEventWithID& result, const std::string& desc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Certain event APIs require the name of an LLEventPump on which they should
|
||||||
|
* post results. While it works to invent a distinct name and let
|
||||||
|
* LLEventPumps::obtain() instantiate the LLEventPump as a "named singleton,"
|
||||||
|
* in a certain sense it's more robust to instantiate a local LLEventPump and
|
||||||
|
* provide its name instead. This class packages the following idiom:
|
||||||
|
*
|
||||||
|
* 1. Instantiate a local LLCoroEventPump, with an optional name prefix.
|
||||||
|
* 2. Provide its actual name to the event API in question as the name of the
|
||||||
|
* reply LLEventPump.
|
||||||
|
* 3. Initiate the request to the event API.
|
||||||
|
* 4. Call your LLEventTempStream's wait() method to wait for the reply.
|
||||||
|
* 5. Let the LLCoroEventPump go out of scope.
|
||||||
|
*/
|
||||||
|
class LL_COMMON_API LLCoroEventPump
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LLCoroEventPump(const std::string& name="coro"):
|
||||||
|
mPump(name, true) // allow tweaking the pump instance name
|
||||||
|
{}
|
||||||
|
/// It's typical to request the LLEventPump name to direct an event API to
|
||||||
|
/// send its response to this pump.
|
||||||
|
std::string getName() const { return mPump.getName(); }
|
||||||
|
/// Less typically, we'd request the pump itself for some reason.
|
||||||
|
LLEventPump& getPump() { return mPump; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for an event on this LLEventPump.
|
||||||
|
*
|
||||||
|
* @note
|
||||||
|
* The other major usage pattern we considered was to bind @c self at
|
||||||
|
* LLCoroEventPump construction time, which would avoid passing the
|
||||||
|
* parameter to each wait() call. But if we were going to bind @c self as
|
||||||
|
* a class member, we'd need to specify a class template parameter
|
||||||
|
* indicating its type. The big advantage of passing it to the wait() call
|
||||||
|
* is that the type can be implicit.
|
||||||
|
*/
|
||||||
|
template <typename SELF>
|
||||||
|
LLSD wait(SELF& self)
|
||||||
|
{
|
||||||
|
return waitForEventOn(self, mPump);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename SELF>
|
||||||
|
LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump,
|
||||||
|
const LLSD& replyPumpNamePath=LLSD())
|
||||||
|
{
|
||||||
|
return ::postAndWait(self, event, requestPump, mPump, replyPumpNamePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
LLEventStream mPump;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Other event APIs require the names of two different LLEventPumps: one for
|
||||||
|
* success response, the other for error response. Extend LLCoroEventPump
|
||||||
|
* for the two-pump use case.
|
||||||
|
*/
|
||||||
|
class LL_COMMON_API LLCoroEventPumps
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LLCoroEventPumps(const std::string& name="coro",
|
||||||
|
const std::string& suff0="Reply",
|
||||||
|
const std::string& suff1="Error"):
|
||||||
|
mPump0(name + suff0, true), // allow tweaking the pump instance name
|
||||||
|
mPump1(name + suff1, true)
|
||||||
|
{}
|
||||||
|
/// request pump 0's name
|
||||||
|
std::string getName0() const { return mPump0.getName(); }
|
||||||
|
/// request pump 1's name
|
||||||
|
std::string getName1() const { return mPump1.getName(); }
|
||||||
|
/// request both names
|
||||||
|
std::pair<std::string, std::string> getNames() const
|
||||||
|
{
|
||||||
|
return std::pair<std::string, std::string>(mPump0.getName(), mPump1.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// request pump 0
|
||||||
|
LLEventPump& getPump0() { return mPump0; }
|
||||||
|
/// request pump 1
|
||||||
|
LLEventPump& getPump1() { return mPump1; }
|
||||||
|
|
||||||
|
/// waitForEventOn(self, either of our two LLEventPumps)
|
||||||
|
template <typename SELF>
|
||||||
|
LLEventWithID wait(SELF& self)
|
||||||
|
{
|
||||||
|
return waitForEventOn(self, mPump0, mPump1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// errorException(wait(self))
|
||||||
|
template <typename SELF>
|
||||||
|
LLSD waitWithException(SELF& self)
|
||||||
|
{
|
||||||
|
return errorException(wait(self), std::string("Error event on ") + getName1());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// errorLog(wait(self))
|
||||||
|
template <typename SELF>
|
||||||
|
LLSD waitWithLog(SELF& self)
|
||||||
|
{
|
||||||
|
return errorLog(wait(self), std::string("Error event on ") + getName1());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename SELF>
|
||||||
|
LLEventWithID postAndWait(SELF& self, const LLSD& event,
|
||||||
|
const LLEventPumpOrPumpName& requestPump,
|
||||||
|
const LLSD& replyPump0NamePath=LLSD(),
|
||||||
|
const LLSD& replyPump1NamePath=LLSD())
|
||||||
|
{
|
||||||
|
return postAndWait2(self, event, requestPump, mPump0, mPump1,
|
||||||
|
replyPump0NamePath, replyPump1NamePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename SELF>
|
||||||
|
LLSD postAndWaitWithException(SELF& self, const LLSD& event,
|
||||||
|
const LLEventPumpOrPumpName& requestPump,
|
||||||
|
const LLSD& replyPump0NamePath=LLSD(),
|
||||||
|
const LLSD& replyPump1NamePath=LLSD())
|
||||||
|
{
|
||||||
|
return errorException(postAndWait(self, event, requestPump,
|
||||||
|
replyPump0NamePath, replyPump1NamePath),
|
||||||
|
std::string("Error event on ") + getName1());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename SELF>
|
||||||
|
LLSD postAndWaitWithLog(SELF& self, const LLSD& event,
|
||||||
|
const LLEventPumpOrPumpName& requestPump,
|
||||||
|
const LLSD& replyPump0NamePath=LLSD(),
|
||||||
|
const LLSD& replyPump1NamePath=LLSD())
|
||||||
|
{
|
||||||
|
return errorLog(postAndWait(self, event, requestPump,
|
||||||
|
replyPump0NamePath, replyPump1NamePath),
|
||||||
|
std::string("Error event on ") + getName1());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
LLEventStream mPump0, mPump1;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* ! defined(LL_LLEVENTCORO_H) */
|
||||||
672
indra/llcommon/lleventdispatcher.cpp
Normal file
672
indra/llcommon/lleventdispatcher.cpp
Normal file
@@ -0,0 +1,672 @@
|
|||||||
|
/**
|
||||||
|
* @file lleventdispatcher.cpp
|
||||||
|
* @author Nat Goodspeed
|
||||||
|
* @date 2009-06-18
|
||||||
|
* @brief Implementation for lleventdispatcher.
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation;
|
||||||
|
* version 2.1 of the License only.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if LL_WINDOWS
|
||||||
|
#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Precompiled header
|
||||||
|
#include "linden_common.h"
|
||||||
|
// associated header
|
||||||
|
#include "lleventdispatcher.h"
|
||||||
|
// STL headers
|
||||||
|
// std headers
|
||||||
|
// external library headers
|
||||||
|
// other Linden headers
|
||||||
|
#include "llevents.h"
|
||||||
|
#include "llerror.h"
|
||||||
|
#include "llsdutil.h"
|
||||||
|
#include "stringize.h"
|
||||||
|
#include <memory> // std::auto_ptr
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* LLSDArgsSource
|
||||||
|
*****************************************************************************/
|
||||||
|
/**
|
||||||
|
* Store an LLSD array, producing its elements one at a time. Die with LL_ERRS
|
||||||
|
* if the consumer requests more elements than the array contains.
|
||||||
|
*/
|
||||||
|
class LL_COMMON_API LLSDArgsSource
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LLSDArgsSource(const std::string function, const LLSD& args);
|
||||||
|
~LLSDArgsSource();
|
||||||
|
|
||||||
|
LLSD next();
|
||||||
|
|
||||||
|
void done() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string _function;
|
||||||
|
LLSD _args;
|
||||||
|
LLSD::Integer _index;
|
||||||
|
};
|
||||||
|
|
||||||
|
LLSDArgsSource::LLSDArgsSource(const std::string function, const LLSD& args):
|
||||||
|
_function(function),
|
||||||
|
_args(args),
|
||||||
|
_index(0)
|
||||||
|
{
|
||||||
|
if (! (_args.isUndefined() || _args.isArray()))
|
||||||
|
{
|
||||||
|
LL_ERRS("LLSDArgsSource") << _function << " needs an args array instead of "
|
||||||
|
<< _args << LL_ENDL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LLSDArgsSource::~LLSDArgsSource()
|
||||||
|
{
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
|
||||||
|
LLSD LLSDArgsSource::next()
|
||||||
|
{
|
||||||
|
if (_index >= _args.size())
|
||||||
|
{
|
||||||
|
LL_ERRS("LLSDArgsSource") << _function << " requires more arguments than the "
|
||||||
|
<< _args.size() << " provided: " << _args << LL_ENDL;
|
||||||
|
}
|
||||||
|
return _args[_index++];
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLSDArgsSource::done() const
|
||||||
|
{
|
||||||
|
if (_index < _args.size())
|
||||||
|
{
|
||||||
|
LL_WARNS("LLSDArgsSource") << _function << " only consumed " << _index
|
||||||
|
<< " of the " << _args.size() << " arguments provided: "
|
||||||
|
<< _args << LL_ENDL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* LLSDArgsMapper
|
||||||
|
*****************************************************************************/
|
||||||
|
/**
|
||||||
|
* From a formal parameters description and a map of arguments, construct an
|
||||||
|
* arguments array.
|
||||||
|
*
|
||||||
|
* That is, given:
|
||||||
|
* - an LLSD array of length n containing parameter-name strings,
|
||||||
|
* corresponding to the arguments of a function of interest
|
||||||
|
* - an LLSD collection specifying default parameter values, either:
|
||||||
|
* - an LLSD array of length m <= n, matching the rightmost m params, or
|
||||||
|
* - an LLSD map explicitly stating default name=value pairs
|
||||||
|
* - an LLSD map of parameter names and actual values for a particular
|
||||||
|
* function call
|
||||||
|
* construct an LLSD array of actual argument values for this function call.
|
||||||
|
*
|
||||||
|
* The parameter-names array and the defaults collection describe the function
|
||||||
|
* being called. The map might vary with every call, providing argument values
|
||||||
|
* for the described parameters.
|
||||||
|
*
|
||||||
|
* The array of parameter names must match the number of parameters expected
|
||||||
|
* by the function of interest.
|
||||||
|
*
|
||||||
|
* If you pass a map of default parameter values, it provides default values
|
||||||
|
* as you might expect. It is an error to specify a default value for a name
|
||||||
|
* not listed in the parameters array.
|
||||||
|
*
|
||||||
|
* If you pass an array of default parameter values, it is mapped to the
|
||||||
|
* rightmost m of the n parameter names. It is an error if the default-values
|
||||||
|
* array is longer than the parameter-names array. Consider the following
|
||||||
|
* parameter names: ["a", "b", "c", "d"].
|
||||||
|
*
|
||||||
|
* - An empty array of default values (or an isUndefined() value) asserts that
|
||||||
|
* every one of the above parameter names is required.
|
||||||
|
* - An array of four default values [1, 2, 3, 4] asserts that every one of
|
||||||
|
* the above parameters is optional. If the current parameter map is empty,
|
||||||
|
* they will be passed to the function as [1, 2, 3, 4].
|
||||||
|
* - An array of two default values [11, 12] asserts that parameters "a" and
|
||||||
|
* "b" are required, while "c" and "d" are optional, having default values
|
||||||
|
* "c"=11 and "d"=12.
|
||||||
|
*
|
||||||
|
* The arguments array is constructed as follows:
|
||||||
|
*
|
||||||
|
* - Arguments-map keys not found in the parameter-names array are ignored.
|
||||||
|
* - Entries from the map provide values for an improper subset of the
|
||||||
|
* parameters named in the parameter-names array. This results in a
|
||||||
|
* tentative values array with "holes." (size of map) + (number of holes) =
|
||||||
|
* (size of names array)
|
||||||
|
* - Holes are filled with the default values.
|
||||||
|
* - Any remaining holes constitute an error.
|
||||||
|
*/
|
||||||
|
class LL_COMMON_API LLSDArgsMapper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Accept description of function: function name, param names, param
|
||||||
|
/// default values
|
||||||
|
LLSDArgsMapper(const std::string& function, const LLSD& names, const LLSD& defaults);
|
||||||
|
|
||||||
|
/// Given arguments map, return LLSD::Array of parameter values, or LL_ERRS.
|
||||||
|
LLSD map(const LLSD& argsmap) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::string formatlist(const LLSD&);
|
||||||
|
|
||||||
|
// The function-name string is purely descriptive. We want error messages
|
||||||
|
// to be able to indicate which function's LLSDArgsMapper has the problem.
|
||||||
|
std::string _function;
|
||||||
|
// Store the names array pretty much as given.
|
||||||
|
LLSD _names;
|
||||||
|
// Though we're handed an array of name strings, it's more useful to us to
|
||||||
|
// store it as a map from name string to position index. Of course that's
|
||||||
|
// easy to generate from the incoming names array, but why do it more than
|
||||||
|
// once?
|
||||||
|
typedef std::map<LLSD::String, LLSD::Integer> IndexMap;
|
||||||
|
IndexMap _indexes;
|
||||||
|
// Generated array of default values, aligned with the array of param names.
|
||||||
|
LLSD _defaults;
|
||||||
|
// Indicate whether we have a default value for each param.
|
||||||
|
typedef std::vector<char> FilledVector;
|
||||||
|
FilledVector _has_dft;
|
||||||
|
};
|
||||||
|
|
||||||
|
LLSDArgsMapper::LLSDArgsMapper(const std::string& function,
|
||||||
|
const LLSD& names, const LLSD& defaults):
|
||||||
|
_function(function),
|
||||||
|
_names(names),
|
||||||
|
_has_dft(names.size())
|
||||||
|
{
|
||||||
|
if (! (_names.isUndefined() || _names.isArray()))
|
||||||
|
{
|
||||||
|
LL_ERRS("LLSDArgsMapper") << function << " names must be an array, not " << names << LL_ENDL;
|
||||||
|
}
|
||||||
|
LLSD::Integer nparams(_names.size());
|
||||||
|
// From _names generate _indexes.
|
||||||
|
for (LLSD::Integer ni = 0, nend = _names.size(); ni < nend; ++ni)
|
||||||
|
{
|
||||||
|
_indexes[_names[ni]] = ni;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Presize _defaults() array so we don't have to resize it more than once.
|
||||||
|
// All entries are initialized to LLSD(); but since _has_dft is still all
|
||||||
|
// 0, they're all "holes" for now.
|
||||||
|
if (nparams)
|
||||||
|
{
|
||||||
|
_defaults[nparams - 1] = LLSD();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaults.isUndefined() || defaults.isArray())
|
||||||
|
{
|
||||||
|
LLSD::Integer ndefaults = defaults.size();
|
||||||
|
// defaults is a (possibly empty) array. Right-align it with names.
|
||||||
|
if (ndefaults > nparams)
|
||||||
|
{
|
||||||
|
LL_ERRS("LLSDArgsMapper") << function << " names array " << names
|
||||||
|
<< " shorter than defaults array " << defaults << LL_ENDL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset by which we slide defaults array right to right-align with
|
||||||
|
// _names array
|
||||||
|
LLSD::Integer offset = nparams - ndefaults;
|
||||||
|
// Fill rightmost _defaults entries from defaults, and mark them as
|
||||||
|
// filled
|
||||||
|
for (LLSD::Integer i = 0, iend = ndefaults; i < iend; ++i)
|
||||||
|
{
|
||||||
|
_defaults[i + offset] = defaults[i];
|
||||||
|
_has_dft[i + offset] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (defaults.isMap())
|
||||||
|
{
|
||||||
|
// defaults is a map. Use it to populate the _defaults array.
|
||||||
|
LLSD bogus;
|
||||||
|
for (LLSD::map_const_iterator mi(defaults.beginMap()), mend(defaults.endMap());
|
||||||
|
mi != mend; ++mi)
|
||||||
|
{
|
||||||
|
IndexMap::const_iterator ixit(_indexes.find(mi->first));
|
||||||
|
if (ixit == _indexes.end())
|
||||||
|
{
|
||||||
|
bogus.append(mi->first);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
LLSD::Integer pos = ixit->second;
|
||||||
|
// Store default value at that position in the _defaults array.
|
||||||
|
_defaults[pos] = mi->second;
|
||||||
|
// Don't forget to record the fact that we've filled this
|
||||||
|
// position.
|
||||||
|
_has_dft[pos] = 1;
|
||||||
|
}
|
||||||
|
if (bogus.size())
|
||||||
|
{
|
||||||
|
LL_ERRS("LLSDArgsMapper") << function << " defaults specified for nonexistent params "
|
||||||
|
<< formatlist(bogus) << LL_ENDL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LL_ERRS("LLSDArgsMapper") << function << " defaults must be a map or an array, not "
|
||||||
|
<< defaults << LL_ENDL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LLSD LLSDArgsMapper::map(const LLSD& argsmap) const
|
||||||
|
{
|
||||||
|
if (! (argsmap.isUndefined() || argsmap.isMap() || argsmap.isArray()))
|
||||||
|
{
|
||||||
|
LL_ERRS("LLSDArgsMapper") << _function << " map() needs a map or array, not "
|
||||||
|
<< argsmap << LL_ENDL;
|
||||||
|
}
|
||||||
|
// Initialize the args array. Indexing a non-const LLSD array grows it
|
||||||
|
// to appropriate size, but we don't want to resize this one on each
|
||||||
|
// new operation. Just make it as big as we need before we start
|
||||||
|
// stuffing values into it.
|
||||||
|
LLSD args(LLSD::emptyArray());
|
||||||
|
if (_defaults.size() == 0)
|
||||||
|
{
|
||||||
|
// If this function requires no arguments, fast exit. (Don't try to
|
||||||
|
// assign to args[-1].)
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
args[_defaults.size() - 1] = LLSD();
|
||||||
|
|
||||||
|
// Get a vector of chars to indicate holes. It's tempting to just scan
|
||||||
|
// for LLSD::isUndefined() values after filling the args array from
|
||||||
|
// the map, but it's plausible for caller to explicitly pass
|
||||||
|
// isUndefined() as the value of some parameter name. That's legal
|
||||||
|
// since isUndefined() has well-defined conversions (default value)
|
||||||
|
// for LLSD data types. So use a whole separate array for detecting
|
||||||
|
// holes. (Avoid std::vector<bool> which is known to be odd -- can we
|
||||||
|
// iterate?)
|
||||||
|
FilledVector filled(args.size());
|
||||||
|
|
||||||
|
if (argsmap.isArray())
|
||||||
|
{
|
||||||
|
// Fill args from array. If there are too many args in passed array,
|
||||||
|
// ignore the rest.
|
||||||
|
LLSD::Integer size(argsmap.size());
|
||||||
|
if (size > args.size())
|
||||||
|
{
|
||||||
|
// We don't just use std::min() because we want to sneak in this
|
||||||
|
// warning if caller passes too many args.
|
||||||
|
LL_WARNS("LLSDArgsMapper") << _function << " needs " << args.size()
|
||||||
|
<< " params, ignoring last " << (size - args.size())
|
||||||
|
<< " of passed " << size << ": " << argsmap << LL_ENDL;
|
||||||
|
size = args.size();
|
||||||
|
}
|
||||||
|
for (LLSD::Integer i(0); i < size; ++i)
|
||||||
|
{
|
||||||
|
// Copy the actual argument from argsmap
|
||||||
|
args[i] = argsmap[i];
|
||||||
|
// Note that it's been filled
|
||||||
|
filled[i] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// argsmap is in fact a map. Walk the map.
|
||||||
|
for (LLSD::map_const_iterator mi(argsmap.beginMap()), mend(argsmap.endMap());
|
||||||
|
mi != mend; ++mi)
|
||||||
|
{
|
||||||
|
// mi->first is a parameter-name string, with mi->second its
|
||||||
|
// value. Look up the name's position index in _indexes.
|
||||||
|
IndexMap::const_iterator ixit(_indexes.find(mi->first));
|
||||||
|
if (ixit == _indexes.end())
|
||||||
|
{
|
||||||
|
// Allow for a map containing more params than were passed in
|
||||||
|
// our names array. Caller typically receives a map containing
|
||||||
|
// the function name, cruft such as reqid, etc. Ignore keys
|
||||||
|
// not defined in _indexes.
|
||||||
|
LL_DEBUGS("LLSDArgsMapper") << _function << " ignoring "
|
||||||
|
<< mi->first << "=" << mi->second << LL_ENDL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
LLSD::Integer pos = ixit->second;
|
||||||
|
// Store the value at that position in the args array.
|
||||||
|
args[pos] = mi->second;
|
||||||
|
// Don't forget to record the fact that we've filled this
|
||||||
|
// position.
|
||||||
|
filled[pos] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill any remaining holes from _defaults.
|
||||||
|
LLSD unfilled(LLSD::emptyArray());
|
||||||
|
for (LLSD::Integer i = 0, iend = args.size(); i < iend; ++i)
|
||||||
|
{
|
||||||
|
if (! filled[i])
|
||||||
|
{
|
||||||
|
// If there's no default value for this parameter, that's an
|
||||||
|
// error.
|
||||||
|
if (! _has_dft[i])
|
||||||
|
{
|
||||||
|
unfilled.append(_names[i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
args[i] = _defaults[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If any required args -- args without defaults -- were left unfilled
|
||||||
|
// by argsmap, that's a problem.
|
||||||
|
if (unfilled.size())
|
||||||
|
{
|
||||||
|
LL_ERRS("LLSDArgsMapper") << _function << " missing required arguments "
|
||||||
|
<< formatlist(unfilled) << " from " << argsmap << LL_ENDL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// done
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LLSDArgsMapper::formatlist(const LLSD& list)
|
||||||
|
{
|
||||||
|
std::ostringstream out;
|
||||||
|
const char* delim = "";
|
||||||
|
for (LLSD::array_const_iterator li(list.beginArray()), lend(list.endArray());
|
||||||
|
li != lend; ++li)
|
||||||
|
{
|
||||||
|
out << delim << li->asString();
|
||||||
|
delim = ", ";
|
||||||
|
}
|
||||||
|
return out.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
LLEventDispatcher::LLEventDispatcher(const std::string& desc, const std::string& key):
|
||||||
|
mDesc(desc),
|
||||||
|
mKey(key)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LLEventDispatcher::~LLEventDispatcher()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DispatchEntry subclass used for callables accepting(const LLSD&)
|
||||||
|
*/
|
||||||
|
struct LLEventDispatcher::LLSDDispatchEntry: public LLEventDispatcher::DispatchEntry
|
||||||
|
{
|
||||||
|
LLSDDispatchEntry(const std::string& desc, const Callable& func, const LLSD& required):
|
||||||
|
DispatchEntry(desc),
|
||||||
|
mFunc(func),
|
||||||
|
mRequired(required)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Callable mFunc;
|
||||||
|
LLSD mRequired;
|
||||||
|
|
||||||
|
virtual void call(const std::string& desc, const LLSD& event) const
|
||||||
|
{
|
||||||
|
// Validate the syntax of the event itself.
|
||||||
|
std::string mismatch(llsd_matches(mRequired, event));
|
||||||
|
if (! mismatch.empty())
|
||||||
|
{
|
||||||
|
LL_ERRS("LLEventDispatcher") << desc << ": bad request: " << mismatch << LL_ENDL;
|
||||||
|
}
|
||||||
|
// Event syntax looks good, go for it!
|
||||||
|
mFunc(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual LLSD addMetadata(LLSD meta) const
|
||||||
|
{
|
||||||
|
meta["required"] = mRequired;
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DispatchEntry subclass for passing LLSD to functions accepting
|
||||||
|
* arbitrary argument types (convertible via LLSDParam)
|
||||||
|
*/
|
||||||
|
struct LLEventDispatcher::ParamsDispatchEntry: public LLEventDispatcher::DispatchEntry
|
||||||
|
{
|
||||||
|
ParamsDispatchEntry(const std::string& desc, const invoker_function& func):
|
||||||
|
DispatchEntry(desc),
|
||||||
|
mInvoker(func)
|
||||||
|
{}
|
||||||
|
|
||||||
|
invoker_function mInvoker;
|
||||||
|
|
||||||
|
virtual void call(const std::string& desc, const LLSD& event) const
|
||||||
|
{
|
||||||
|
LLSDArgsSource src(desc, event);
|
||||||
|
mInvoker(boost::bind(&LLSDArgsSource::next, boost::ref(src)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DispatchEntry subclass for dispatching LLSD::Array to functions accepting
|
||||||
|
* arbitrary argument types (convertible via LLSDParam)
|
||||||
|
*/
|
||||||
|
struct LLEventDispatcher::ArrayParamsDispatchEntry: public LLEventDispatcher::ParamsDispatchEntry
|
||||||
|
{
|
||||||
|
ArrayParamsDispatchEntry(const std::string& desc, const invoker_function& func,
|
||||||
|
LLSD::Integer arity):
|
||||||
|
ParamsDispatchEntry(desc, func),
|
||||||
|
mArity(arity)
|
||||||
|
{}
|
||||||
|
|
||||||
|
LLSD::Integer mArity;
|
||||||
|
|
||||||
|
virtual LLSD addMetadata(LLSD meta) const
|
||||||
|
{
|
||||||
|
LLSD array(LLSD::emptyArray());
|
||||||
|
// Resize to number of arguments required
|
||||||
|
if (mArity)
|
||||||
|
array[mArity - 1] = LLSD();
|
||||||
|
llassert_always(array.size() == mArity);
|
||||||
|
meta["required"] = array;
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DispatchEntry subclass for dispatching LLSD::Map to functions accepting
|
||||||
|
* arbitrary argument types (convertible via LLSDParam)
|
||||||
|
*/
|
||||||
|
struct LLEventDispatcher::MapParamsDispatchEntry: public LLEventDispatcher::ParamsDispatchEntry
|
||||||
|
{
|
||||||
|
MapParamsDispatchEntry(const std::string& name, const std::string& desc,
|
||||||
|
const invoker_function& func,
|
||||||
|
const LLSD& params, const LLSD& defaults):
|
||||||
|
ParamsDispatchEntry(desc, func),
|
||||||
|
mMapper(name, params, defaults),
|
||||||
|
mRequired(LLSD::emptyMap())
|
||||||
|
{
|
||||||
|
// Build the set of all param keys, then delete the ones that are
|
||||||
|
// optional. What's left are the ones that are required.
|
||||||
|
for (LLSD::array_const_iterator pi(params.beginArray()), pend(params.endArray());
|
||||||
|
pi != pend; ++pi)
|
||||||
|
{
|
||||||
|
mRequired[pi->asString()] = LLSD();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaults.isArray() || defaults.isUndefined())
|
||||||
|
{
|
||||||
|
// Right-align the params and defaults arrays.
|
||||||
|
LLSD::Integer offset = params.size() - defaults.size();
|
||||||
|
// Now the name of every defaults[i] is at params[i + offset].
|
||||||
|
for (LLSD::Integer i(0), iend(defaults.size()); i < iend; ++i)
|
||||||
|
{
|
||||||
|
// Erase this optional param from mRequired.
|
||||||
|
mRequired.erase(params[i + offset].asString());
|
||||||
|
// Instead, make an entry in mOptional with the default
|
||||||
|
// param's name and value.
|
||||||
|
mOptional[params[i + offset].asString()] = defaults[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (defaults.isMap())
|
||||||
|
{
|
||||||
|
// if defaults is already a map, then it's already in the form we
|
||||||
|
// intend to deliver in metadata
|
||||||
|
mOptional = defaults;
|
||||||
|
// Just delete from mRequired every key appearing in mOptional.
|
||||||
|
for (LLSD::map_const_iterator mi(mOptional.beginMap()), mend(mOptional.endMap());
|
||||||
|
mi != mend; ++mi)
|
||||||
|
{
|
||||||
|
mRequired.erase(mi->first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LLSDArgsMapper mMapper;
|
||||||
|
LLSD mRequired;
|
||||||
|
LLSD mOptional;
|
||||||
|
|
||||||
|
virtual void call(const std::string& desc, const LLSD& event) const
|
||||||
|
{
|
||||||
|
// Just convert from LLSD::Map to LLSD::Array using mMapper, then pass
|
||||||
|
// to base-class call() method.
|
||||||
|
ParamsDispatchEntry::call(desc, mMapper.map(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual LLSD addMetadata(LLSD meta) const
|
||||||
|
{
|
||||||
|
meta["required"] = mRequired;
|
||||||
|
meta["optional"] = mOptional;
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void LLEventDispatcher::addArrayParamsDispatchEntry(const std::string& name,
|
||||||
|
const std::string& desc,
|
||||||
|
const invoker_function& invoker,
|
||||||
|
LLSD::Integer arity)
|
||||||
|
{
|
||||||
|
mDispatch.insert(
|
||||||
|
DispatchMap::value_type(name, DispatchMap::mapped_type(
|
||||||
|
new ArrayParamsDispatchEntry(desc, invoker, arity))));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLEventDispatcher::addMapParamsDispatchEntry(const std::string& name,
|
||||||
|
const std::string& desc,
|
||||||
|
const invoker_function& invoker,
|
||||||
|
const LLSD& params,
|
||||||
|
const LLSD& defaults)
|
||||||
|
{
|
||||||
|
mDispatch.insert(
|
||||||
|
DispatchMap::value_type(name, DispatchMap::mapped_type(
|
||||||
|
new MapParamsDispatchEntry(name, desc, invoker, params, defaults))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register a callable by name
|
||||||
|
void LLEventDispatcher::add(const std::string& name, const std::string& desc,
|
||||||
|
const Callable& callable, const LLSD& required)
|
||||||
|
{
|
||||||
|
mDispatch.insert(
|
||||||
|
DispatchMap::value_type(name, DispatchMap::mapped_type(
|
||||||
|
new LLSDDispatchEntry(desc, callable, required))));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLEventDispatcher::addFail(const std::string& name, const std::string& classname) const
|
||||||
|
{
|
||||||
|
LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << ")::add(" << name
|
||||||
|
<< "): " << classname << " is not a subclass "
|
||||||
|
<< "of LLEventDispatcher" << LL_ENDL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unregister a callable
|
||||||
|
bool LLEventDispatcher::remove(const std::string& name)
|
||||||
|
{
|
||||||
|
DispatchMap::iterator found = mDispatch.find(name);
|
||||||
|
if (found == mDispatch.end())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mDispatch.erase(found);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call a registered callable with an explicitly-specified name. If no
|
||||||
|
/// such callable exists, die with LL_ERRS.
|
||||||
|
void LLEventDispatcher::operator()(const std::string& name, const LLSD& event) const
|
||||||
|
{
|
||||||
|
if (! try_call(name, event))
|
||||||
|
{
|
||||||
|
LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): '" << name
|
||||||
|
<< "' not found" << LL_ENDL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract the @a key value from the incoming @a event, and call the
|
||||||
|
/// callable whose name is specified by that map @a key. If no such
|
||||||
|
/// callable exists, die with LL_ERRS.
|
||||||
|
void LLEventDispatcher::operator()(const LLSD& event) const
|
||||||
|
{
|
||||||
|
// This could/should be implemented in terms of the two-arg overload.
|
||||||
|
// However -- we can produce a more informative error message.
|
||||||
|
std::string name(event[mKey]);
|
||||||
|
if (! try_call(name, event))
|
||||||
|
{
|
||||||
|
LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): bad " << mKey
|
||||||
|
<< " value '" << name << "'" << LL_ENDL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LLEventDispatcher::try_call(const LLSD& event) const
|
||||||
|
{
|
||||||
|
return try_call(event[mKey], event);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LLEventDispatcher::try_call(const std::string& name, const LLSD& event) const
|
||||||
|
{
|
||||||
|
DispatchMap::const_iterator found = mDispatch.find(name);
|
||||||
|
if (found == mDispatch.end())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Found the name, so it's plausible to even attempt the call.
|
||||||
|
found->second->call(STRINGIZE("LLEventDispatcher(" << mDesc << ") calling '" << name << "'"),
|
||||||
|
event);
|
||||||
|
return true; // tell caller we were able to call
|
||||||
|
}
|
||||||
|
|
||||||
|
LLSD LLEventDispatcher::getMetadata(const std::string& name) const
|
||||||
|
{
|
||||||
|
DispatchMap::const_iterator found = mDispatch.find(name);
|
||||||
|
if (found == mDispatch.end())
|
||||||
|
{
|
||||||
|
return LLSD();
|
||||||
|
}
|
||||||
|
LLSD meta;
|
||||||
|
meta["name"] = name;
|
||||||
|
meta["desc"] = found->second->mDesc;
|
||||||
|
return found->second->addMetadata(meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLDispatchListener::LLDispatchListener(const std::string& pumpname, const std::string& key):
|
||||||
|
LLEventDispatcher(pumpname, key),
|
||||||
|
mPump(pumpname, true), // allow tweaking for uniqueness
|
||||||
|
mBoundListener(mPump.listen("self", boost::bind(&LLDispatchListener::process, this, _1)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LLDispatchListener::process(const LLSD& event)
|
||||||
|
{
|
||||||
|
(*this)(event);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LLEventDispatcher::DispatchEntry::DispatchEntry(const std::string& desc):
|
||||||
|
mDesc(desc)
|
||||||
|
{}
|
||||||
|
|
||||||
541
indra/llcommon/lleventdispatcher.h
Normal file
541
indra/llcommon/lleventdispatcher.h
Normal file
@@ -0,0 +1,541 @@
|
|||||||
|
/**
|
||||||
|
* @file lleventdispatcher.h
|
||||||
|
* @author Nat Goodspeed
|
||||||
|
* @date 2009-06-18
|
||||||
|
* @brief Central mechanism for dispatching events by string name. This is
|
||||||
|
* useful when you have a single LLEventPump listener on which you can
|
||||||
|
* request different operations, vs. instantiating a different
|
||||||
|
* LLEventPump for each such operation.
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation;
|
||||||
|
* version 2.1 of the License only.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*
|
||||||
|
* The invoker machinery that constructs a boost::fusion argument list for use
|
||||||
|
* with boost::fusion::invoke() is derived from
|
||||||
|
* http://www.boost.org/doc/libs/1_45_0/libs/function_types/example/interpreter.hpp
|
||||||
|
* whose license information is copied below:
|
||||||
|
*
|
||||||
|
* "(C) Copyright Tobias Schwinger
|
||||||
|
*
|
||||||
|
* Use modification and distribution are subject to the boost Software License,
|
||||||
|
* Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt)."
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if ! defined(LL_LLEVENTDISPATCHER_H)
|
||||||
|
#define LL_LLEVENTDISPATCHER_H
|
||||||
|
|
||||||
|
// nil is too generic a term to be allowed to be a global macro. In
|
||||||
|
// particular, boost::fusion defines a 'class nil' (properly encapsulated in a
|
||||||
|
// namespace) that a global 'nil' macro breaks badly.
|
||||||
|
#if defined(nil)
|
||||||
|
// Capture the value of the macro 'nil', hoping int is an appropriate type.
|
||||||
|
static const int nil_(nil);
|
||||||
|
// Now forget the macro.
|
||||||
|
#undef nil
|
||||||
|
// Finally, reintroduce 'nil' as a properly-scoped alias for the previously-
|
||||||
|
// defined const 'nil_'. Make it static since otherwise it produces duplicate-
|
||||||
|
// symbol link errors later.
|
||||||
|
static const int& nil(nil_);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
#include <boost/function.hpp>
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
#include <boost/iterator/transform_iterator.hpp>
|
||||||
|
#include <boost/utility/enable_if.hpp>
|
||||||
|
#include <boost/function_types/is_nonmember_callable_builtin.hpp>
|
||||||
|
#include <boost/function_types/parameter_types.hpp>
|
||||||
|
#include <boost/function_types/function_arity.hpp>
|
||||||
|
#include <boost/type_traits/remove_cv.hpp>
|
||||||
|
#include <boost/type_traits/remove_reference.hpp>
|
||||||
|
#include <boost/fusion/include/push_back.hpp>
|
||||||
|
#include <boost/fusion/include/cons.hpp>
|
||||||
|
#include <boost/fusion/include/invoke.hpp>
|
||||||
|
#include <boost/mpl/begin.hpp>
|
||||||
|
#include <boost/mpl/end.hpp>
|
||||||
|
#include <boost/mpl/next.hpp>
|
||||||
|
#include <boost/mpl/deref.hpp>
|
||||||
|
#include <typeinfo>
|
||||||
|
#include "llevents.h"
|
||||||
|
#include "llsdutil.h"
|
||||||
|
|
||||||
|
class LLSD;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an LLSD map, examine a string-valued key and call a corresponding
|
||||||
|
* callable. This class is designed to be contained by an LLEventPump
|
||||||
|
* listener class that will register some of its own methods, though any
|
||||||
|
* callable can be used.
|
||||||
|
*/
|
||||||
|
class LL_COMMON_API LLEventDispatcher
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LLEventDispatcher(const std::string& desc, const std::string& key);
|
||||||
|
virtual ~LLEventDispatcher();
|
||||||
|
|
||||||
|
/// @name Register functions accepting(const LLSD&)
|
||||||
|
//@{
|
||||||
|
|
||||||
|
/// Accept any C++ callable with the right signature, typically a
|
||||||
|
/// boost::bind() expression
|
||||||
|
typedef boost::function<void(const LLSD&)> Callable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a @a callable by @a name. The passed @a callable accepts a
|
||||||
|
* single LLSD value and uses it in any way desired, e.g. extract
|
||||||
|
* parameters and call some other function. The optional @a required
|
||||||
|
* parameter is used to validate the structure of each incoming event (see
|
||||||
|
* llsd_matches()).
|
||||||
|
*/
|
||||||
|
void add(const std::string& name,
|
||||||
|
const std::string& desc,
|
||||||
|
const Callable& callable,
|
||||||
|
const LLSD& required=LLSD());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The case of a free function (or static method) accepting(const LLSD&)
|
||||||
|
* could also be intercepted by the arbitrary-args overload below. Ensure
|
||||||
|
* that it's directed to the Callable overload above instead.
|
||||||
|
*/
|
||||||
|
void add(const std::string& name,
|
||||||
|
const std::string& desc,
|
||||||
|
void (*f)(const LLSD&),
|
||||||
|
const LLSD& required=LLSD())
|
||||||
|
{
|
||||||
|
add(name, desc, Callable(f), required);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special case: a subclass of this class can pass an unbound member
|
||||||
|
* function pointer (of an LLEventDispatcher subclass) without explicitly
|
||||||
|
* specifying the <tt>boost::bind()</tt> expression. The passed @a method
|
||||||
|
* accepts a single LLSD value, presumably containing other parameters.
|
||||||
|
*/
|
||||||
|
template <class CLASS>
|
||||||
|
void add(const std::string& name,
|
||||||
|
const std::string& desc,
|
||||||
|
void (CLASS::*method)(const LLSD&),
|
||||||
|
const LLSD& required=LLSD())
|
||||||
|
{
|
||||||
|
addMethod<CLASS>(name, desc, method, required);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Overload for both const and non-const methods. The passed @a method
|
||||||
|
/// accepts a single LLSD value, presumably containing other parameters.
|
||||||
|
template <class CLASS>
|
||||||
|
void add(const std::string& name,
|
||||||
|
const std::string& desc,
|
||||||
|
void (CLASS::*method)(const LLSD&) const,
|
||||||
|
const LLSD& required=LLSD())
|
||||||
|
{
|
||||||
|
addMethod<CLASS>(name, desc, method, required);
|
||||||
|
}
|
||||||
|
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/// @name Register functions with arbitrary param lists
|
||||||
|
//@{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a free function with arbitrary parameters. (This also works
|
||||||
|
* for static class methods.)
|
||||||
|
*
|
||||||
|
* @note This supports functions with up to about 6 parameters -- after
|
||||||
|
* that you start getting dismaying compile errors in which
|
||||||
|
* boost::fusion::joint_view is mentioned a surprising number of times.
|
||||||
|
*
|
||||||
|
* When calling this name, pass an LLSD::Array. Each entry in turn will be
|
||||||
|
* converted to the corresponding parameter type using LLSDParam.
|
||||||
|
*/
|
||||||
|
template<typename Function>
|
||||||
|
typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function>
|
||||||
|
>::type add(const std::string& name,
|
||||||
|
const std::string& desc,
|
||||||
|
Function f);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a nonstatic class method with arbitrary parameters.
|
||||||
|
*
|
||||||
|
* @note This supports functions with up to about 6 parameters -- after
|
||||||
|
* that you start getting dismaying compile errors in which
|
||||||
|
* boost::fusion::joint_view is mentioned a surprising number of times.
|
||||||
|
*
|
||||||
|
* To cover cases such as a method on an LLSingleton we don't yet want to
|
||||||
|
* instantiate, instead of directly storing an instance pointer, accept a
|
||||||
|
* nullary callable returning a pointer/reference to the desired class
|
||||||
|
* instance. If you already have an instance in hand,
|
||||||
|
* boost::lambda::var(instance) or boost::lambda::constant(instance_ptr)
|
||||||
|
* produce suitable callables.
|
||||||
|
*
|
||||||
|
* When calling this name, pass an LLSD::Array. Each entry in turn will be
|
||||||
|
* converted to the corresponding parameter type using LLSDParam.
|
||||||
|
*/
|
||||||
|
template<typename Method, typename InstanceGetter>
|
||||||
|
typename boost::enable_if< boost::function_types::is_member_function_pointer<Method>
|
||||||
|
>::type add(const std::string& name,
|
||||||
|
const std::string& desc,
|
||||||
|
Method f,
|
||||||
|
const InstanceGetter& getter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a free function with arbitrary parameters. (This also works
|
||||||
|
* for static class methods.)
|
||||||
|
*
|
||||||
|
* @note This supports functions with up to about 6 parameters -- after
|
||||||
|
* that you start getting dismaying compile errors in which
|
||||||
|
* boost::fusion::joint_view is mentioned a surprising number of times.
|
||||||
|
*
|
||||||
|
* Pass an LLSD::Array of parameter names, and optionally another
|
||||||
|
* LLSD::Array of default parameter values, a la LLSDArgsMapper.
|
||||||
|
*
|
||||||
|
* When calling this name, pass an LLSD::Map. We will internally generate
|
||||||
|
* an LLSD::Array using LLSDArgsMapper and then convert each entry in turn
|
||||||
|
* to the corresponding parameter type using LLSDParam.
|
||||||
|
*/
|
||||||
|
template<typename Function>
|
||||||
|
typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function>
|
||||||
|
>::type add(const std::string& name,
|
||||||
|
const std::string& desc,
|
||||||
|
Function f,
|
||||||
|
const LLSD& params,
|
||||||
|
const LLSD& defaults=LLSD());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a nonstatic class method with arbitrary parameters.
|
||||||
|
*
|
||||||
|
* @note This supports functions with up to about 6 parameters -- after
|
||||||
|
* that you start getting dismaying compile errors in which
|
||||||
|
* boost::fusion::joint_view is mentioned a surprising number of times.
|
||||||
|
*
|
||||||
|
* To cover cases such as a method on an LLSingleton we don't yet want to
|
||||||
|
* instantiate, instead of directly storing an instance pointer, accept a
|
||||||
|
* nullary callable returning a pointer/reference to the desired class
|
||||||
|
* instance. If you already have an instance in hand,
|
||||||
|
* boost::lambda::var(instance) or boost::lambda::constant(instance_ptr)
|
||||||
|
* produce suitable callables.
|
||||||
|
*
|
||||||
|
* Pass an LLSD::Array of parameter names, and optionally another
|
||||||
|
* LLSD::Array of default parameter values, a la LLSDArgsMapper.
|
||||||
|
*
|
||||||
|
* When calling this name, pass an LLSD::Map. We will internally generate
|
||||||
|
* an LLSD::Array using LLSDArgsMapper and then convert each entry in turn
|
||||||
|
* to the corresponding parameter type using LLSDParam.
|
||||||
|
*/
|
||||||
|
template<typename Method, typename InstanceGetter>
|
||||||
|
typename boost::enable_if< boost::function_types::is_member_function_pointer<Method>
|
||||||
|
>::type add(const std::string& name,
|
||||||
|
const std::string& desc,
|
||||||
|
Method f,
|
||||||
|
const InstanceGetter& getter,
|
||||||
|
const LLSD& params,
|
||||||
|
const LLSD& defaults=LLSD());
|
||||||
|
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/// Unregister a callable
|
||||||
|
bool remove(const std::string& name);
|
||||||
|
|
||||||
|
/// Call a registered callable with an explicitly-specified name. If no
|
||||||
|
/// such callable exists, die with LL_ERRS. If the @a event fails to match
|
||||||
|
/// the @a required prototype specified at add() time, die with LL_ERRS.
|
||||||
|
void operator()(const std::string& name, const LLSD& event) const;
|
||||||
|
|
||||||
|
/// Call a registered callable with an explicitly-specified name and
|
||||||
|
/// return <tt>true</tt>. If no such callable exists, return
|
||||||
|
/// <tt>false</tt>. If the @a event fails to match the @a required
|
||||||
|
/// prototype specified at add() time, die with LL_ERRS.
|
||||||
|
bool try_call(const std::string& name, const LLSD& event) const;
|
||||||
|
|
||||||
|
/// Extract the @a key value from the incoming @a event, and call the
|
||||||
|
/// callable whose name is specified by that map @a key. If no such
|
||||||
|
/// callable exists, die with LL_ERRS. If the @a event fails to match the
|
||||||
|
/// @a required prototype specified at add() time, die with LL_ERRS.
|
||||||
|
void operator()(const LLSD& event) const;
|
||||||
|
|
||||||
|
/// Extract the @a key value from the incoming @a event, call the callable
|
||||||
|
/// whose name is specified by that map @a key and return <tt>true</tt>.
|
||||||
|
/// If no such callable exists, return <tt>false</tt>. If the @a event
|
||||||
|
/// fails to match the @a required prototype specified at add() time, die
|
||||||
|
/// with LL_ERRS.
|
||||||
|
bool try_call(const LLSD& event) const;
|
||||||
|
|
||||||
|
/// @name Iterate over defined names
|
||||||
|
//@{
|
||||||
|
typedef std::pair<std::string, std::string> NameDesc;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct DispatchEntry
|
||||||
|
{
|
||||||
|
DispatchEntry(const std::string& desc);
|
||||||
|
virtual ~DispatchEntry() {} // suppress MSVC warning, sigh
|
||||||
|
|
||||||
|
std::string mDesc;
|
||||||
|
|
||||||
|
virtual void call(const std::string& desc, const LLSD& event) const = 0;
|
||||||
|
virtual LLSD addMetadata(LLSD) const = 0;
|
||||||
|
};
|
||||||
|
// Tried using boost::ptr_map<std::string, DispatchEntry>, but ptr_map<>
|
||||||
|
// wants its value type to be "clonable," even just to dereference an
|
||||||
|
// iterator. I don't want to clone entries -- if I have to copy an entry
|
||||||
|
// around, I want it to continue pointing to the same DispatchEntry
|
||||||
|
// subclass object. However, I definitely want DispatchMap to destroy
|
||||||
|
// DispatchEntry if no references are outstanding at the time an entry is
|
||||||
|
// removed. This looks like a job for boost::shared_ptr.
|
||||||
|
typedef std::map<std::string, boost::shared_ptr<DispatchEntry> > DispatchMap;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// We want the flexibility to redefine what data we store per name,
|
||||||
|
/// therefore our public interface doesn't expose DispatchMap iterators,
|
||||||
|
/// or DispatchMap itself, or DispatchEntry. Instead we explicitly
|
||||||
|
/// transform each DispatchMap item to NameDesc on dereferencing.
|
||||||
|
typedef boost::transform_iterator<NameDesc(*)(const DispatchMap::value_type&), DispatchMap::const_iterator> const_iterator;
|
||||||
|
const_iterator begin() const
|
||||||
|
{
|
||||||
|
return boost::make_transform_iterator(mDispatch.begin(), makeNameDesc);
|
||||||
|
}
|
||||||
|
const_iterator end() const
|
||||||
|
{
|
||||||
|
return boost::make_transform_iterator(mDispatch.end(), makeNameDesc);
|
||||||
|
}
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/// Get information about a specific Callable
|
||||||
|
LLSD getMetadata(const std::string& name) const;
|
||||||
|
|
||||||
|
/// Retrieve the LLSD key we use for one-arg <tt>operator()</tt> method
|
||||||
|
std::string getDispatchKey() const { return mKey; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <class CLASS, typename METHOD>
|
||||||
|
void addMethod(const std::string& name, const std::string& desc,
|
||||||
|
const METHOD& method, const LLSD& required)
|
||||||
|
{
|
||||||
|
CLASS* downcast = dynamic_cast<CLASS*>(this);
|
||||||
|
if (! downcast)
|
||||||
|
{
|
||||||
|
addFail(name, typeid(CLASS).name());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
add(name, desc, boost::bind(method, downcast, _1), required);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void addFail(const std::string& name, const std::string& classname) const;
|
||||||
|
|
||||||
|
std::string mDesc, mKey;
|
||||||
|
DispatchMap mDispatch;
|
||||||
|
|
||||||
|
static NameDesc makeNameDesc(const DispatchMap::value_type& item)
|
||||||
|
{
|
||||||
|
return NameDesc(item.first, item.second->mDesc);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LLSDDispatchEntry;
|
||||||
|
struct ParamsDispatchEntry;
|
||||||
|
struct ArrayParamsDispatchEntry;
|
||||||
|
struct MapParamsDispatchEntry;
|
||||||
|
|
||||||
|
// Step 2 of parameter analysis. Instantiating invoker<some_function_type>
|
||||||
|
// implicitly sets its From and To parameters to the (compile time) begin
|
||||||
|
// and end iterators over that function's parameter types.
|
||||||
|
template< typename Function
|
||||||
|
, class From = typename boost::mpl::begin< boost::function_types::parameter_types<Function> >::type
|
||||||
|
, class To = typename boost::mpl::end< boost::function_types::parameter_types<Function> >::type
|
||||||
|
>
|
||||||
|
struct invoker;
|
||||||
|
|
||||||
|
// deliver LLSD arguments one at a time
|
||||||
|
typedef boost::function<LLSD()> args_source;
|
||||||
|
// obtain args from an args_source to build param list and call target
|
||||||
|
// function
|
||||||
|
typedef boost::function<void(const args_source&)> invoker_function;
|
||||||
|
|
||||||
|
template <typename Function>
|
||||||
|
invoker_function make_invoker(Function f);
|
||||||
|
template <typename Method, typename InstanceGetter>
|
||||||
|
invoker_function make_invoker(Method f, const InstanceGetter& getter);
|
||||||
|
void addArrayParamsDispatchEntry(const std::string& name,
|
||||||
|
const std::string& desc,
|
||||||
|
const invoker_function& invoker,
|
||||||
|
LLSD::Integer arity);
|
||||||
|
void addMapParamsDispatchEntry(const std::string& name,
|
||||||
|
const std::string& desc,
|
||||||
|
const invoker_function& invoker,
|
||||||
|
const LLSD& params,
|
||||||
|
const LLSD& defaults);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* LLEventDispatcher template implementation details
|
||||||
|
*****************************************************************************/
|
||||||
|
// Step 3 of parameter analysis, the recursive case.
|
||||||
|
template<typename Function, class From, class To>
|
||||||
|
struct LLEventDispatcher::invoker
|
||||||
|
{
|
||||||
|
template<typename T>
|
||||||
|
struct remove_cv_ref
|
||||||
|
: boost::remove_cv< typename boost::remove_reference<T>::type >
|
||||||
|
{ };
|
||||||
|
|
||||||
|
// apply() accepts an arbitrary boost::fusion sequence as args. It
|
||||||
|
// examines the next parameter type in the parameter-types sequence
|
||||||
|
// bounded by From and To, obtains the next LLSD object from the passed
|
||||||
|
// args_source and constructs an LLSDParam of appropriate type to try
|
||||||
|
// to convert the value. It then recurs with the next parameter-types
|
||||||
|
// iterator, passing the args sequence thus far.
|
||||||
|
template<typename Args>
|
||||||
|
static inline
|
||||||
|
void apply(Function func, const args_source& argsrc, Args const & args)
|
||||||
|
{
|
||||||
|
typedef typename boost::mpl::deref<From>::type arg_type;
|
||||||
|
typedef typename boost::mpl::next<From>::type next_iter_type;
|
||||||
|
typedef typename remove_cv_ref<arg_type>::type plain_arg_type;
|
||||||
|
|
||||||
|
invoker<Function, next_iter_type, To>::apply
|
||||||
|
( func, argsrc, boost::fusion::push_back(args, LLSDParam<plain_arg_type>(argsrc())));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special treatment for instance (first) parameter of a non-static member
|
||||||
|
// function. Accept the instance-getter callable, calling that to produce
|
||||||
|
// the first args value. Since we know we're at the top of the recursion
|
||||||
|
// chain, we need not also require a partial args sequence from our caller.
|
||||||
|
template <typename InstanceGetter>
|
||||||
|
static inline
|
||||||
|
void method_apply(Function func, const args_source& argsrc, const InstanceGetter& getter)
|
||||||
|
{
|
||||||
|
typedef typename boost::mpl::next<From>::type next_iter_type;
|
||||||
|
|
||||||
|
// Instead of grabbing the first item from argsrc and making an
|
||||||
|
// LLSDParam of it, call getter() and pass that as the instance param.
|
||||||
|
invoker<Function, next_iter_type, To>::apply
|
||||||
|
( func, argsrc, boost::fusion::push_back(boost::fusion::nil(), boost::ref(getter())));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Step 4 of parameter analysis, the leaf case. When the general
|
||||||
|
// invoker<Function, From, To> logic has advanced From until it matches To,
|
||||||
|
// the compiler will pick this template specialization.
|
||||||
|
template<typename Function, class To>
|
||||||
|
struct LLEventDispatcher::invoker<Function,To,To>
|
||||||
|
{
|
||||||
|
// the argument list is complete, now call the function
|
||||||
|
template<typename Args>
|
||||||
|
static inline
|
||||||
|
void apply(Function func, const args_source&, Args const & args)
|
||||||
|
{
|
||||||
|
boost::fusion::invoke(func, args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Function>
|
||||||
|
typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function> >::type
|
||||||
|
LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f)
|
||||||
|
{
|
||||||
|
// Construct an invoker_function, a callable accepting const args_source&.
|
||||||
|
// Add to DispatchMap an ArrayParamsDispatchEntry that will handle the
|
||||||
|
// caller's LLSD::Array.
|
||||||
|
addArrayParamsDispatchEntry(name, desc, make_invoker(f),
|
||||||
|
boost::function_types::function_arity<Function>::value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Method, typename InstanceGetter>
|
||||||
|
typename boost::enable_if< boost::function_types::is_member_function_pointer<Method> >::type
|
||||||
|
LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f,
|
||||||
|
const InstanceGetter& getter)
|
||||||
|
{
|
||||||
|
// Subtract 1 from the compile-time arity because the getter takes care of
|
||||||
|
// the first parameter. We only need (arity - 1) additional arguments.
|
||||||
|
addArrayParamsDispatchEntry(name, desc, make_invoker(f, getter),
|
||||||
|
boost::function_types::function_arity<Method>::value - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Function>
|
||||||
|
typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function> >::type
|
||||||
|
LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f,
|
||||||
|
const LLSD& params, const LLSD& defaults)
|
||||||
|
{
|
||||||
|
// See comments for previous is_nonmember_callable_builtin add().
|
||||||
|
addMapParamsDispatchEntry(name, desc, make_invoker(f), params, defaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Method, typename InstanceGetter>
|
||||||
|
typename boost::enable_if< boost::function_types::is_member_function_pointer<Method> >::type
|
||||||
|
LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f,
|
||||||
|
const InstanceGetter& getter,
|
||||||
|
const LLSD& params, const LLSD& defaults)
|
||||||
|
{
|
||||||
|
addMapParamsDispatchEntry(name, desc, make_invoker(f, getter), params, defaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Function>
|
||||||
|
LLEventDispatcher::invoker_function
|
||||||
|
LLEventDispatcher::make_invoker(Function f)
|
||||||
|
{
|
||||||
|
// Step 1 of parameter analysis, the top of the recursion. Passing a
|
||||||
|
// suitable f (see add()'s enable_if condition) to this method causes it
|
||||||
|
// to infer the function type; specifying that function type to invoker<>
|
||||||
|
// causes it to fill in the begin/end MPL iterators over the function's
|
||||||
|
// list of parameter types.
|
||||||
|
// While normally invoker::apply() could infer its template type from the
|
||||||
|
// boost::fusion::nil parameter value, here we must be explicit since
|
||||||
|
// we're boost::bind()ing it rather than calling it directly.
|
||||||
|
return boost::bind(&invoker<Function>::template apply<boost::fusion::nil>,
|
||||||
|
f,
|
||||||
|
_1,
|
||||||
|
boost::fusion::nil());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Method, typename InstanceGetter>
|
||||||
|
LLEventDispatcher::invoker_function
|
||||||
|
LLEventDispatcher::make_invoker(Method f, const InstanceGetter& getter)
|
||||||
|
{
|
||||||
|
// Use invoker::method_apply() to treat the instance (first) arg specially.
|
||||||
|
return boost::bind(&invoker<Method>::template method_apply<InstanceGetter>,
|
||||||
|
f,
|
||||||
|
_1,
|
||||||
|
getter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* LLDispatchListener
|
||||||
|
*****************************************************************************/
|
||||||
|
/**
|
||||||
|
* Bundle an LLEventPump and a listener with an LLEventDispatcher. A class
|
||||||
|
* that contains (or derives from) LLDispatchListener need only specify the
|
||||||
|
* LLEventPump name and dispatch key, and add() its methods. Incoming events
|
||||||
|
* will automatically be dispatched.
|
||||||
|
*/
|
||||||
|
class LL_COMMON_API LLDispatchListener: public LLEventDispatcher
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LLDispatchListener(const std::string& pumpname, const std::string& key);
|
||||||
|
|
||||||
|
std::string getPumpName() const { return mPump.getName(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool process(const LLSD& event);
|
||||||
|
|
||||||
|
LLEventStream mPump;
|
||||||
|
LLTempBoundListener mBoundListener;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* ! defined(LL_LLEVENTDISPATCHER_H) */
|
||||||
166
indra/llcommon/lleventfilter.cpp
Normal file
166
indra/llcommon/lleventfilter.cpp
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
/**
|
||||||
|
* @file lleventfilter.cpp
|
||||||
|
* @author Nat Goodspeed
|
||||||
|
* @date 2009-03-05
|
||||||
|
* @brief Implementation for lleventfilter.
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation;
|
||||||
|
* version 2.1 of the License only.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Precompiled header
|
||||||
|
#include "linden_common.h"
|
||||||
|
// associated header
|
||||||
|
#include "lleventfilter.h"
|
||||||
|
// STL headers
|
||||||
|
// std headers
|
||||||
|
// external library headers
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
// other Linden headers
|
||||||
|
#include "llerror.h" // LL_ERRS
|
||||||
|
#include "llsdutil.h" // llsd_matches()
|
||||||
|
|
||||||
|
LLEventFilter::LLEventFilter(LLEventPump& source, const std::string& name, bool tweak):
|
||||||
|
LLEventStream(name, tweak)
|
||||||
|
{
|
||||||
|
source.listen(getName(), boost::bind(&LLEventFilter::post, this, _1));
|
||||||
|
}
|
||||||
|
|
||||||
|
LLEventMatching::LLEventMatching(const LLSD& pattern):
|
||||||
|
LLEventFilter("matching"),
|
||||||
|
mPattern(pattern)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LLEventMatching::LLEventMatching(LLEventPump& source, const LLSD& pattern):
|
||||||
|
LLEventFilter(source, "matching"),
|
||||||
|
mPattern(pattern)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LLEventMatching::post(const LLSD& event)
|
||||||
|
{
|
||||||
|
if (! llsd_matches(mPattern, event).empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return LLEventStream::post(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLEventTimeoutBase::LLEventTimeoutBase():
|
||||||
|
LLEventFilter("timeout")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LLEventTimeoutBase::LLEventTimeoutBase(LLEventPump& source):
|
||||||
|
LLEventFilter(source, "timeout")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLEventTimeoutBase::actionAfter(F32 seconds, const Action& action)
|
||||||
|
{
|
||||||
|
setCountdown(seconds);
|
||||||
|
mAction = action;
|
||||||
|
if (! mMainloop.connected())
|
||||||
|
{
|
||||||
|
LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop"));
|
||||||
|
mMainloop = mainloop.listen(getName(), boost::bind(&LLEventTimeoutBase::tick, this, _1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ErrorAfter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ErrorAfter(const std::string& message): mMessage(message) {}
|
||||||
|
|
||||||
|
void operator()()
|
||||||
|
{
|
||||||
|
LL_ERRS("LLEventTimeout") << mMessage << LL_ENDL;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string mMessage;
|
||||||
|
};
|
||||||
|
|
||||||
|
void LLEventTimeoutBase::errorAfter(F32 seconds, const std::string& message)
|
||||||
|
{
|
||||||
|
actionAfter(seconds, ErrorAfter(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
class EventAfter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EventAfter(LLEventPump& pump, const LLSD& event):
|
||||||
|
mPump(pump),
|
||||||
|
mEvent(event)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void operator()()
|
||||||
|
{
|
||||||
|
mPump.post(mEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
LLEventPump& mPump;
|
||||||
|
LLSD mEvent;
|
||||||
|
};
|
||||||
|
|
||||||
|
void LLEventTimeoutBase::eventAfter(F32 seconds, const LLSD& event)
|
||||||
|
{
|
||||||
|
actionAfter(seconds, EventAfter(*this, event));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LLEventTimeoutBase::post(const LLSD& event)
|
||||||
|
{
|
||||||
|
cancel();
|
||||||
|
return LLEventStream::post(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLEventTimeoutBase::cancel()
|
||||||
|
{
|
||||||
|
mMainloop.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LLEventTimeoutBase::tick(const LLSD&)
|
||||||
|
{
|
||||||
|
if (countdownElapsed())
|
||||||
|
{
|
||||||
|
cancel();
|
||||||
|
mAction();
|
||||||
|
}
|
||||||
|
return false; // show event to other listeners
|
||||||
|
}
|
||||||
|
|
||||||
|
LLEventTimeout::LLEventTimeout() {}
|
||||||
|
|
||||||
|
LLEventTimeout::LLEventTimeout(LLEventPump& source):
|
||||||
|
LLEventTimeoutBase(source)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLEventTimeout::setCountdown(F32 seconds)
|
||||||
|
{
|
||||||
|
mTimer.setTimerExpirySec(seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LLEventTimeout::countdownElapsed() const
|
||||||
|
{
|
||||||
|
return mTimer.hasExpired();
|
||||||
|
}
|
||||||
203
indra/llcommon/lleventfilter.h
Normal file
203
indra/llcommon/lleventfilter.h
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
/**
|
||||||
|
* @file lleventfilter.h
|
||||||
|
* @author Nat Goodspeed
|
||||||
|
* @date 2009-03-05
|
||||||
|
* @brief Define LLEventFilter: LLEventStream subclass with conditions
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation;
|
||||||
|
* version 2.1 of the License only.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if ! defined(LL_LLEVENTFILTER_H)
|
||||||
|
#define LL_LLEVENTFILTER_H
|
||||||
|
|
||||||
|
#include "llevents.h"
|
||||||
|
#include "stdtypes.h"
|
||||||
|
#include "lltimer.h"
|
||||||
|
#include <boost/function.hpp>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic base class
|
||||||
|
*/
|
||||||
|
class LL_COMMON_API LLEventFilter: public LLEventStream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// construct a standalone LLEventFilter
|
||||||
|
LLEventFilter(const std::string& name="filter", bool tweak=true):
|
||||||
|
LLEventStream(name, tweak)
|
||||||
|
{}
|
||||||
|
/// construct LLEventFilter and connect it to the specified LLEventPump
|
||||||
|
LLEventFilter(LLEventPump& source, const std::string& name="filter", bool tweak=true);
|
||||||
|
|
||||||
|
/// Post an event to all listeners
|
||||||
|
virtual bool post(const LLSD& event) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pass through only events matching a specified pattern
|
||||||
|
*/
|
||||||
|
class LLEventMatching: public LLEventFilter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Pass an LLSD map with keys and values the incoming event must match
|
||||||
|
LLEventMatching(const LLSD& pattern);
|
||||||
|
/// instantiate and connect
|
||||||
|
LLEventMatching(LLEventPump& source, const LLSD& pattern);
|
||||||
|
|
||||||
|
/// Only pass through events matching the pattern
|
||||||
|
virtual bool post(const LLSD& event);
|
||||||
|
|
||||||
|
private:
|
||||||
|
LLSD mPattern;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for an event to be posted. If no such event arrives within a specified
|
||||||
|
* time, take a specified action. See LLEventTimeout for production
|
||||||
|
* implementation.
|
||||||
|
*
|
||||||
|
* @NOTE This is an abstract base class so that, for testing, we can use an
|
||||||
|
* alternate "timer" that doesn't actually consume real time.
|
||||||
|
*/
|
||||||
|
class LL_COMMON_API LLEventTimeoutBase: public LLEventFilter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// construct standalone
|
||||||
|
LLEventTimeoutBase();
|
||||||
|
/// construct and connect
|
||||||
|
LLEventTimeoutBase(LLEventPump& source);
|
||||||
|
|
||||||
|
/// Callable, can be constructed with boost::bind()
|
||||||
|
typedef boost::function<void()> Action;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start countdown timer for the specified number of @a seconds. Forward
|
||||||
|
* all events. If any event arrives before timer expires, cancel timer. If
|
||||||
|
* no event arrives before timer expires, take specified @a action.
|
||||||
|
*
|
||||||
|
* This is a one-shot timer. Once it has either expired or been canceled,
|
||||||
|
* it is inert until another call to actionAfter().
|
||||||
|
*
|
||||||
|
* Calling actionAfter() while an existing timer is running cheaply
|
||||||
|
* replaces that original timer. Thus, a valid use case is to detect
|
||||||
|
* idleness of some event source by calling actionAfter() on each new
|
||||||
|
* event. A rapid sequence of events will keep the timer from expiring;
|
||||||
|
* the first gap in events longer than the specified timer will fire the
|
||||||
|
* specified Action.
|
||||||
|
*
|
||||||
|
* Any post() call cancels the timer. To be satisfied with only a
|
||||||
|
* particular event, chain on an LLEventMatching that only passes such
|
||||||
|
* events:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* event ultimate
|
||||||
|
* source ---> LLEventMatching ---> LLEventTimeout ---> listener
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @NOTE
|
||||||
|
* The implementation relies on frequent events on the LLEventPump named
|
||||||
|
* "mainloop".
|
||||||
|
*/
|
||||||
|
void actionAfter(F32 seconds, const Action& action);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like actionAfter(), but where the desired Action is LL_ERRS
|
||||||
|
* termination. Pass the timeout time and the desired LL_ERRS @a message.
|
||||||
|
*
|
||||||
|
* This method is useful when, for instance, some async API guarantees an
|
||||||
|
* event, whether success or failure, within a stated time window.
|
||||||
|
* Instantiate an LLEventTimeout listening to that API and call
|
||||||
|
* errorAfter() on each async request with a timeout comfortably longer
|
||||||
|
* than the API's time guarantee (much longer than the anticipated
|
||||||
|
* "mainloop" granularity).
|
||||||
|
*
|
||||||
|
* Then if the async API breaks its promise, the program terminates with
|
||||||
|
* the specified LL_ERRS @a message. The client of the async API can
|
||||||
|
* therefore assume the guarantee is upheld.
|
||||||
|
*
|
||||||
|
* @NOTE
|
||||||
|
* errorAfter() is implemented in terms of actionAfter(), so all remarks
|
||||||
|
* about calling actionAfter() also apply to errorAfter().
|
||||||
|
*/
|
||||||
|
void errorAfter(F32 seconds, const std::string& message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like actionAfter(), but where the desired Action is a particular event
|
||||||
|
* for all listeners. Pass the timeout time and the desired @a event data.
|
||||||
|
*
|
||||||
|
* Suppose the timeout should only be satisfied by a particular event, but
|
||||||
|
* the ultimate listener must see all other incoming events as well, plus
|
||||||
|
* the timeout @a event if any:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* some LLEventMatching LLEventMatching
|
||||||
|
* event ---> for particular ---> LLEventTimeout ---> for timeout
|
||||||
|
* source event event \
|
||||||
|
* \ \ ultimate
|
||||||
|
* `-----------------------------------------------------> listener
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Since a given listener can listen on more than one LLEventPump, we can
|
||||||
|
* set things up so it sees the set union of events from LLEventTimeout
|
||||||
|
* and the original event source. However, as LLEventTimeout passes
|
||||||
|
* through all incoming events, the "particular event" that satisfies the
|
||||||
|
* left LLEventMatching would reach the ultimate listener twice. So we add
|
||||||
|
* an LLEventMatching that only passes timeout events.
|
||||||
|
*
|
||||||
|
* @NOTE
|
||||||
|
* eventAfter() is implemented in terms of actionAfter(), so all remarks
|
||||||
|
* about calling actionAfter() also apply to eventAfter().
|
||||||
|
*/
|
||||||
|
void eventAfter(F32 seconds, const LLSD& event);
|
||||||
|
|
||||||
|
/// Pass event through, canceling the countdown timer
|
||||||
|
virtual bool post(const LLSD& event);
|
||||||
|
|
||||||
|
/// Cancel timer without event
|
||||||
|
void cancel();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void setCountdown(F32 seconds) = 0;
|
||||||
|
virtual bool countdownElapsed() const = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool tick(const LLSD&);
|
||||||
|
|
||||||
|
LLBoundListener mMainloop;
|
||||||
|
Action mAction;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Production implementation of LLEventTimoutBase
|
||||||
|
class LL_COMMON_API LLEventTimeout: public LLEventTimeoutBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LLEventTimeout();
|
||||||
|
LLEventTimeout(LLEventPump& source);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void setCountdown(F32 seconds);
|
||||||
|
virtual bool countdownElapsed() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
LLTimer mTimer;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* ! defined(LL_LLEVENTFILTER_H) */
|
||||||
628
indra/llcommon/llevents.cpp
Normal file
628
indra/llcommon/llevents.cpp
Normal file
@@ -0,0 +1,628 @@
|
|||||||
|
/**
|
||||||
|
* @file llevents.cpp
|
||||||
|
* @author Nat Goodspeed
|
||||||
|
* @date 2008-09-12
|
||||||
|
* @brief Implementation for llevents.
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation;
|
||||||
|
* version 2.1 of the License only.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Precompiled header
|
||||||
|
#include "linden_common.h"
|
||||||
|
|
||||||
|
#if LL_WINDOWS
|
||||||
|
#pragma warning (disable : 4675) // "resolved by ADL" -- just as I want!
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// associated header
|
||||||
|
#include "llevents.h"
|
||||||
|
// STL headers
|
||||||
|
#include <set>
|
||||||
|
#include <sstream>
|
||||||
|
#include <algorithm>
|
||||||
|
// std headers
|
||||||
|
#include <typeinfo>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cctype>
|
||||||
|
// external library headers
|
||||||
|
#include <boost/range/iterator_range.hpp>
|
||||||
|
#if LL_WINDOWS
|
||||||
|
#pragma warning (push)
|
||||||
|
#pragma warning (disable : 4701) // compiler thinks might use uninitialized var, but no
|
||||||
|
#endif
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
#if LL_WINDOWS
|
||||||
|
#pragma warning (pop)
|
||||||
|
#endif
|
||||||
|
// other Linden headers
|
||||||
|
#include "stringize.h"
|
||||||
|
#include "llerror.h"
|
||||||
|
#include "llsdutil.h"
|
||||||
|
#if LL_MSVC
|
||||||
|
#pragma warning (disable : 4702)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* queue_names: specify LLEventPump names that should be instantiated as
|
||||||
|
* LLEventQueue
|
||||||
|
*****************************************************************************/
|
||||||
|
/**
|
||||||
|
* At present, we recognize particular requested LLEventPump names as needing
|
||||||
|
* LLEventQueues. Later on we'll migrate this information to an external
|
||||||
|
* configuration file.
|
||||||
|
*/
|
||||||
|
const char* queue_names[] =
|
||||||
|
{
|
||||||
|
"placeholder - replace with first real name string"
|
||||||
|
};
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* If there's a "mainloop" pump, listen on that to flush all LLEventQueues
|
||||||
|
*****************************************************************************/
|
||||||
|
struct RegisterFlush : public LLEventTrackable
|
||||||
|
{
|
||||||
|
RegisterFlush():
|
||||||
|
pumps(LLEventPumps::instance())
|
||||||
|
{
|
||||||
|
pumps.obtain("mainloop").listen("flushLLEventQueues", boost::bind(&RegisterFlush::flush, this, _1));
|
||||||
|
}
|
||||||
|
bool flush(const LLSD&)
|
||||||
|
{
|
||||||
|
pumps.flush();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
~RegisterFlush()
|
||||||
|
{
|
||||||
|
// LLEventTrackable handles stopListening for us.
|
||||||
|
}
|
||||||
|
LLEventPumps& pumps;
|
||||||
|
};
|
||||||
|
static RegisterFlush registerFlush;
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* LLEventPumps
|
||||||
|
*****************************************************************************/
|
||||||
|
LLEventPumps::LLEventPumps():
|
||||||
|
// Until we migrate this information to an external config file,
|
||||||
|
// initialize mQueueNames from the static queue_names array.
|
||||||
|
mQueueNames(boost::begin(queue_names), boost::end(queue_names))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LLEventPump& LLEventPumps::obtain(const std::string& name)
|
||||||
|
{
|
||||||
|
PumpMap::iterator found = mPumpMap.find(name);
|
||||||
|
if (found != mPumpMap.end())
|
||||||
|
{
|
||||||
|
// Here we already have an LLEventPump instance with the requested
|
||||||
|
// name.
|
||||||
|
return *found->second;
|
||||||
|
}
|
||||||
|
// Here we must instantiate an LLEventPump subclass.
|
||||||
|
LLEventPump* newInstance;
|
||||||
|
// Should this name be an LLEventQueue?
|
||||||
|
PumpNames::const_iterator nfound = mQueueNames.find(name);
|
||||||
|
if (nfound != mQueueNames.end())
|
||||||
|
newInstance = new LLEventQueue(name);
|
||||||
|
else
|
||||||
|
newInstance = new LLEventStream(name);
|
||||||
|
// LLEventPump's constructor implicitly registers each new instance in
|
||||||
|
// mPumpMap. But remember that we instantiated it (in mOurPumps) so we'll
|
||||||
|
// delete it later.
|
||||||
|
mOurPumps.insert(newInstance);
|
||||||
|
return *newInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLEventPumps::flush()
|
||||||
|
{
|
||||||
|
// Flush every known LLEventPump instance. Leave it up to each instance to
|
||||||
|
// decide what to do with the flush() call.
|
||||||
|
for (PumpMap::iterator pmi = mPumpMap.begin(), pmend = mPumpMap.end(); pmi != pmend; ++pmi)
|
||||||
|
{
|
||||||
|
pmi->second->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLEventPumps::reset()
|
||||||
|
{
|
||||||
|
// Reset every known LLEventPump instance. Leave it up to each instance to
|
||||||
|
// decide what to do with the reset() call.
|
||||||
|
for (PumpMap::iterator pmi = mPumpMap.begin(), pmend = mPumpMap.end(); pmi != pmend; ++pmi)
|
||||||
|
{
|
||||||
|
pmi->second->reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LLEventPumps::registerNew(const LLEventPump& pump, const std::string& name, bool tweak)
|
||||||
|
{
|
||||||
|
std::pair<PumpMap::iterator, bool> inserted =
|
||||||
|
mPumpMap.insert(PumpMap::value_type(name, const_cast<LLEventPump*>(&pump)));
|
||||||
|
// If the insert worked, then the name is unique; return that.
|
||||||
|
if (inserted.second)
|
||||||
|
return name;
|
||||||
|
// Here the new entry was NOT inserted, and therefore name isn't unique.
|
||||||
|
// Unless we're permitted to tweak it, that's Bad.
|
||||||
|
if (! tweak)
|
||||||
|
{
|
||||||
|
throw LLEventPump::DupPumpName(std::string("Duplicate LLEventPump name '") + name + "'");
|
||||||
|
}
|
||||||
|
// The passed name isn't unique, but we're permitted to tweak it. Find the
|
||||||
|
// first decimal-integer suffix not already taken. The insert() attempt
|
||||||
|
// above will have set inserted.first to the iterator of the existing
|
||||||
|
// entry by that name. Starting there, walk forward until we reach an
|
||||||
|
// entry that doesn't start with 'name'. For each entry consisting of name
|
||||||
|
// + integer suffix, capture the integer suffix in a set. Use a set
|
||||||
|
// because we're going to encounter string suffixes in the order: name1,
|
||||||
|
// name10, name11, name2, ... Walking those possibilities in that order
|
||||||
|
// isn't convenient to detect the first available "hole."
|
||||||
|
std::set<int> suffixes;
|
||||||
|
PumpMap::iterator pmi(inserted.first), pmend(mPumpMap.end());
|
||||||
|
// We already know inserted.first references the existing entry with
|
||||||
|
// 'name' as the key; skip that one and start with the next.
|
||||||
|
while (++pmi != pmend)
|
||||||
|
{
|
||||||
|
if (pmi->first.substr(0, name.length()) != name)
|
||||||
|
{
|
||||||
|
// Found the first entry beyond the entries starting with 'name':
|
||||||
|
// stop looping.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Here we're looking at an entry that starts with 'name'. Is the rest
|
||||||
|
// of it an integer?
|
||||||
|
// Dubious (?) assumption: in the local character set, decimal digits
|
||||||
|
// are in increasing order such that '9' is the last of them. This
|
||||||
|
// test deals with 'name' values such as 'a', where there might be a
|
||||||
|
// very large number of entries starting with 'a' whose suffixes
|
||||||
|
// aren't integers. A secondary assumption is that digit characters
|
||||||
|
// precede most common name characters (true in ASCII, false in
|
||||||
|
// EBCDIC). The test below is correct either way, but it's worth more
|
||||||
|
// if the assumption holds.
|
||||||
|
if (pmi->first[name.length()] > '9')
|
||||||
|
break;
|
||||||
|
// It should be cheaper to detect that we're not looking at a digit
|
||||||
|
// character -- and therefore the suffix can't possibly be an integer
|
||||||
|
// -- than to attempt the lexical_cast and catch the exception.
|
||||||
|
if (! std::isdigit(pmi->first[name.length()]))
|
||||||
|
continue;
|
||||||
|
// Okay, the first character of the suffix is a digit, it's worth at
|
||||||
|
// least attempting to convert to int.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
suffixes.insert(boost::lexical_cast<int>(pmi->first.substr(name.length())));
|
||||||
|
}
|
||||||
|
catch (const boost::bad_lexical_cast&)
|
||||||
|
{
|
||||||
|
// If the rest of pmi->first isn't an int, just ignore it.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Here we've accumulated in 'suffixes' all existing int suffixes of the
|
||||||
|
// entries starting with 'name'. Find the first unused one.
|
||||||
|
int suffix = 1;
|
||||||
|
for ( ; suffixes.find(suffix) != suffixes.end(); ++suffix)
|
||||||
|
;
|
||||||
|
// Here 'suffix' is not in 'suffixes'. Construct a new name based on that
|
||||||
|
// suffix, insert it and return it.
|
||||||
|
std::ostringstream out;
|
||||||
|
out << name << suffix;
|
||||||
|
return registerNew(pump, out.str(), tweak);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLEventPumps::unregister(const LLEventPump& pump)
|
||||||
|
{
|
||||||
|
// Remove this instance from mPumpMap
|
||||||
|
PumpMap::iterator found = mPumpMap.find(pump.getName());
|
||||||
|
if (found != mPumpMap.end())
|
||||||
|
{
|
||||||
|
mPumpMap.erase(found);
|
||||||
|
}
|
||||||
|
// If this instance is one we created, also remove it from mOurPumps so we
|
||||||
|
// won't try again to delete it later!
|
||||||
|
PumpSet::iterator psfound = mOurPumps.find(const_cast<LLEventPump*>(&pump));
|
||||||
|
if (psfound != mOurPumps.end())
|
||||||
|
{
|
||||||
|
mOurPumps.erase(psfound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//static
|
||||||
|
bool LLEventPumps::sDeleted;
|
||||||
|
|
||||||
|
//static
|
||||||
|
void LLEventPumps::maybe_unregister(const LLEventPump& pump)
|
||||||
|
{
|
||||||
|
if (!sDeleted)
|
||||||
|
{
|
||||||
|
LLEventPumps::instance().unregister(pump);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LLEventPumps::~LLEventPumps()
|
||||||
|
{
|
||||||
|
// Deleting an LLEventPump calls its destructor, which calls maybe_unregister(),
|
||||||
|
// which would try to remove that LLEventPump instance from a NEWLY created LLEventPumps
|
||||||
|
// singleton (as we're already being destructed). Therefore, mark that we're not
|
||||||
|
// home anymore... --Aleric
|
||||||
|
sDeleted = true;
|
||||||
|
|
||||||
|
// Subsequently we can delete every LLEventPump we instantiated (via obtain()).
|
||||||
|
// We're not clearing mPumpMap or mOurPumps here... their destructors will.
|
||||||
|
for (LLEventPumps::PumpSet::iterator pump = mOurPumps.begin(); pump != mOurPumps.end(); ++pump)
|
||||||
|
{
|
||||||
|
delete *pump;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* LLEventPump
|
||||||
|
*****************************************************************************/
|
||||||
|
#if LL_WINDOWS
|
||||||
|
#pragma warning (push)
|
||||||
|
#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
|
||||||
|
#endif
|
||||||
|
|
||||||
|
LLEventPump::LLEventPump(const std::string& name, bool tweak):
|
||||||
|
// Register every new instance with LLEventPumps
|
||||||
|
mName(LLEventPumps::instance().registerNew(*this, name, tweak)),
|
||||||
|
mSignal(new LLStandardSignal()),
|
||||||
|
mEnabled(true)
|
||||||
|
{}
|
||||||
|
|
||||||
|
#if LL_WINDOWS
|
||||||
|
#pragma warning (pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
LLEventPump::~LLEventPump()
|
||||||
|
{
|
||||||
|
// Unregister this doomed instance from LLEventPumps
|
||||||
|
LLEventPumps::maybe_unregister(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static data member
|
||||||
|
const LLEventPump::NameList LLEventPump::empty;
|
||||||
|
|
||||||
|
std::string LLEventPump::inventName(const std::string& pfx)
|
||||||
|
{
|
||||||
|
static long suffix = 0;
|
||||||
|
return STRINGIZE(pfx << suffix++);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLEventPump::reset()
|
||||||
|
{
|
||||||
|
mSignal.reset();
|
||||||
|
mConnections.clear();
|
||||||
|
//mDeps.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventListener& listener,
|
||||||
|
const NameList& after,
|
||||||
|
const NameList& before)
|
||||||
|
{
|
||||||
|
// Check for duplicate name before connecting listener to mSignal
|
||||||
|
ConnectionMap::const_iterator found = mConnections.find(name);
|
||||||
|
// In some cases the user might disconnect a connection explicitly -- or
|
||||||
|
// might use LLEventTrackable to disconnect implicitly. Either way, we can
|
||||||
|
// end up retaining in mConnections a zombie connection object that's
|
||||||
|
// already been disconnected. Such a connection object can't be
|
||||||
|
// reconnected -- nor, in the case of LLEventTrackable, would we want to
|
||||||
|
// try, since disconnection happens with the destruction of the listener
|
||||||
|
// object. That means it's safe to overwrite a disconnected connection
|
||||||
|
// object with the new one we're attempting. The case we want to prevent
|
||||||
|
// is only when the existing connection object is still connected.
|
||||||
|
if (found != mConnections.end() && found->second.connected())
|
||||||
|
{
|
||||||
|
throw DupListenerName(std::string("Attempt to register duplicate listener name '") + name +
|
||||||
|
"' on " + typeid(*this).name() + " '" + getName() + "'");
|
||||||
|
}
|
||||||
|
// Okay, name is unique, try to reconcile its dependencies. Specify a new
|
||||||
|
// "node" value that we never use for an mSignal placement; we'll fix it
|
||||||
|
// later.
|
||||||
|
DependencyMap::node_type& newNode = mDeps.add(name, -1.0, after, before);
|
||||||
|
// What if this listener has been added, removed and re-added? In that
|
||||||
|
// case newNode already has a non-negative value because we never remove a
|
||||||
|
// listener from mDeps. But keep processing uniformly anyway in case the
|
||||||
|
// listener was added back with different dependencies. Then mDeps.sort()
|
||||||
|
// would put it in a different position, and the old newNode placement
|
||||||
|
// value would be wrong, so we'd have to reassign it anyway. Trust that
|
||||||
|
// re-adding a listener with the same dependencies is the trivial case for
|
||||||
|
// mDeps.sort(): it can just replay its cache.
|
||||||
|
DependencyMap::sorted_range sorted_range;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Can we pick an order that works including this new entry?
|
||||||
|
sorted_range = mDeps.sort();
|
||||||
|
}
|
||||||
|
catch (const DependencyMap::Cycle& e)
|
||||||
|
{
|
||||||
|
// No: the new node's after/before dependencies have made mDeps
|
||||||
|
// unsortable. If we leave the new node in mDeps, it will continue
|
||||||
|
// to screw up all future attempts to sort()! Pull it out.
|
||||||
|
mDeps.remove(name);
|
||||||
|
throw Cycle(std::string("New listener '") + name + "' on " + typeid(*this).name() +
|
||||||
|
" '" + getName() + "' would cause cycle: " + e.what());
|
||||||
|
}
|
||||||
|
// Walk the list to verify that we haven't changed the order.
|
||||||
|
float previous = 0.0, myprev = 0.0;
|
||||||
|
DependencyMap::sorted_iterator mydmi = sorted_range.end(); // need this visible after loop
|
||||||
|
for (DependencyMap::sorted_iterator dmi = sorted_range.begin();
|
||||||
|
dmi != sorted_range.end(); ++dmi)
|
||||||
|
{
|
||||||
|
// Since we've added the new entry with an invalid placement,
|
||||||
|
// recognize it and skip it.
|
||||||
|
if (dmi->first == name)
|
||||||
|
{
|
||||||
|
// Remember the iterator belonging to our new node, and which
|
||||||
|
// placement value was 'previous' at that point.
|
||||||
|
mydmi = dmi;
|
||||||
|
myprev = previous;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// If the new node has rearranged the existing nodes, we'll find
|
||||||
|
// that their placement values are no longer in increasing order.
|
||||||
|
if (dmi->second < previous)
|
||||||
|
{
|
||||||
|
// This is another scenario in which we'd better back out the
|
||||||
|
// newly-added node from mDeps -- but don't do it yet, we want to
|
||||||
|
// traverse the existing mDeps to report on it!
|
||||||
|
// Describe the change to the order of our listeners. Copy
|
||||||
|
// everything but the newest listener to a vector we can sort to
|
||||||
|
// obtain the old order.
|
||||||
|
typedef std::vector< std::pair<float, std::string> > SortNameList;
|
||||||
|
SortNameList sortnames;
|
||||||
|
for (DependencyMap::sorted_iterator cdmi(sorted_range.begin()), cdmend(sorted_range.end());
|
||||||
|
cdmi != cdmend; ++cdmi)
|
||||||
|
{
|
||||||
|
if (cdmi->first != name)
|
||||||
|
{
|
||||||
|
sortnames.push_back(SortNameList::value_type(cdmi->second, cdmi->first));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::sort(sortnames.begin(), sortnames.end());
|
||||||
|
std::ostringstream out;
|
||||||
|
out << "New listener '" << name << "' on " << typeid(*this).name() << " '" << getName()
|
||||||
|
<< "' would move previous listener '" << dmi->first << "'\nwas: ";
|
||||||
|
SortNameList::const_iterator sni(sortnames.begin()), snend(sortnames.end());
|
||||||
|
if (sni != snend)
|
||||||
|
{
|
||||||
|
out << sni->second;
|
||||||
|
while (++sni != snend)
|
||||||
|
{
|
||||||
|
out << ", " << sni->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out << "\nnow: ";
|
||||||
|
DependencyMap::sorted_iterator ddmi(sorted_range.begin()), ddmend(sorted_range.end());
|
||||||
|
if (ddmi != ddmend)
|
||||||
|
{
|
||||||
|
out << ddmi->first;
|
||||||
|
while (++ddmi != ddmend)
|
||||||
|
{
|
||||||
|
out << ", " << ddmi->first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// NOW remove the offending listener node.
|
||||||
|
mDeps.remove(name);
|
||||||
|
// Having constructed a description of the order change, inform caller.
|
||||||
|
throw OrderChange(out.str());
|
||||||
|
}
|
||||||
|
// This node becomes the previous one.
|
||||||
|
previous = dmi->second;
|
||||||
|
}
|
||||||
|
// We just got done with a successful mDeps.add(name, ...) call. We'd
|
||||||
|
// better have found 'name' somewhere in that sorted list!
|
||||||
|
assert(mydmi != sorted_range.end());
|
||||||
|
// Four cases:
|
||||||
|
// 0. name is the only entry: placement 1.0
|
||||||
|
// 1. name is the first of several entries: placement (next placement)/2
|
||||||
|
// 2. name is between two other entries: placement (myprev + (next placement))/2
|
||||||
|
// 3. name is the last entry: placement ceil(myprev) + 1.0
|
||||||
|
// Since we've cleverly arranged for myprev to be 0.0 if name is the
|
||||||
|
// first entry, this folds down to two cases. Case 1 is subsumed by
|
||||||
|
// case 2, and case 0 is subsumed by case 3. So we need only handle
|
||||||
|
// cases 2 and 3, which means we need only detect whether name is the
|
||||||
|
// last entry. Increment mydmi to see if there's anything beyond.
|
||||||
|
if (++mydmi != sorted_range.end())
|
||||||
|
{
|
||||||
|
// The new node isn't last. Place it between the previous node and
|
||||||
|
// the successor.
|
||||||
|
newNode = (myprev + mydmi->second)/2.0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// The new node is last. Bump myprev up to the next integer, add
|
||||||
|
// 1.0 and use that.
|
||||||
|
newNode = std::ceil(myprev) + 1.0;
|
||||||
|
}
|
||||||
|
// Now that newNode has a value that places it appropriately in mSignal,
|
||||||
|
// connect it.
|
||||||
|
LLBoundListener bound = mSignal->connect(newNode, listener);
|
||||||
|
mConnections[name] = bound;
|
||||||
|
return bound;
|
||||||
|
}
|
||||||
|
|
||||||
|
LLBoundListener LLEventPump::getListener(const std::string& name) const
|
||||||
|
{
|
||||||
|
ConnectionMap::const_iterator found = mConnections.find(name);
|
||||||
|
if (found != mConnections.end())
|
||||||
|
{
|
||||||
|
return found->second;
|
||||||
|
}
|
||||||
|
// not found, return dummy LLBoundListener
|
||||||
|
return LLBoundListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLEventPump::stopListening(const std::string& name)
|
||||||
|
{
|
||||||
|
ConnectionMap::iterator found = mConnections.find(name);
|
||||||
|
if (found != mConnections.end())
|
||||||
|
{
|
||||||
|
found->second.disconnect();
|
||||||
|
mConnections.erase(found);
|
||||||
|
}
|
||||||
|
// We intentionally do NOT remove this name from mDeps. It may happen that
|
||||||
|
// the same listener with the same name and dependencies will jump on and
|
||||||
|
// off this LLEventPump repeatedly. Keeping a cache of dependencies will
|
||||||
|
// avoid a new dependency sort in such cases.
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* LLEventStream
|
||||||
|
*****************************************************************************/
|
||||||
|
bool LLEventStream::post(const LLSD& event)
|
||||||
|
{
|
||||||
|
if (! mEnabled || !mSignal)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// NOTE NOTE NOTE: Any new access to member data beyond this point should
|
||||||
|
// cause us to move our LLStandardSignal object to a pimpl class along
|
||||||
|
// with said member data. Then the local shared_ptr will preserve both.
|
||||||
|
|
||||||
|
// DEV-43463: capture a local copy of mSignal. We've turned up a
|
||||||
|
// cross-coroutine scenario (described in the Jira) in which this post()
|
||||||
|
// call could end up destroying 'this', the LLEventPump subclass instance
|
||||||
|
// containing mSignal, during the call through *mSignal. So -- capture a
|
||||||
|
// *stack* instance of the shared_ptr, ensuring that our heap
|
||||||
|
// LLStandardSignal object will live at least until post() returns, even
|
||||||
|
// if 'this' gets destroyed during the call.
|
||||||
|
boost::shared_ptr<LLStandardSignal> signal(mSignal);
|
||||||
|
// Let caller know if any one listener handled the event. This is mostly
|
||||||
|
// useful when using LLEventStream as a listener for an upstream
|
||||||
|
// LLEventPump.
|
||||||
|
return (*signal)(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* LLEventQueue
|
||||||
|
*****************************************************************************/
|
||||||
|
bool LLEventQueue::post(const LLSD& event)
|
||||||
|
{
|
||||||
|
if (mEnabled)
|
||||||
|
{
|
||||||
|
// Defer sending this event by queueing it until flush()
|
||||||
|
mEventQueue.push_back(event);
|
||||||
|
}
|
||||||
|
// Unconditionally return false. We won't know until flush() whether a
|
||||||
|
// listener claims to have handled the event -- meanwhile, don't block
|
||||||
|
// other listeners.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLEventQueue::flush()
|
||||||
|
{
|
||||||
|
if(!mSignal) return;
|
||||||
|
|
||||||
|
// Consider the case when a given listener on this LLEventQueue posts yet
|
||||||
|
// another event on the same queue. If we loop over mEventQueue directly,
|
||||||
|
// we'll end up processing all those events during the same flush() call
|
||||||
|
// -- rather like an EventStream. Instead, copy mEventQueue and clear it,
|
||||||
|
// so that any new events posted to this LLEventQueue during flush() will
|
||||||
|
// be processed in the *next* flush() call.
|
||||||
|
EventQueue queue(mEventQueue);
|
||||||
|
mEventQueue.clear();
|
||||||
|
// NOTE NOTE NOTE: Any new access to member data beyond this point should
|
||||||
|
// cause us to move our LLStandardSignal object to a pimpl class along
|
||||||
|
// with said member data. Then the local shared_ptr will preserve both.
|
||||||
|
|
||||||
|
// DEV-43463: capture a local copy of mSignal. See LLEventStream::post()
|
||||||
|
// for detailed comments.
|
||||||
|
boost::shared_ptr<LLStandardSignal> signal(mSignal);
|
||||||
|
for ( ; ! queue.empty(); queue.pop_front())
|
||||||
|
{
|
||||||
|
(*signal)(queue.front());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* LLListenerOrPumpName
|
||||||
|
*****************************************************************************/
|
||||||
|
LLListenerOrPumpName::LLListenerOrPumpName(const std::string& pumpname):
|
||||||
|
// Look up the specified pumpname, and bind its post() method as our listener
|
||||||
|
mListener(boost::bind(&LLEventPump::post,
|
||||||
|
boost::ref(LLEventPumps::instance().obtain(pumpname)),
|
||||||
|
_1))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LLListenerOrPumpName::LLListenerOrPumpName(const char* pumpname):
|
||||||
|
// Look up the specified pumpname, and bind its post() method as our listener
|
||||||
|
mListener(boost::bind(&LLEventPump::post,
|
||||||
|
boost::ref(LLEventPumps::instance().obtain(pumpname)),
|
||||||
|
_1))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LLListenerOrPumpName::operator()(const LLSD& event) const
|
||||||
|
{
|
||||||
|
if (! mListener)
|
||||||
|
{
|
||||||
|
throw Empty("attempting to call uninitialized");
|
||||||
|
}
|
||||||
|
return (*mListener)(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLReqID::stamp(LLSD& response) const
|
||||||
|
{
|
||||||
|
if (! (response.isUndefined() || response.isMap()))
|
||||||
|
{
|
||||||
|
// If 'response' was previously completely empty, it's okay to
|
||||||
|
// turn it into a map. If it was already a map, then it should be
|
||||||
|
// okay to add a key. But if it was anything else (e.g. a scalar),
|
||||||
|
// assigning a ["reqid"] key will DISCARD the previous value,
|
||||||
|
// replacing it with a map. That would be Bad.
|
||||||
|
LL_INFOS("LLReqID") << "stamp(" << mReqid << ") leaving non-map response unmodified: "
|
||||||
|
<< response << LL_ENDL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LLSD oldReqid(response["reqid"]);
|
||||||
|
if (! (oldReqid.isUndefined() || llsd_equals(oldReqid, mReqid)))
|
||||||
|
{
|
||||||
|
LL_INFOS("LLReqID") << "stamp(" << mReqid << ") preserving existing [\"reqid\"] value "
|
||||||
|
<< oldReqid << " in response: " << response << LL_ENDL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
response["reqid"] = mReqid;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sendReply(const LLSD& reply, const LLSD& request, const std::string& replyKey)
|
||||||
|
{
|
||||||
|
// If the original request has no value for replyKey, it's pointless to
|
||||||
|
// construct or send a reply event: on which LLEventPump should we send
|
||||||
|
// it? Allow that to be optional: if the caller wants to require replyKey,
|
||||||
|
// it can so specify when registering the operation method.
|
||||||
|
if (! request.has(replyKey))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here the request definitely contains replyKey; reasonable to proceed.
|
||||||
|
|
||||||
|
// Copy 'reply' to modify it.
|
||||||
|
LLSD newreply(reply);
|
||||||
|
// Get the ["reqid"] element from request
|
||||||
|
LLReqID reqID(request);
|
||||||
|
// and copy it to 'newreply'.
|
||||||
|
reqID.stamp(newreply);
|
||||||
|
// Send reply on LLEventPump named in request[replyKey]. Don't forget to
|
||||||
|
// send the modified 'newreply' instead of the original 'reply'.
|
||||||
|
return LLEventPumps::instance().obtain(request[replyKey]).post(newreply);
|
||||||
|
}
|
||||||
1051
indra/llcommon/llevents.h
Normal file
1051
indra/llcommon/llevents.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,135 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file llfasttimer.cpp
|
|
||||||
* @brief Implementation of the fast timer.
|
|
||||||
*
|
|
||||||
* $LicenseInfo:firstyear=2004&license=viewergpl$
|
|
||||||
*
|
|
||||||
* Copyright (c) 2004-2009, Linden Research, Inc.
|
|
||||||
*
|
|
||||||
* Second Life Viewer Source Code
|
|
||||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
|
||||||
* to you under the terms of the GNU General Public License, version 2.0
|
|
||||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
|
||||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
|
||||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
|
||||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
|
||||||
*
|
|
||||||
* There are special exceptions to the terms and conditions of the GPL as
|
|
||||||
* it is applied to this Source Code. View the full text of the exception
|
|
||||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
|
||||||
* online at
|
|
||||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
||||||
*
|
|
||||||
* By copying, modifying or distributing this software, you acknowledge
|
|
||||||
* that you have read and understood your obligations described above,
|
|
||||||
* and agree to abide by those obligations.
|
|
||||||
*
|
|
||||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
|
||||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
|
||||||
* COMPLETENESS OR PERFORMANCE.
|
|
||||||
* $/LicenseInfo$
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "linden_common.h"
|
|
||||||
|
|
||||||
#include "llfasttimer.h"
|
|
||||||
#include "llmemory.h"
|
|
||||||
#include "llprocessor.h"
|
|
||||||
|
|
||||||
#if LL_WINDOWS
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
|
||||||
#include "lltimer.h"
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
// statics
|
|
||||||
|
|
||||||
LLFastTimer::EFastTimerType LLFastTimer::sCurType = LLFastTimer::FTM_OTHER;
|
|
||||||
int LLFastTimer::sCurDepth = 0;
|
|
||||||
U64 LLFastTimer::sStart[LLFastTimer::FTM_MAX_DEPTH];
|
|
||||||
U64 LLFastTimer::sCounter[LLFastTimer::FTM_NUM_TYPES];
|
|
||||||
U64 LLFastTimer::sCountHistory[LLFastTimer::FTM_HISTORY_NUM][LLFastTimer::FTM_NUM_TYPES];
|
|
||||||
U64 LLFastTimer::sCountAverage[LLFastTimer::FTM_NUM_TYPES];
|
|
||||||
U64 LLFastTimer::sCalls[LLFastTimer::FTM_NUM_TYPES];
|
|
||||||
U64 LLFastTimer::sCallHistory[LLFastTimer::FTM_HISTORY_NUM][LLFastTimer::FTM_NUM_TYPES];
|
|
||||||
U64 LLFastTimer::sCallAverage[LLFastTimer::FTM_NUM_TYPES];
|
|
||||||
S32 LLFastTimer::sCurFrameIndex = -1;
|
|
||||||
S32 LLFastTimer::sLastFrameIndex = -1;
|
|
||||||
int LLFastTimer::sPauseHistory = 0;
|
|
||||||
int LLFastTimer::sResetHistory = 0;
|
|
||||||
|
|
||||||
U64 LLFastTimer::sClockResolution = calc_clock_frequency(50U); // Resolution of get_clock_count()
|
|
||||||
|
|
||||||
U64 LLFastTimer::countsPerSecond() // counts per second for the *32-bit* timer
|
|
||||||
{
|
|
||||||
return sClockResolution >> 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LLFastTimer::reset()
|
|
||||||
{
|
|
||||||
countsPerSecond(); // good place to calculate clock frequency
|
|
||||||
|
|
||||||
if (sCurDepth != 0)
|
|
||||||
{
|
|
||||||
llerrs << "LLFastTimer::Reset() when sCurDepth != 0" << llendl;
|
|
||||||
}
|
|
||||||
if (sPauseHistory)
|
|
||||||
{
|
|
||||||
sResetHistory = 1;
|
|
||||||
}
|
|
||||||
else if (sResetHistory)
|
|
||||||
{
|
|
||||||
sCurFrameIndex = -1;
|
|
||||||
sResetHistory = 0;
|
|
||||||
}
|
|
||||||
else if (sCurFrameIndex >= 0)
|
|
||||||
{
|
|
||||||
int hidx = sCurFrameIndex % FTM_HISTORY_NUM;
|
|
||||||
for (S32 i=0; i<FTM_NUM_TYPES; i++)
|
|
||||||
{
|
|
||||||
sCountHistory[hidx][i] = sCounter[i];
|
|
||||||
sCountAverage[i] = (sCountAverage[i]*sCurFrameIndex + sCounter[i]) / (sCurFrameIndex+1);
|
|
||||||
sCallHistory[hidx][i] = sCalls[i];
|
|
||||||
sCallAverage[i] = (sCallAverage[i]*sCurFrameIndex + sCalls[i]) / (sCurFrameIndex+1);
|
|
||||||
}
|
|
||||||
sLastFrameIndex = sCurFrameIndex;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (S32 i=0; i<FTM_NUM_TYPES; i++)
|
|
||||||
{
|
|
||||||
sCountAverage[i] = 0;
|
|
||||||
sCallAverage[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sCurFrameIndex++;
|
|
||||||
|
|
||||||
for (S32 i=0; i<FTM_NUM_TYPES; i++)
|
|
||||||
{
|
|
||||||
sCounter[i] = 0;
|
|
||||||
sCalls[i] = 0;
|
|
||||||
}
|
|
||||||
sCurDepth = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Important note: These implementations must be FAST!
|
|
||||||
//
|
|
||||||
|
|
||||||
// shift off lower 8 bits for lower resolution but longer term timing
|
|
||||||
// on 1Ghz machine, a 32-bit word will hold ~1000 seconds of timing
|
|
||||||
|
|
||||||
//LL_COMMON_API U64 get_clock_count(); // in lltimer.cpp
|
|
||||||
// On windows these use QueryPerformanceCounter, which is arguably fine and also works on amd architectures.
|
|
||||||
U32 LLFastTimer::getCPUClockCount32()
|
|
||||||
{
|
|
||||||
return get_clock_count() >> 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
U64 LLFastTimer::getCPUClockCount64()
|
|
||||||
{
|
|
||||||
return get_clock_count();
|
|
||||||
}
|
|
||||||
@@ -1,294 +1,35 @@
|
|||||||
/**
|
/**
|
||||||
* @file llfasttimer.h
|
* @file llfasttimer.h
|
||||||
* @brief Declaration of a fast timer.
|
* @brief Inline implementations of fast timers.
|
||||||
*
|
|
||||||
* $LicenseInfo:firstyear=2004&license=viewergpl$
|
|
||||||
*
|
|
||||||
* Copyright (c) 2004-2009, Linden Research, Inc.
|
|
||||||
*
|
*
|
||||||
|
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
* to you under the terms of the GNU General Public License, version 2.0
|
|
||||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
|
||||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
|
||||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
|
||||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
|
||||||
*
|
*
|
||||||
* There are special exceptions to the terms and conditions of the GPL as
|
* This library is free software; you can redistribute it and/or
|
||||||
* it is applied to this Source Code. View the full text of the exception
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
* License as published by the Free Software Foundation;
|
||||||
* online at
|
* version 2.1 of the License only.
|
||||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
||||||
*
|
*
|
||||||
* By copying, modifying or distributing this software, you acknowledge
|
* This library is distributed in the hope that it will be useful,
|
||||||
* that you have read and understood your obligations described above,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* and agree to abide by those obligations.
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
*
|
*
|
||||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
* License along with this library; if not, write to the Free Software
|
||||||
* COMPLETENESS OR PERFORMANCE.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
* $/LicenseInfo$
|
* $/LicenseInfo$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LL_LLFASTTIMER_H
|
#ifndef LL_FASTTIMER_H
|
||||||
#define LL_LLFASTTIMER_H
|
#define LL_FASTTIMER_H
|
||||||
|
|
||||||
#define FAST_TIMER_ON 1
|
// Implementation of getCPUClockCount32() and getCPUClockCount64 are now in llfastertimer_class.cpp.
|
||||||
|
|
||||||
|
|
||||||
class LL_COMMON_API LLFastTimer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum EFastTimerType
|
|
||||||
{
|
|
||||||
// high level
|
|
||||||
FTM_FRAME,
|
|
||||||
FTM_UPDATE,
|
|
||||||
FTM_RENDER,
|
|
||||||
FTM_SWAP,
|
|
||||||
FTM_CLIENT_COPY,
|
|
||||||
FTM_IDLE,
|
|
||||||
FTM_SLEEP,
|
|
||||||
|
|
||||||
// general timers
|
|
||||||
FT_STRING_FORMAT,
|
|
||||||
|
|
||||||
// common messaging components
|
|
||||||
FTM_PUMP,
|
|
||||||
FTM_CURL,
|
|
||||||
FTM_PUMPIO,
|
|
||||||
|
|
||||||
// common simulation components
|
|
||||||
FTM_UPDATE_ANIMATION,
|
|
||||||
FTM_UPDATE_TERRAIN,
|
|
||||||
FTM_UPDATE_PRIMITIVES,
|
|
||||||
FTM_UPDATE_PARTICLES,
|
|
||||||
FTM_SIMULATE_PARTICLES,
|
|
||||||
FTM_UPDATE_SKY,
|
|
||||||
FTM_UPDATE_TEXTURES,
|
|
||||||
FTM_UPDATE_WLPARAM,
|
|
||||||
FTM_UPDATE_WATER,
|
|
||||||
FTM_UPDATE_CLOUDS,
|
|
||||||
FTM_UPDATE_GRASS,
|
|
||||||
FTM_UPDATE_TREE,
|
|
||||||
FTM_UPDATE_AVATAR,
|
|
||||||
#if MESH_ENABLED
|
|
||||||
FTM_UPDATE_RIGGED_VOLUME,
|
|
||||||
FTM_SKIN_RIGGED,
|
|
||||||
FTM_RIGGED_OCTREE,
|
|
||||||
#endif //MESH_ENABLED
|
|
||||||
|
|
||||||
// common render components
|
|
||||||
FTM_SHADOW_GEOMETRY,
|
|
||||||
FTM_SHADOW_RENDER,
|
|
||||||
FTM_SHADOW_TERRAIN,
|
|
||||||
FTM_SHADOW_AVATAR,
|
|
||||||
FTM_SHADOW_SIMPLE,
|
|
||||||
FTM_SHADOW_ALPHA,
|
|
||||||
FTM_SHADOW_TREE,
|
|
||||||
|
|
||||||
FTM_RENDER_GEOMETRY,
|
|
||||||
FTM_RENDER_TERRAIN,
|
|
||||||
FTM_RENDER_SIMPLE,
|
|
||||||
FTM_RENDER_FULLBRIGHT,
|
|
||||||
FTM_RENDER_GLOW,
|
|
||||||
FTM_RENDER_GRASS,
|
|
||||||
FTM_RENDER_INVISIBLE,
|
|
||||||
FTM_RENDER_SHINY,
|
|
||||||
FTM_RENDER_BUMP,
|
|
||||||
FTM_RENDER_TREES,
|
|
||||||
FTM_RENDER_CHARACTERS,
|
|
||||||
FTM_RENDER_OCCLUSION,
|
|
||||||
FTM_RENDER_ALPHA,
|
|
||||||
FTM_RENDER_CLOUDS,
|
|
||||||
FTM_RENDER_HUD,
|
|
||||||
FTM_RENDER_PARTICLES,
|
|
||||||
FTM_RENDER_WATER,
|
|
||||||
FTM_RENDER_WL_SKY,
|
|
||||||
FTM_RENDER_FAKE_VBO_UPDATE,
|
|
||||||
FTM_RENDER_TIMER,
|
|
||||||
FTM_RENDER_UI,
|
|
||||||
FTM_RENDER_BLOOM,
|
|
||||||
FTM_RENDER_BLOOM_FBO,
|
|
||||||
FTM_RENDER_FONTS,
|
|
||||||
|
|
||||||
// deferred rendering
|
|
||||||
FTM_RENDER_DEFERRED,
|
|
||||||
FTM_BIND_DEFERRED,
|
|
||||||
FTM_SUN_SHADOW,
|
|
||||||
FTM_SOFTEN_SHADOW,
|
|
||||||
FTM_EDGE_DETECTION,
|
|
||||||
FTM_GI_TRACE,
|
|
||||||
FTM_GI_GATHER,
|
|
||||||
FTM_ATMOSPHERICS,
|
|
||||||
FTM_LOCAL_LIGHTS,
|
|
||||||
FTM_FULLSCREEN_LIGHTS,
|
|
||||||
FTM_PROJECTORS,
|
|
||||||
FTM_POST,
|
|
||||||
|
|
||||||
FTM_VISIBLE_CLOUD,
|
|
||||||
|
|
||||||
// newview specific
|
|
||||||
FTM_MESSAGES,
|
|
||||||
FTM_MOUSEHANDLER,
|
|
||||||
FTM_KEYHANDLER,
|
|
||||||
FTM_REBUILD,
|
|
||||||
FTM_STATESORT,
|
|
||||||
FTM_STATESORT_DRAWABLE,
|
|
||||||
FTM_STATESORT_POSTSORT,
|
|
||||||
#if MESH_ENABLED
|
|
||||||
FTM_MESH_UPDATE,
|
|
||||||
FTM_MESH_LOCK1,
|
|
||||||
FTM_MESH_LOCK2,
|
|
||||||
FTM_LOAD_MESH_LOD,
|
|
||||||
#endif //MESH_ENABLED
|
|
||||||
FTM_REBUILD_VBO,
|
|
||||||
FTM_REBUILD_VOLUME_VB,
|
|
||||||
FTM_REBUILD_BRIDGE_VB,
|
|
||||||
FTM_REBUILD_HUD_VB,
|
|
||||||
FTM_REBUILD_TERRAIN_VB,
|
|
||||||
FTM_REBUILD_WATER_VB,
|
|
||||||
FTM_REBUILD_TREE_VB,
|
|
||||||
FTM_REBUILD_PARTICLE_VB,
|
|
||||||
FTM_REBUILD_CLOUD_VB,
|
|
||||||
FTM_REBUILD_GRASS_VB,
|
|
||||||
FTM_REBUILD_NONE_VB,
|
|
||||||
FTM_REBUILD_OCCLUSION_VB,
|
|
||||||
FTM_POOLS,
|
|
||||||
FTM_POOLRENDER,
|
|
||||||
FTM_IDLE_CB,
|
|
||||||
FTM_WORLD_UPDATE,
|
|
||||||
FTM_UPDATE_MOVE,
|
|
||||||
FTM_OCTREE_BALANCE,
|
|
||||||
FTM_UPDATE_LIGHTS,
|
|
||||||
FTM_CULL,
|
|
||||||
FTM_CULL_REBOUND,
|
|
||||||
FTM_FRUSTUM_CULL,
|
|
||||||
FTM_GEO_UPDATE,
|
|
||||||
FTM_GEO_RESERVE,
|
|
||||||
FTM_GEO_LIGHT,
|
|
||||||
FTM_GEO_SHADOW,
|
|
||||||
FTM_GEO_SKY,
|
|
||||||
FTM_GEN_VOLUME,
|
|
||||||
FTM_GEN_TRIANGLES,
|
|
||||||
FTM_GEN_FLEX,
|
|
||||||
FTM_AUDIO_UPDATE,
|
|
||||||
FTM_RESET_DRAWORDER,
|
|
||||||
FTM_OBJECTLIST_UPDATE,
|
|
||||||
FTM_AVATAR_UPDATE,
|
|
||||||
FTM_JOINT_UPDATE,
|
|
||||||
FTM_ATTACHMENT_UPDATE,
|
|
||||||
FTM_LOD_UPDATE,
|
|
||||||
FTM_REGION_UPDATE,
|
|
||||||
FTM_CLEANUP,
|
|
||||||
FTM_NETWORK,
|
|
||||||
FTM_IDLE_NETWORK,
|
|
||||||
FTM_CREATE_OBJECT,
|
|
||||||
FTM_LOAD_AVATAR,
|
|
||||||
FTM_PROCESS_MESSAGES,
|
|
||||||
FTM_PROCESS_OBJECTS,
|
|
||||||
FTM_PROCESS_IMAGES,
|
|
||||||
FTM_IMAGE_UPDATE,
|
|
||||||
FTM_IMAGE_CREATE,
|
|
||||||
FTM_IMAGE_DECODE,
|
|
||||||
FTM_IMAGE_READBACK,
|
|
||||||
FTM_IMAGE_MARK_DIRTY,
|
|
||||||
FTM_PIPELINE,
|
|
||||||
FTM_VFILE_WAIT,
|
|
||||||
FTM_FLEXIBLE_UPDATE,
|
|
||||||
FTM_OCCLUSION_READBACK,
|
|
||||||
FTM_HUD_EFFECTS,
|
|
||||||
FTM_HUD_UPDATE,
|
|
||||||
FTM_INVENTORY,
|
|
||||||
FTM_AUTO_SELECT,
|
|
||||||
FTM_ARRANGE,
|
|
||||||
FTM_FILTER,
|
|
||||||
FTM_REFRESH,
|
|
||||||
FTM_SORT,
|
|
||||||
FTM_PICK,
|
|
||||||
FTM_STATEMACHINE,
|
|
||||||
|
|
||||||
// Temp
|
|
||||||
FTM_TEMP1,
|
|
||||||
FTM_TEMP2,
|
|
||||||
FTM_TEMP3,
|
|
||||||
FTM_TEMP4,
|
|
||||||
FTM_TEMP5,
|
|
||||||
FTM_TEMP6,
|
|
||||||
FTM_TEMP7,
|
|
||||||
FTM_TEMP8,
|
|
||||||
|
|
||||||
FTM_OTHER, // Special, used by display code
|
|
||||||
|
|
||||||
FTM_NUM_TYPES
|
|
||||||
};
|
|
||||||
enum { FTM_HISTORY_NUM = 60 };
|
|
||||||
enum { FTM_MAX_DEPTH = 64 };
|
|
||||||
|
|
||||||
public:
|
|
||||||
static EFastTimerType sCurType;
|
|
||||||
|
|
||||||
LLFastTimer(EFastTimerType type)
|
|
||||||
{
|
|
||||||
#if FAST_TIMER_ON
|
|
||||||
mType = type;
|
|
||||||
sCurType = type;
|
|
||||||
// These don't get counted, because they use CPU clockticks
|
|
||||||
//gTimerBins[gCurTimerBin]++;
|
|
||||||
//LLTimer::sNumTimerCalls++;
|
|
||||||
|
|
||||||
U64 cpu_clocks = getCPUClockCount32();
|
|
||||||
|
|
||||||
sStart[sCurDepth] = cpu_clocks;
|
|
||||||
sCurDepth++;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
~LLFastTimer()
|
|
||||||
{
|
|
||||||
#if FAST_TIMER_ON
|
|
||||||
U64 end,delta;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
// These don't get counted, because they use CPU clockticks
|
|
||||||
//gTimerBins[gCurTimerBin]++;
|
|
||||||
//LLTimer::sNumTimerCalls++;
|
|
||||||
end = getCPUClockCount32();
|
|
||||||
|
|
||||||
sCurDepth--;
|
|
||||||
delta = end - sStart[sCurDepth];
|
|
||||||
sCounter[mType] += delta;
|
|
||||||
sCalls[mType]++;
|
|
||||||
// Subtract delta from parents
|
|
||||||
for (i=0; i<sCurDepth; i++)
|
|
||||||
sStart[i] += delta;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void reset();
|
|
||||||
static U64 countsPerSecond();
|
|
||||||
|
|
||||||
public:
|
|
||||||
static int sCurDepth;
|
|
||||||
static U64 sStart[FTM_MAX_DEPTH];
|
|
||||||
static U64 sCounter[FTM_NUM_TYPES];
|
|
||||||
static U64 sCalls[FTM_NUM_TYPES];
|
|
||||||
static U64 sCountAverage[FTM_NUM_TYPES];
|
|
||||||
static U64 sCallAverage[FTM_NUM_TYPES];
|
|
||||||
static U64 sCountHistory[FTM_HISTORY_NUM][FTM_NUM_TYPES];
|
|
||||||
static U64 sCallHistory[FTM_HISTORY_NUM][FTM_NUM_TYPES];
|
|
||||||
|
|
||||||
static int sPauseHistory;
|
|
||||||
static int sResetHistory;
|
|
||||||
|
|
||||||
static U32 getCPUClockCount32();
|
|
||||||
static U64 getCPUClockCount64();
|
|
||||||
|
|
||||||
static U64 sClockResolution;
|
|
||||||
static S32 sCurFrameIndex;
|
|
||||||
static S32 sLastFrameIndex;
|
|
||||||
|
|
||||||
EFastTimerType mType;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
// pull in the actual class definition
|
||||||
|
#include "llfasttimer_class.h"
|
||||||
|
|
||||||
#endif // LL_LLFASTTIMER_H
|
#endif // LL_LLFASTTIMER_H
|
||||||
|
|||||||
951
indra/llcommon/llfasttimer_class.cpp
Normal file
951
indra/llcommon/llfasttimer_class.cpp
Normal file
@@ -0,0 +1,951 @@
|
|||||||
|
/**
|
||||||
|
* @file llfasttimer_class.cpp
|
||||||
|
* @brief Implementation of the fast timer.
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation;
|
||||||
|
* version 2.1 of the License only.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
//
|
||||||
|
// LLFastTimer documentation, written by Aleric (Feb 2012).
|
||||||
|
//
|
||||||
|
// Disclaimer: this is horrible code and I distantiate myself from its design.
|
||||||
|
// It's neither robust nor object oriented. I just document what I find, in
|
||||||
|
// order to be able to fix the bugs (that logically result from such a design).
|
||||||
|
//
|
||||||
|
// Note that the choosen names of the variables are non-intuitive and make
|
||||||
|
// understanding the code harder. However, I didn't change them in order to
|
||||||
|
// make merging less of a nightmare in the future -- Aleric.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// First of all, absolutely nothing in this code is even remotely thread-safe:
|
||||||
|
// FastTimers should only be used from the main thread and never from another
|
||||||
|
// thread.
|
||||||
|
//
|
||||||
|
// NamedTimerFactory is a singleton, accessed through NamedTimerFactory::instance().
|
||||||
|
//
|
||||||
|
// It has four pointer members which are initialized once to point to
|
||||||
|
// four objects with a life-time equal to the application/singleton:
|
||||||
|
//
|
||||||
|
// mTimerRoot --> NamedTimer("root")
|
||||||
|
// mActiveTimerRoot --> NamedTimer("Frame")
|
||||||
|
// mRootFrameState --> FrameState(mActiveTimerRoot)
|
||||||
|
// mAppTimer --> LLFastTimer(mRootFrameState)
|
||||||
|
//
|
||||||
|
// A NamedTimer has a name and a life-time of approximately that of the application.
|
||||||
|
// There is exactly one instance per unique name.
|
||||||
|
// NamedTimer's are ordered in a hierarchy with each one parent and zero or more
|
||||||
|
// children (the "root" has parent NULL).
|
||||||
|
// The parent of mActiveTimerRoot is mTimerRoot, which has one child: mActiveTimerRoot.
|
||||||
|
// NamedTimer::getDepth() returns the number of parents; mTimerRoot has a depth of 0,
|
||||||
|
// mActiveTimerRoot has a depth of 1 and so on. NamedTimer::getRootNamedTimer() just
|
||||||
|
// returns mActiveTimerRoot.
|
||||||
|
//
|
||||||
|
// Each NamedTimer is linked to exactly one FrameState object, namely
|
||||||
|
// LLFastTimer::getFrameStateList()[named_timer.getFrameStateIndex()], where
|
||||||
|
// getFrameStateList() is a static function returning a global std::vector<FrameState>.
|
||||||
|
// This vector is ordered "Depth First" (the FrameState objects (belonging to
|
||||||
|
// NamedTimer objects) with smallest depth first). The vector is re-sorted a few
|
||||||
|
// times in the beginning (and indexes in FrameState updated) since timers are added
|
||||||
|
// whenever they are first used, not in "Depth First" order, but stabilizes after a
|
||||||
|
// while. This implies that FrameState pointers can't really be used: FrameState
|
||||||
|
// objects move around in memory whenever something is inserted or removed from the
|
||||||
|
// std::vector and/or when the vector is re-sorted. However, FrameState pointers ARE
|
||||||
|
// being used and code exists that tries to update those pointers in the above
|
||||||
|
// mentioned cases (this part had bugs, which I now fixed).
|
||||||
|
//
|
||||||
|
// FrameState objects point back to their corresponding NamedTimer through mTimer.
|
||||||
|
// They have also parents: the FrameState object corresponding to the parent of mTimer.
|
||||||
|
//
|
||||||
|
// Thus, so far we have (assuming "namedtimerX" was created first):
|
||||||
|
//
|
||||||
|
// NamedTimer's: FrameState's:
|
||||||
|
//
|
||||||
|
// NULL
|
||||||
|
// ^
|
||||||
|
// |
|
||||||
|
// depth=0: "root" (mTimerRoot) <-------> getFrameStateList()[0]
|
||||||
|
// ^ ^
|
||||||
|
// | (parent) | (parent)
|
||||||
|
// | |
|
||||||
|
// depth=1: "Frame" (mActiveTimerRoot) <-------> mRootFrameState
|
||||||
|
// ^ ^ ^ ^
|
||||||
|
// | | | |
|
||||||
|
// | (parent) | (parent) | (parent) | (parent)
|
||||||
|
// | | | |
|
||||||
|
// depth=2: "namedtimerX" | <-------> getFrameStateList()[2] |
|
||||||
|
// "namedtimerY" <-------> getFrameStateList()[3]
|
||||||
|
//
|
||||||
|
// where the NamedTimer's point to the corresponding FrameState's by means of
|
||||||
|
// NamedTimer::mFrameStateIndex, and the FrameState's point back through FrameState::mTimer.
|
||||||
|
//
|
||||||
|
// Note the missing getFrameStateList()[1], which is ignored and replaced by
|
||||||
|
// a specific call to 'new FrameState' in initSingleton(). The reason for that is
|
||||||
|
// probably because otherwise mRootFrameState has to be updated every time the
|
||||||
|
// frame state list vector is moved in memory. This special case adds some complexity to,
|
||||||
|
// for instance, getFrameState() which now needs to test if the caller is mActiveTimerRoot.
|
||||||
|
//
|
||||||
|
// DeclareTimer objects are NameTimer/FrameState pointer pairs with again a lifetime
|
||||||
|
// of approximately that of the application. The are usually static, even global,
|
||||||
|
// and are passed an name as string; the name is looked up and added if not already
|
||||||
|
// existing, or else the previously created pair is returned. Obviously, "root" and
|
||||||
|
// "Frame" are the only ones that don't have a corresponding DeclareTimer object.
|
||||||
|
//
|
||||||
|
// LLFastTimer objects are short lived objects, created in a scope and destroyed
|
||||||
|
// at the end in order to measure the time that the application spent in that
|
||||||
|
// scope. They are passed DeclareTimer objects to know which timer to append to.
|
||||||
|
// LLFastTimer::mFrameState is a pointer to the corresponding timer.
|
||||||
|
// The static LLFastTimer::sCurTimerData is a CurTimerData struct that has
|
||||||
|
// a duplicate of that pointer as well as a pointer to the corresponding NamedTimer,
|
||||||
|
// of the last LLFastTimer object that was created (and not destroyed again);
|
||||||
|
// in other words: the running timer with the largest depth.
|
||||||
|
// When a new LLFastTimer object is created while one is already running,
|
||||||
|
// then this sCurTimerData is saved in the already running one (as
|
||||||
|
// LLFastTimer::mLastTimerData) and restored upon destruction of that child timer.
|
||||||
|
//
|
||||||
|
// The following FrameState pointers are being used:
|
||||||
|
//
|
||||||
|
// FrameState::mParent
|
||||||
|
// DeclareTimer::mFrameState
|
||||||
|
// CurTimerData::mFrameState
|
||||||
|
// LLFastTimer::mFrameState
|
||||||
|
//
|
||||||
|
// All of those can be invalidated whenever something is added to the std::vector<FrameState>,
|
||||||
|
// and when that vector is sorted.
|
||||||
|
//
|
||||||
|
// Adding new FrameState objects is done in NamedTimer(std::string const& name), called from
|
||||||
|
// createNamedTimer(), called whenever a DeclareTimer is constructed. At the end of the
|
||||||
|
// DeclareTimer constructor update_cached_pointers_if_changed() is called, which calls
|
||||||
|
// updateCachedPointers() if the std::vector moved in memory since last time it was called.
|
||||||
|
//
|
||||||
|
// Sorting is done in NamedTimer::resetFrame(), which theoretically can be called from
|
||||||
|
// anywhere. Also here updateCachedPointers() is called, directly after sorting the vector.
|
||||||
|
//
|
||||||
|
// I fixed updateCachedPointers() to correct all of the above pointers and removed
|
||||||
|
// another FrameState pointer that was unnecessary.
|
||||||
|
|
||||||
|
#include "linden_common.h"
|
||||||
|
|
||||||
|
#include "llfasttimer.h"
|
||||||
|
|
||||||
|
#include "llmemory.h"
|
||||||
|
#include "llprocessor.h"
|
||||||
|
#include "llsingleton.h"
|
||||||
|
#include "lltreeiterators.h"
|
||||||
|
#include "llsdserialize.h"
|
||||||
|
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
#if LL_WINDOWS
|
||||||
|
#include "lltimer.h"
|
||||||
|
#elif LL_LINUX || LL_SOLARIS
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include "lltimer.h"
|
||||||
|
#elif LL_DARWIN
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include "lltimer.h" // get_clock_count()
|
||||||
|
#else
|
||||||
|
#error "architecture not supported"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
// statics
|
||||||
|
|
||||||
|
S32 LLFastTimer::sCurFrameIndex = -1;
|
||||||
|
S32 LLFastTimer::sLastFrameIndex = -1;
|
||||||
|
U64 LLFastTimer::sLastFrameTime = LLFastTimer::getCPUClockCount64();
|
||||||
|
bool LLFastTimer::sPauseHistory = 0;
|
||||||
|
bool LLFastTimer::sResetHistory = 0;
|
||||||
|
LLFastTimer::CurTimerData LLFastTimer::sCurTimerData;
|
||||||
|
BOOL LLFastTimer::sLog = FALSE;
|
||||||
|
std::string LLFastTimer::sLogName = "";
|
||||||
|
BOOL LLFastTimer::sMetricLog = FALSE;
|
||||||
|
LLMutex* LLFastTimer::sLogLock = NULL;
|
||||||
|
std::queue<LLSD> LLFastTimer::sLogQueue;
|
||||||
|
|
||||||
|
std::vector<LLFastTimer::FrameState>* LLFastTimer::sTimerInfos = NULL;
|
||||||
|
U64 LLFastTimer::sTimerCycles = 0;
|
||||||
|
U32 LLFastTimer::sTimerCalls = 0;
|
||||||
|
|
||||||
|
|
||||||
|
// FIXME: move these declarations to the relevant modules
|
||||||
|
|
||||||
|
// helper functions
|
||||||
|
typedef LLTreeDFSPostIter<LLFastTimer::NamedTimer, LLFastTimer::NamedTimer::child_const_iter> timer_tree_bottom_up_iterator_t;
|
||||||
|
|
||||||
|
static timer_tree_bottom_up_iterator_t begin_timer_tree_bottom_up(LLFastTimer::NamedTimer& id)
|
||||||
|
{
|
||||||
|
return timer_tree_bottom_up_iterator_t(&id,
|
||||||
|
boost::bind(boost::mem_fn(&LLFastTimer::NamedTimer::beginChildren), _1),
|
||||||
|
boost::bind(boost::mem_fn(&LLFastTimer::NamedTimer::endChildren), _1));
|
||||||
|
}
|
||||||
|
|
||||||
|
static timer_tree_bottom_up_iterator_t end_timer_tree_bottom_up()
|
||||||
|
{
|
||||||
|
return timer_tree_bottom_up_iterator_t();
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef LLTreeDFSIter<LLFastTimer::NamedTimer, LLFastTimer::NamedTimer::child_const_iter> timer_tree_dfs_iterator_t;
|
||||||
|
|
||||||
|
|
||||||
|
static timer_tree_dfs_iterator_t begin_timer_tree(LLFastTimer::NamedTimer& id)
|
||||||
|
{
|
||||||
|
return timer_tree_dfs_iterator_t(&id,
|
||||||
|
boost::bind(boost::mem_fn(&LLFastTimer::NamedTimer::beginChildren), _1),
|
||||||
|
boost::bind(boost::mem_fn(&LLFastTimer::NamedTimer::endChildren), _1));
|
||||||
|
}
|
||||||
|
|
||||||
|
static timer_tree_dfs_iterator_t end_timer_tree()
|
||||||
|
{
|
||||||
|
return timer_tree_dfs_iterator_t();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// factory class that creates NamedTimers via static DeclareTimer objects
|
||||||
|
class NamedTimerFactory : public LLSingleton<NamedTimerFactory>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NamedTimerFactory()
|
||||||
|
: mActiveTimerRoot(NULL),
|
||||||
|
mTimerRoot(NULL),
|
||||||
|
mAppTimer(NULL),
|
||||||
|
mRootFrameState(NULL)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/*virtual */ void initSingleton()
|
||||||
|
{
|
||||||
|
mTimerRoot = new LLFastTimer::NamedTimer("root");
|
||||||
|
|
||||||
|
mActiveTimerRoot = new LLFastTimer::NamedTimer("Frame");
|
||||||
|
mActiveTimerRoot->setCollapsed(false);
|
||||||
|
|
||||||
|
mRootFrameState = new LLFastTimer::FrameState(mActiveTimerRoot);
|
||||||
|
// getFrameState and setParent recursively call this function,
|
||||||
|
// so we have to work around that by using a specialized implementation
|
||||||
|
// for the special case were mTimerRoot != mActiveTimerRoot -- Aleric
|
||||||
|
mRootFrameState->mParent = &LLFastTimer::getFrameStateList()[0]; // &mTimerRoot->getFrameState()
|
||||||
|
mRootFrameState->mParent->mActiveCount = 1;
|
||||||
|
// And the following four lines are mActiveTimerRoot->setParent(mTimerRoot);
|
||||||
|
llassert(!mActiveTimerRoot->mParent);
|
||||||
|
mActiveTimerRoot->mParent = mTimerRoot; // mParent = parent;
|
||||||
|
//mRootFrameState->mParent = mRootFrameState->mParent; // getFrameState().mParent = &parent->getFrameState();
|
||||||
|
mTimerRoot->getChildren().push_back(mActiveTimerRoot); // parent->getChildren().push_back(this);
|
||||||
|
mTimerRoot->mNeedsSorting = true; // parent->mNeedsSorting = true;
|
||||||
|
|
||||||
|
mAppTimer = new LLFastTimer(mRootFrameState);
|
||||||
|
}
|
||||||
|
|
||||||
|
~NamedTimerFactory()
|
||||||
|
{
|
||||||
|
std::for_each(mTimers.begin(), mTimers.end(), DeletePairedPointer());
|
||||||
|
|
||||||
|
delete mAppTimer;
|
||||||
|
delete mActiveTimerRoot;
|
||||||
|
delete mTimerRoot;
|
||||||
|
delete mRootFrameState;
|
||||||
|
}
|
||||||
|
|
||||||
|
LLFastTimer::NamedTimer& createNamedTimer(const std::string& name)
|
||||||
|
{
|
||||||
|
timer_map_t::iterator found_it = mTimers.find(name);
|
||||||
|
if (found_it != mTimers.end())
|
||||||
|
{
|
||||||
|
return *found_it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
LLFastTimer::NamedTimer* timer = new LLFastTimer::NamedTimer(name);
|
||||||
|
timer->setParent(mTimerRoot);
|
||||||
|
mTimers.insert(std::make_pair(name, timer));
|
||||||
|
|
||||||
|
return *timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
LLFastTimer::NamedTimer* getTimerByName(const std::string& name)
|
||||||
|
{
|
||||||
|
timer_map_t::iterator found_it = mTimers.find(name);
|
||||||
|
if (found_it != mTimers.end())
|
||||||
|
{
|
||||||
|
return found_it->second;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
LLFastTimer::NamedTimer* getActiveRootTimer() { return mActiveTimerRoot; }
|
||||||
|
LLFastTimer::NamedTimer* getRootTimer() { return mTimerRoot; }
|
||||||
|
const LLFastTimer* getAppTimer() { return mAppTimer; }
|
||||||
|
LLFastTimer::FrameState& getRootFrameState() { return *mRootFrameState; }
|
||||||
|
|
||||||
|
typedef std::map<std::string, LLFastTimer::NamedTimer*> timer_map_t;
|
||||||
|
timer_map_t::iterator beginTimers() { return mTimers.begin(); }
|
||||||
|
timer_map_t::iterator endTimers() { return mTimers.end(); }
|
||||||
|
S32 timerCount() { return mTimers.size(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
timer_map_t mTimers;
|
||||||
|
|
||||||
|
LLFastTimer::NamedTimer* mActiveTimerRoot;
|
||||||
|
LLFastTimer::NamedTimer* mTimerRoot;
|
||||||
|
LLFastTimer* mAppTimer;
|
||||||
|
LLFastTimer::FrameState* mRootFrameState; // Points to memory allocated with new, so this pointer is not invalidated.
|
||||||
|
};
|
||||||
|
|
||||||
|
void update_cached_pointers_if_changed()
|
||||||
|
{
|
||||||
|
// detect when elements have moved and update cached pointers
|
||||||
|
static LLFastTimer::FrameState* sFirstTimerAddress = NULL;
|
||||||
|
if (&*(LLFastTimer::getFrameStateList().begin()) != sFirstTimerAddress)
|
||||||
|
{
|
||||||
|
LLFastTimer::updateCachedPointers();
|
||||||
|
sFirstTimerAddress = &*(LLFastTimer::getFrameStateList().begin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LLFastTimer::DeclareTimer::DeclareTimer(const std::string& name, bool open )
|
||||||
|
: mTimer(NamedTimerFactory::instance().createNamedTimer(name))
|
||||||
|
{
|
||||||
|
mTimer.setCollapsed(!open);
|
||||||
|
mFrameState = &mTimer.getFrameState();
|
||||||
|
update_cached_pointers_if_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
LLFastTimer::DeclareTimer::DeclareTimer(const std::string& name)
|
||||||
|
: mTimer(NamedTimerFactory::instance().createNamedTimer(name))
|
||||||
|
{
|
||||||
|
mFrameState = &mTimer.getFrameState();
|
||||||
|
update_cached_pointers_if_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void LLFastTimer::updateCachedPointers()
|
||||||
|
{
|
||||||
|
// Update DeclareTimer::mFrameState pointers.
|
||||||
|
for (DeclareTimer::instance_iter it = DeclareTimer::beginInstances(); it != DeclareTimer::endInstances(); ++it)
|
||||||
|
{
|
||||||
|
// update cached pointer
|
||||||
|
it->mFrameState = &it->mTimer.getFrameState();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update CurTimerData::mFrameState and LLFastTimer::mFrameState of timers on the stack.
|
||||||
|
FrameState& root_frame_state(NamedTimerFactory::instance().getRootFrameState()); // This one is not invalidated.
|
||||||
|
CurTimerData* cur_timer_data = &LLFastTimer::sCurTimerData;
|
||||||
|
// If the the following condition holds then cur_timer_data->mCurTimer == mAppTimer and
|
||||||
|
// we can stop since mAppTimer->mFrameState is allocated with new and does not invalidate.
|
||||||
|
while(cur_timer_data->mFrameState != &root_frame_state)
|
||||||
|
{
|
||||||
|
cur_timer_data->mFrameState = cur_timer_data->mCurTimer->mFrameState = &cur_timer_data->mNamedTimer->getFrameState();
|
||||||
|
cur_timer_data = &cur_timer_data->mCurTimer->mLastTimerData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update FrameState::mParent
|
||||||
|
info_list_t& frame_state_list(getFrameStateList());
|
||||||
|
FrameState* const vector_start = &*frame_state_list.begin();
|
||||||
|
int const vector_size = frame_state_list.size();
|
||||||
|
FrameState const* const old_vector_start = root_frame_state.mParent;
|
||||||
|
if (vector_start != old_vector_start)
|
||||||
|
{
|
||||||
|
// Vector was moved; if it was sorted then FrameState::mParent will get fixed after returning from this function (see LLFastTimer::NamedTimer::resetFrame).
|
||||||
|
root_frame_state.mParent = vector_start;
|
||||||
|
ptrdiff_t offset = vector_start - old_vector_start;
|
||||||
|
llassert(frame_state_list[vector_size - 1].mParent == vector_start); // The one that was added at the end is already OK.
|
||||||
|
for (int i = 2; i < vector_size - 1; ++i)
|
||||||
|
{
|
||||||
|
FrameState*& parent = frame_state_list[i].mParent;
|
||||||
|
if (parent != &root_frame_state)
|
||||||
|
{
|
||||||
|
parent += offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// See lltimer.cpp.
|
||||||
|
#if LL_LINUX || LL_DARWIN || LL_SOLARIS
|
||||||
|
std::string LLFastTimer::sClockType = "gettimeofday";
|
||||||
|
#elif LL_WINDOWS
|
||||||
|
std::string LLFastTimer::sClockType = "QueryPerformanceCounter";
|
||||||
|
#else
|
||||||
|
#error "Platform not supported"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//static
|
||||||
|
U64 LLFastTimer::countsPerSecond() // counts per second for the *32-bit* timer
|
||||||
|
{
|
||||||
|
static bool firstcall = true;
|
||||||
|
static U64 sCPUClockFrequency;
|
||||||
|
if (firstcall)
|
||||||
|
{
|
||||||
|
sCPUClockFrequency = calc_clock_frequency();
|
||||||
|
firstcall = false;
|
||||||
|
}
|
||||||
|
return sCPUClockFrequency >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
LLFastTimer::FrameState::FrameState(LLFastTimer::NamedTimer* timerp)
|
||||||
|
: mActiveCount(0),
|
||||||
|
mCalls(0),
|
||||||
|
mSelfTimeCounter(0),
|
||||||
|
mParent(NULL),
|
||||||
|
mLastCaller(NULL),
|
||||||
|
mMoveUpTree(false),
|
||||||
|
mTimer(timerp)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
LLFastTimer::NamedTimer::NamedTimer(const std::string& name)
|
||||||
|
: mName(name),
|
||||||
|
mCollapsed(true),
|
||||||
|
mParent(NULL),
|
||||||
|
mTotalTimeCounter(0),
|
||||||
|
mCountAverage(0),
|
||||||
|
mCallAverage(0),
|
||||||
|
mNeedsSorting(false)
|
||||||
|
{
|
||||||
|
info_list_t& frame_state_list = getFrameStateList();
|
||||||
|
mFrameStateIndex = frame_state_list.size();
|
||||||
|
getFrameStateList().push_back(FrameState(this));
|
||||||
|
|
||||||
|
mCountHistory = new U32[HISTORY_NUM];
|
||||||
|
memset(mCountHistory, 0, sizeof(U32) * HISTORY_NUM);
|
||||||
|
mCallHistory = new U32[HISTORY_NUM];
|
||||||
|
memset(mCallHistory, 0, sizeof(U32) * HISTORY_NUM);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLFastTimer::NamedTimer::~NamedTimer()
|
||||||
|
{
|
||||||
|
delete[] mCountHistory;
|
||||||
|
delete[] mCallHistory;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LLFastTimer::NamedTimer::getToolTip(S32 history_idx)
|
||||||
|
{
|
||||||
|
F64 ms_multiplier = 1000.0 / (F64)LLFastTimer::countsPerSecond();
|
||||||
|
if (history_idx < 0)
|
||||||
|
{
|
||||||
|
// by default, show average number of call
|
||||||
|
return llformat("%s (%.2f ms, %d calls)", getName().c_str(), (F32)((F32)getCountAverage() * ms_multiplier), (S32)getCallAverage());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return llformat("%s (%.2f ms, %d calls)", getName().c_str(), (F32)((F32)getHistoricalCount(history_idx) * ms_multiplier), (S32)getHistoricalCalls(history_idx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLFastTimer::NamedTimer::setParent(NamedTimer* parent)
|
||||||
|
{
|
||||||
|
llassert_always(parent != this);
|
||||||
|
llassert_always(parent != NULL);
|
||||||
|
|
||||||
|
if (mParent)
|
||||||
|
{
|
||||||
|
// subtract our accumulated from previous parent
|
||||||
|
for (S32 i = 0; i < HISTORY_NUM; i++)
|
||||||
|
{
|
||||||
|
mParent->mCountHistory[i] -= mCountHistory[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// subtract average timing from previous parent
|
||||||
|
mParent->mCountAverage -= mCountAverage;
|
||||||
|
|
||||||
|
std::vector<NamedTimer*>& children = mParent->getChildren();
|
||||||
|
std::vector<NamedTimer*>::iterator found_it = std::find(children.begin(), children.end(), this);
|
||||||
|
if (found_it != children.end())
|
||||||
|
{
|
||||||
|
children.erase(found_it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mParent = parent;
|
||||||
|
if (parent)
|
||||||
|
{
|
||||||
|
getFrameState().mParent = &parent->getFrameState();
|
||||||
|
parent->getChildren().push_back(this);
|
||||||
|
parent->mNeedsSorting = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
S32 LLFastTimer::NamedTimer::getDepth()
|
||||||
|
{
|
||||||
|
S32 depth = 0;
|
||||||
|
NamedTimer* timerp = mParent;
|
||||||
|
while(timerp)
|
||||||
|
{
|
||||||
|
depth++;
|
||||||
|
timerp = timerp->mParent;
|
||||||
|
}
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void LLFastTimer::NamedTimer::processTimes()
|
||||||
|
{
|
||||||
|
if (sCurFrameIndex < 0) return;
|
||||||
|
|
||||||
|
buildHierarchy();
|
||||||
|
accumulateTimings();
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort timer info structs by depth first traversal order
|
||||||
|
struct SortTimersDFS
|
||||||
|
{
|
||||||
|
bool operator()(const LLFastTimer::FrameState& i1, const LLFastTimer::FrameState& i2)
|
||||||
|
{
|
||||||
|
return i1.mTimer->getFrameStateIndex() < i2.mTimer->getFrameStateIndex();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// sort child timers by name
|
||||||
|
struct SortTimerByName
|
||||||
|
{
|
||||||
|
bool operator()(const LLFastTimer::NamedTimer* i1, const LLFastTimer::NamedTimer* i2)
|
||||||
|
{
|
||||||
|
return i1->getName() < i2->getName();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//static
|
||||||
|
void LLFastTimer::NamedTimer::buildHierarchy()
|
||||||
|
{
|
||||||
|
if (sCurFrameIndex < 0 ) return;
|
||||||
|
|
||||||
|
// set up initial tree
|
||||||
|
{
|
||||||
|
for (instance_iter it = beginInstances(); it != endInstances(); ++it)
|
||||||
|
{
|
||||||
|
NamedTimer& timer = *it;
|
||||||
|
if (&timer == NamedTimerFactory::instance().getRootTimer()) continue;
|
||||||
|
|
||||||
|
// bootstrap tree construction by attaching to last timer to be on stack
|
||||||
|
// when this timer was called
|
||||||
|
FrameState& frame_state(timer.getFrameState());
|
||||||
|
if (frame_state.mLastCaller && timer.mParent == NamedTimerFactory::instance().getRootTimer())
|
||||||
|
{
|
||||||
|
timer.setParent(frame_state.mLastCaller);
|
||||||
|
// no need to push up tree on first use, flag can be set spuriously
|
||||||
|
frame_state.mMoveUpTree = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bump timers up tree if they've been flagged as being in the wrong place
|
||||||
|
// do this in a bottom up order to promote descendants first before promoting ancestors
|
||||||
|
// this preserves partial order derived from current frame's observations
|
||||||
|
for(timer_tree_bottom_up_iterator_t it = begin_timer_tree_bottom_up(*NamedTimerFactory::instance().getRootTimer());
|
||||||
|
it != end_timer_tree_bottom_up();
|
||||||
|
++it)
|
||||||
|
{
|
||||||
|
NamedTimer* timerp = *it;
|
||||||
|
// skip root timer
|
||||||
|
if (timerp == NamedTimerFactory::instance().getRootTimer()) continue;
|
||||||
|
|
||||||
|
if (timerp->getFrameState().mMoveUpTree)
|
||||||
|
{
|
||||||
|
// since ancestors have already been visited, reparenting won't affect tree traversal
|
||||||
|
//step up tree, bringing our descendants with us
|
||||||
|
//llinfos << "Moving " << timerp->getName() << " from child of " << timerp->getParent()->getName() <<
|
||||||
|
// " to child of " << timerp->getParent()->getParent()->getName() << llendl;
|
||||||
|
timerp->setParent(timerp->getParent()->getParent());
|
||||||
|
timerp->getFrameState().mMoveUpTree = false;
|
||||||
|
|
||||||
|
// don't bubble up any ancestors until descendants are done bubbling up
|
||||||
|
it.skipAncestors();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort timers by time last called, so call graph makes sense
|
||||||
|
for(timer_tree_dfs_iterator_t it = begin_timer_tree(*NamedTimerFactory::instance().getRootTimer());
|
||||||
|
it != end_timer_tree();
|
||||||
|
++it)
|
||||||
|
{
|
||||||
|
NamedTimer* timerp = (*it);
|
||||||
|
if (timerp->mNeedsSorting)
|
||||||
|
{
|
||||||
|
std::sort(timerp->getChildren().begin(), timerp->getChildren().end(), SortTimerByName());
|
||||||
|
}
|
||||||
|
timerp->mNeedsSorting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//static
|
||||||
|
void LLFastTimer::NamedTimer::accumulateTimings()
|
||||||
|
{
|
||||||
|
U32 cur_time = getCPUClockCount32();
|
||||||
|
|
||||||
|
// walk up stack of active timers and accumulate current time while leaving timing structures active
|
||||||
|
LLFastTimer* cur_timer = sCurTimerData.mCurTimer;
|
||||||
|
// root defined by parent pointing to self
|
||||||
|
CurTimerData* cur_data = &sCurTimerData;
|
||||||
|
while(cur_timer->mLastTimerData.mCurTimer != cur_timer)
|
||||||
|
{
|
||||||
|
U32 cumulative_time_delta = cur_time - cur_timer->mStartTime;
|
||||||
|
U32 self_time_delta = cumulative_time_delta - cur_data->mChildTime;
|
||||||
|
cur_data->mChildTime = 0;
|
||||||
|
cur_timer->mFrameState->mSelfTimeCounter += self_time_delta;
|
||||||
|
cur_timer->mStartTime = cur_time;
|
||||||
|
|
||||||
|
cur_data = &cur_timer->mLastTimerData;
|
||||||
|
cur_data->mChildTime += cumulative_time_delta;
|
||||||
|
|
||||||
|
cur_timer = cur_timer->mLastTimerData.mCurTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// traverse tree in DFS post order, or bottom up
|
||||||
|
for(timer_tree_bottom_up_iterator_t it = begin_timer_tree_bottom_up(*NamedTimerFactory::instance().getActiveRootTimer());
|
||||||
|
it != end_timer_tree_bottom_up();
|
||||||
|
++it)
|
||||||
|
{
|
||||||
|
NamedTimer* timerp = (*it);
|
||||||
|
timerp->mTotalTimeCounter = timerp->getFrameState().mSelfTimeCounter;
|
||||||
|
for (child_const_iter child_it = timerp->beginChildren(); child_it != timerp->endChildren(); ++child_it)
|
||||||
|
{
|
||||||
|
timerp->mTotalTimeCounter += (*child_it)->mTotalTimeCounter;
|
||||||
|
}
|
||||||
|
|
||||||
|
S32 cur_frame = sCurFrameIndex;
|
||||||
|
if (cur_frame >= 0)
|
||||||
|
{
|
||||||
|
// update timer history
|
||||||
|
int hidx = cur_frame % HISTORY_NUM;
|
||||||
|
|
||||||
|
timerp->mCountHistory[hidx] = timerp->mTotalTimeCounter;
|
||||||
|
timerp->mCountAverage = ((U64)timerp->mCountAverage * cur_frame + timerp->mTotalTimeCounter) / (cur_frame+1);
|
||||||
|
timerp->mCallHistory[hidx] = timerp->getFrameState().mCalls;
|
||||||
|
timerp->mCallAverage = ((U64)timerp->mCallAverage * cur_frame + timerp->getFrameState().mCalls) / (cur_frame+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
U32 LLFastTimer::NamedTimer::getCountAverage() const
|
||||||
|
{
|
||||||
|
return mCountAverage;// (sCurFrameIndex <= 0 || mCountAverage <= 0) ? 0 : mCountAverage / llmin(sCurFrameIndex,(S32)HISTORY_NUM);
|
||||||
|
}
|
||||||
|
U32 LLFastTimer::NamedTimer::getCallAverage() const
|
||||||
|
{
|
||||||
|
return mCallAverage;// (sCurFrameIndex <= 0 || mCallAverage <= 0) ? 0 : mCallAverage / llmin(sCurFrameIndex,(S32)HISTORY_NUM);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void LLFastTimer::NamedTimer::resetFrame()
|
||||||
|
{
|
||||||
|
if (sLog)
|
||||||
|
{ //output current frame counts to performance log
|
||||||
|
|
||||||
|
static S32 call_count = 0;
|
||||||
|
if (call_count % 100 == 0)
|
||||||
|
{
|
||||||
|
llinfos << "countsPerSecond (32 bit): " << countsPerSecond() << llendl;
|
||||||
|
llinfos << "get_clock_count (64 bit): " << get_clock_count() << llendl;
|
||||||
|
llinfos << "LLProcessorInfo().getCPUFrequency() " << LLProcessorInfo().getCPUFrequency() << llendl;
|
||||||
|
llinfos << "getCPUClockCount32() " << getCPUClockCount32() << llendl;
|
||||||
|
llinfos << "getCPUClockCount64() " << getCPUClockCount64() << llendl;
|
||||||
|
llinfos << "elapsed sec " << ((F64)getCPUClockCount64())/((F64)LLProcessorInfo().getCPUFrequency()*1000000.0) << llendl;
|
||||||
|
}
|
||||||
|
call_count++;
|
||||||
|
|
||||||
|
F64 iclock_freq = 1000.0 / countsPerSecond(); // good place to calculate clock frequency
|
||||||
|
|
||||||
|
F64 total_time = 0;
|
||||||
|
LLSD sd;
|
||||||
|
|
||||||
|
{
|
||||||
|
for (instance_iter it = beginInstances(); it != endInstances(); ++it)
|
||||||
|
{
|
||||||
|
NamedTimer& timer = *it;
|
||||||
|
FrameState& info = timer.getFrameState();
|
||||||
|
sd[timer.getName()]["Time"] = (LLSD::Real) (info.mSelfTimeCounter*iclock_freq);
|
||||||
|
sd[timer.getName()]["Calls"] = (LLSD::Integer) info.mCalls;
|
||||||
|
|
||||||
|
// computing total time here because getting the root timer's getCountHistory
|
||||||
|
// doesn't work correctly on the first frame
|
||||||
|
total_time = total_time + info.mSelfTimeCounter * iclock_freq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sd["Total"]["Time"] = (LLSD::Real) total_time;
|
||||||
|
sd["Total"]["Calls"] = (LLSD::Integer) 1;
|
||||||
|
|
||||||
|
{
|
||||||
|
LLMutexLock lock(sLogLock);
|
||||||
|
sLogQueue.push(sd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// tag timers by position in depth first traversal of tree
|
||||||
|
S32 index = 0;
|
||||||
|
for(timer_tree_dfs_iterator_t it = begin_timer_tree(*NamedTimerFactory::instance().getRootTimer());
|
||||||
|
it != end_timer_tree();
|
||||||
|
++it)
|
||||||
|
{
|
||||||
|
NamedTimer* timerp = (*it);
|
||||||
|
|
||||||
|
timerp->mFrameStateIndex = index;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
llassert(index == (S32)getFrameStateList().size());
|
||||||
|
|
||||||
|
// sort timers by DFS traversal order to improve cache coherency
|
||||||
|
std::sort(getFrameStateList().begin(), getFrameStateList().end(), SortTimersDFS());
|
||||||
|
|
||||||
|
// update pointers into framestatelist now that we've sorted it
|
||||||
|
updateCachedPointers();
|
||||||
|
|
||||||
|
// reset for next frame
|
||||||
|
{
|
||||||
|
for (instance_iter it = beginInstances(); it != endInstances(); ++it)
|
||||||
|
{
|
||||||
|
NamedTimer& timer = *it;
|
||||||
|
|
||||||
|
FrameState& info = timer.getFrameState();
|
||||||
|
info.mSelfTimeCounter = 0;
|
||||||
|
info.mCalls = 0;
|
||||||
|
info.mLastCaller = NULL;
|
||||||
|
info.mMoveUpTree = false;
|
||||||
|
// update parent pointer in timer state struct
|
||||||
|
if (timer.mParent)
|
||||||
|
{
|
||||||
|
info.mParent = &timer.mParent->getFrameState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//sTimerCycles = 0;
|
||||||
|
//sTimerCalls = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//static
|
||||||
|
void LLFastTimer::NamedTimer::reset()
|
||||||
|
{
|
||||||
|
resetFrame(); // reset frame data
|
||||||
|
|
||||||
|
// walk up stack of active timers and reset start times to current time
|
||||||
|
// effectively zeroing out any accumulated time
|
||||||
|
U32 cur_time = getCPUClockCount32();
|
||||||
|
|
||||||
|
// root defined by parent pointing to self
|
||||||
|
CurTimerData* cur_data = &sCurTimerData;
|
||||||
|
LLFastTimer* cur_timer = cur_data->mCurTimer;
|
||||||
|
while(cur_timer->mLastTimerData.mCurTimer != cur_timer)
|
||||||
|
{
|
||||||
|
cur_timer->mStartTime = cur_time;
|
||||||
|
cur_data->mChildTime = 0;
|
||||||
|
|
||||||
|
cur_data = &cur_timer->mLastTimerData;
|
||||||
|
cur_timer = cur_data->mCurTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset all history
|
||||||
|
{
|
||||||
|
for (instance_iter it = beginInstances(); it != endInstances(); ++it)
|
||||||
|
{
|
||||||
|
NamedTimer& timer = *it;
|
||||||
|
if (&timer != NamedTimerFactory::instance().getRootTimer())
|
||||||
|
{
|
||||||
|
timer.setParent(NamedTimerFactory::instance().getRootTimer());
|
||||||
|
}
|
||||||
|
|
||||||
|
timer.mCountAverage = 0;
|
||||||
|
timer.mCallAverage = 0;
|
||||||
|
memset(timer.mCountHistory, 0, sizeof(U32) * HISTORY_NUM);
|
||||||
|
memset(timer.mCallHistory, 0, sizeof(U32) * HISTORY_NUM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sLastFrameIndex = 0;
|
||||||
|
sCurFrameIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//static
|
||||||
|
LLFastTimer::info_list_t& LLFastTimer::getFrameStateList()
|
||||||
|
{
|
||||||
|
if (!sTimerInfos)
|
||||||
|
{
|
||||||
|
sTimerInfos = new info_list_t;
|
||||||
|
#if 0
|
||||||
|
// Avoid the vector being moved in memory by reserving enough memory right away.
|
||||||
|
sTimerInfos->reserve(1024);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return *sTimerInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
U32 LLFastTimer::NamedTimer::getHistoricalCount(S32 history_index) const
|
||||||
|
{
|
||||||
|
S32 history_idx = (getLastFrameIndex() + history_index) % LLFastTimer::NamedTimer::HISTORY_NUM;
|
||||||
|
return mCountHistory[history_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
U32 LLFastTimer::NamedTimer::getHistoricalCalls(S32 history_index ) const
|
||||||
|
{
|
||||||
|
S32 history_idx = (getLastFrameIndex() + history_index) % LLFastTimer::NamedTimer::HISTORY_NUM;
|
||||||
|
return mCallHistory[history_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
LLFastTimer::FrameState& LLFastTimer::NamedTimer::getFrameState() const
|
||||||
|
{
|
||||||
|
llassert_always(mFrameStateIndex >= 0);
|
||||||
|
if (this == NamedTimerFactory::instance().getActiveRootTimer())
|
||||||
|
{
|
||||||
|
return NamedTimerFactory::instance().getRootFrameState();
|
||||||
|
}
|
||||||
|
return getFrameStateList()[mFrameStateIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
LLFastTimer::NamedTimer& LLFastTimer::NamedTimer::getRootNamedTimer()
|
||||||
|
{
|
||||||
|
return *NamedTimerFactory::instance().getActiveRootTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<LLFastTimer::NamedTimer*>::const_iterator LLFastTimer::NamedTimer::beginChildren()
|
||||||
|
{
|
||||||
|
return mChildren.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<LLFastTimer::NamedTimer*>::const_iterator LLFastTimer::NamedTimer::endChildren()
|
||||||
|
{
|
||||||
|
return mChildren.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<LLFastTimer::NamedTimer*>& LLFastTimer::NamedTimer::getChildren()
|
||||||
|
{
|
||||||
|
return mChildren;
|
||||||
|
}
|
||||||
|
|
||||||
|
//static
|
||||||
|
void LLFastTimer::nextFrame()
|
||||||
|
{
|
||||||
|
countsPerSecond(); // good place to calculate clock frequency
|
||||||
|
U64 frame_time = getCPUClockCount64();
|
||||||
|
if ((frame_time - sLastFrameTime) >> 8 > 0xffffffff)
|
||||||
|
{
|
||||||
|
llinfos << "Slow frame, fast timers inaccurate" << llendl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sPauseHistory)
|
||||||
|
{
|
||||||
|
NamedTimer::processTimes();
|
||||||
|
sLastFrameIndex = sCurFrameIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get ready for next frame
|
||||||
|
NamedTimer::resetFrame();
|
||||||
|
sLastFrameTime = frame_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
//static
|
||||||
|
void LLFastTimer::dumpCurTimes()
|
||||||
|
{
|
||||||
|
// accumulate timings, etc.
|
||||||
|
NamedTimer::processTimes();
|
||||||
|
|
||||||
|
F64 clock_freq = (F64)countsPerSecond();
|
||||||
|
F64 iclock_freq = 1000.0 / clock_freq; // clock_ticks -> milliseconds
|
||||||
|
|
||||||
|
// walk over timers in depth order and output timings
|
||||||
|
for(timer_tree_dfs_iterator_t it = begin_timer_tree(*NamedTimerFactory::instance().getRootTimer());
|
||||||
|
it != end_timer_tree();
|
||||||
|
++it)
|
||||||
|
{
|
||||||
|
NamedTimer* timerp = (*it);
|
||||||
|
F64 total_time_ms = ((F64)timerp->getHistoricalCount(0) * iclock_freq);
|
||||||
|
// Don't bother with really brief times, keep output concise
|
||||||
|
if (total_time_ms < 0.1) continue;
|
||||||
|
|
||||||
|
std::ostringstream out_str;
|
||||||
|
for (S32 i = 0; i < timerp->getDepth(); i++)
|
||||||
|
{
|
||||||
|
out_str << "\t";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
out_str << timerp->getName() << " "
|
||||||
|
<< std::setprecision(3) << total_time_ms << " ms, "
|
||||||
|
<< timerp->getHistoricalCalls(0) << " calls";
|
||||||
|
|
||||||
|
llinfos << out_str.str() << llendl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//static
|
||||||
|
void LLFastTimer::reset()
|
||||||
|
{
|
||||||
|
NamedTimer::reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//static
|
||||||
|
void LLFastTimer::writeLog(std::ostream& os)
|
||||||
|
{
|
||||||
|
while (!sLogQueue.empty())
|
||||||
|
{
|
||||||
|
LLSD& sd = sLogQueue.front();
|
||||||
|
LLSDSerialize::toXML(sd, os);
|
||||||
|
LLMutexLock lock(sLogLock);
|
||||||
|
sLogQueue.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//static
|
||||||
|
const LLFastTimer::NamedTimer* LLFastTimer::getTimerByName(const std::string& name)
|
||||||
|
{
|
||||||
|
return NamedTimerFactory::instance().getTimerByName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLFastTimer::LLFastTimer(LLFastTimer::FrameState* state)
|
||||||
|
: mFrameState(state)
|
||||||
|
{
|
||||||
|
// Only called for mAppTimer with mRootFrameState, which never invalidates.
|
||||||
|
llassert(state == &NamedTimerFactory::instance().getRootFrameState());
|
||||||
|
|
||||||
|
U32 start_time = getCPUClockCount32();
|
||||||
|
mStartTime = start_time;
|
||||||
|
mFrameState->mActiveCount++;
|
||||||
|
LLFastTimer::sCurTimerData.mCurTimer = this;
|
||||||
|
LLFastTimer::sCurTimerData.mNamedTimer = mFrameState->mTimer;
|
||||||
|
LLFastTimer::sCurTimerData.mFrameState = mFrameState;
|
||||||
|
LLFastTimer::sCurTimerData.mChildTime = 0;
|
||||||
|
// This is the root FastTimer (mAppTimer), mark it as such by having
|
||||||
|
// mLastTimerData be equal to sCurTimerData (which is a rather arbitrary
|
||||||
|
// and not very logical way to do that --Aleric).
|
||||||
|
mLastTimerData = LLFastTimer::sCurTimerData;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Important note: These implementations must be FAST!
|
||||||
|
//
|
||||||
|
|
||||||
|
//LL_COMMON_API U64 get_clock_count(); // in lltimer.cpp
|
||||||
|
// These use QueryPerformanceCounter, which is arguably fine and also works on AMD architectures.
|
||||||
|
U32 LLFastTimer::getCPUClockCount32()
|
||||||
|
{
|
||||||
|
return (U32)(get_clock_count()>>8);
|
||||||
|
}
|
||||||
|
|
||||||
|
U64 LLFastTimer::getCPUClockCount64()
|
||||||
|
{
|
||||||
|
return get_clock_count();
|
||||||
|
}
|
||||||
|
|
||||||
277
indra/llcommon/llfasttimer_class.h
Normal file
277
indra/llcommon/llfasttimer_class.h
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
/**
|
||||||
|
* @file llfasttimer_class.h
|
||||||
|
* @brief Declaration of a fast timer.
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation;
|
||||||
|
* version 2.1 of the License only.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LL_FASTTIMER_CLASS_H
|
||||||
|
#define LL_FASTTIMER_CLASS_H
|
||||||
|
|
||||||
|
#include "llinstancetracker.h"
|
||||||
|
|
||||||
|
#define FAST_TIMER_ON 1
|
||||||
|
#define TIME_FAST_TIMERS 0
|
||||||
|
#define DEBUG_FAST_TIMER_THREADS 1
|
||||||
|
|
||||||
|
class LLMutex;
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
#include "llsd.h"
|
||||||
|
|
||||||
|
LL_COMMON_API void assert_main_thread();
|
||||||
|
|
||||||
|
class LL_COMMON_API LLFastTimer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class NamedTimer;
|
||||||
|
|
||||||
|
struct LL_COMMON_API FrameState
|
||||||
|
{
|
||||||
|
FrameState(NamedTimer* timerp);
|
||||||
|
|
||||||
|
U32 mSelfTimeCounter;
|
||||||
|
U32 mCalls;
|
||||||
|
FrameState* mParent; // info for caller timer
|
||||||
|
NamedTimer* mLastCaller; // used to bootstrap tree construction
|
||||||
|
NamedTimer* mTimer;
|
||||||
|
U16 mActiveCount; // number of timers with this ID active on stack
|
||||||
|
bool mMoveUpTree; // needs to be moved up the tree of timers at the end of frame
|
||||||
|
};
|
||||||
|
|
||||||
|
// stores a "named" timer instance to be reused via multiple LLFastTimer stack instances
|
||||||
|
class LL_COMMON_API NamedTimer
|
||||||
|
: public LLInstanceTracker<NamedTimer>
|
||||||
|
{
|
||||||
|
friend class DeclareTimer;
|
||||||
|
public:
|
||||||
|
~NamedTimer();
|
||||||
|
|
||||||
|
enum { HISTORY_NUM = 300 };
|
||||||
|
|
||||||
|
const std::string& getName() const { return mName; }
|
||||||
|
NamedTimer* getParent() const { return mParent; }
|
||||||
|
void setParent(NamedTimer* parent);
|
||||||
|
S32 getDepth();
|
||||||
|
std::string getToolTip(S32 history_index = -1);
|
||||||
|
|
||||||
|
typedef std::vector<NamedTimer*>::const_iterator child_const_iter;
|
||||||
|
child_const_iter beginChildren();
|
||||||
|
child_const_iter endChildren();
|
||||||
|
std::vector<NamedTimer*>& getChildren();
|
||||||
|
|
||||||
|
void setCollapsed(bool collapsed) { mCollapsed = collapsed; }
|
||||||
|
bool getCollapsed() const { return mCollapsed; }
|
||||||
|
|
||||||
|
U32 getCountAverage() const; //{ return mCountAverage }
|
||||||
|
U32 getCallAverage() const; //{ return mCallAverage }
|
||||||
|
|
||||||
|
U32 getHistoricalCount(S32 history_index = 0) const;
|
||||||
|
U32 getHistoricalCalls(S32 history_index = 0) const;
|
||||||
|
|
||||||
|
static NamedTimer& getRootNamedTimer();
|
||||||
|
|
||||||
|
S32 getFrameStateIndex() const { return mFrameStateIndex; }
|
||||||
|
|
||||||
|
FrameState& getFrameState() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class LLFastTimer;
|
||||||
|
friend class NamedTimerFactory;
|
||||||
|
|
||||||
|
//
|
||||||
|
// methods
|
||||||
|
//
|
||||||
|
NamedTimer(const std::string& name);
|
||||||
|
// recursive call to gather total time from children
|
||||||
|
static void accumulateTimings();
|
||||||
|
|
||||||
|
// updates cumulative times and hierarchy,
|
||||||
|
// can be called multiple times in a frame, at any point
|
||||||
|
static void processTimes();
|
||||||
|
|
||||||
|
static void buildHierarchy();
|
||||||
|
static void resetFrame();
|
||||||
|
static void reset();
|
||||||
|
|
||||||
|
//
|
||||||
|
// members
|
||||||
|
//
|
||||||
|
S32 mFrameStateIndex;
|
||||||
|
|
||||||
|
std::string mName;
|
||||||
|
|
||||||
|
U32 mTotalTimeCounter;
|
||||||
|
|
||||||
|
U32 mCountAverage;
|
||||||
|
U32 mCallAverage;
|
||||||
|
|
||||||
|
U32* mCountHistory;
|
||||||
|
U32* mCallHistory;
|
||||||
|
|
||||||
|
// tree structure
|
||||||
|
NamedTimer* mParent; // NamedTimer of caller(parent)
|
||||||
|
std::vector<NamedTimer*> mChildren;
|
||||||
|
bool mCollapsed; // don't show children
|
||||||
|
bool mNeedsSorting; // sort children whenever child added
|
||||||
|
};
|
||||||
|
|
||||||
|
// used to statically declare a new named timer
|
||||||
|
class LL_COMMON_API DeclareTimer
|
||||||
|
: public LLInstanceTracker<DeclareTimer>
|
||||||
|
{
|
||||||
|
friend class LLFastTimer;
|
||||||
|
public:
|
||||||
|
DeclareTimer(const std::string& name, bool open);
|
||||||
|
DeclareTimer(const std::string& name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
NamedTimer& mTimer;
|
||||||
|
FrameState* mFrameState;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
LLFastTimer(LLFastTimer::FrameState* state);
|
||||||
|
|
||||||
|
LL_FORCE_INLINE LLFastTimer(LLFastTimer::DeclareTimer& timer)
|
||||||
|
: mFrameState(timer.mFrameState)
|
||||||
|
{
|
||||||
|
#if TIME_FAST_TIMERS
|
||||||
|
U64 timer_start = getCPUClockCount64();
|
||||||
|
#endif
|
||||||
|
#if FAST_TIMER_ON
|
||||||
|
LLFastTimer::FrameState* frame_state = mFrameState;
|
||||||
|
mStartTime = getCPUClockCount32();
|
||||||
|
|
||||||
|
frame_state->mActiveCount++;
|
||||||
|
frame_state->mCalls++;
|
||||||
|
// keep current parent as long as it is active when we are
|
||||||
|
frame_state->mMoveUpTree |= (frame_state->mParent->mActiveCount == 0);
|
||||||
|
|
||||||
|
LLFastTimer::CurTimerData* cur_timer_data = &LLFastTimer::sCurTimerData;
|
||||||
|
mLastTimerData = *cur_timer_data;
|
||||||
|
cur_timer_data->mCurTimer = this;
|
||||||
|
cur_timer_data->mNamedTimer = &timer.mTimer;
|
||||||
|
cur_timer_data->mFrameState = frame_state;
|
||||||
|
cur_timer_data->mChildTime = 0;
|
||||||
|
#endif
|
||||||
|
#if TIME_FAST_TIMERS
|
||||||
|
U64 timer_end = getCPUClockCount64();
|
||||||
|
sTimerCycles += timer_end - timer_start;
|
||||||
|
#endif
|
||||||
|
#if DEBUG_FAST_TIMER_THREADS
|
||||||
|
#if !LL_RELEASE
|
||||||
|
assert_main_thread();
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
LL_FORCE_INLINE ~LLFastTimer()
|
||||||
|
{
|
||||||
|
#if TIME_FAST_TIMERS
|
||||||
|
U64 timer_start = getCPUClockCount64();
|
||||||
|
#endif
|
||||||
|
#if FAST_TIMER_ON
|
||||||
|
LLFastTimer::FrameState* frame_state = mFrameState;
|
||||||
|
U32 total_time = getCPUClockCount32() - mStartTime;
|
||||||
|
|
||||||
|
frame_state->mSelfTimeCounter += total_time - LLFastTimer::sCurTimerData.mChildTime;
|
||||||
|
frame_state->mActiveCount--;
|
||||||
|
|
||||||
|
// store last caller to bootstrap tree creation
|
||||||
|
// do this in the destructor in case of recursion to get topmost caller
|
||||||
|
frame_state->mLastCaller = mLastTimerData.mNamedTimer;
|
||||||
|
|
||||||
|
// we are only tracking self time, so subtract our total time delta from parents
|
||||||
|
mLastTimerData.mChildTime += total_time;
|
||||||
|
|
||||||
|
LLFastTimer::sCurTimerData = mLastTimerData;
|
||||||
|
#endif
|
||||||
|
#if TIME_FAST_TIMERS
|
||||||
|
U64 timer_end = getCPUClockCount64();
|
||||||
|
sTimerCycles += timer_end - timer_start;
|
||||||
|
sTimerCalls++;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static LLMutex* sLogLock;
|
||||||
|
static std::queue<LLSD> sLogQueue;
|
||||||
|
static BOOL sLog;
|
||||||
|
static BOOL sMetricLog;
|
||||||
|
static std::string sLogName;
|
||||||
|
static bool sPauseHistory;
|
||||||
|
static bool sResetHistory;
|
||||||
|
static U64 sTimerCycles;
|
||||||
|
static U32 sTimerCalls;
|
||||||
|
|
||||||
|
typedef std::vector<FrameState> info_list_t;
|
||||||
|
static info_list_t& getFrameStateList();
|
||||||
|
|
||||||
|
|
||||||
|
// call this once a frame to reset timers
|
||||||
|
static void nextFrame();
|
||||||
|
|
||||||
|
// dumps current cumulative frame stats to log
|
||||||
|
// call nextFrame() to reset timers
|
||||||
|
static void dumpCurTimes();
|
||||||
|
|
||||||
|
// call this to reset timer hierarchy, averages, etc.
|
||||||
|
static void reset();
|
||||||
|
|
||||||
|
// called to update all FrameState pointers.
|
||||||
|
static void updateCachedPointers();
|
||||||
|
|
||||||
|
static U64 countsPerSecond();
|
||||||
|
static S32 getLastFrameIndex() { return sLastFrameIndex; }
|
||||||
|
static S32 getCurFrameIndex() { return sCurFrameIndex; }
|
||||||
|
|
||||||
|
static void writeLog(std::ostream& os);
|
||||||
|
static const NamedTimer* getTimerByName(const std::string& name);
|
||||||
|
|
||||||
|
struct CurTimerData
|
||||||
|
{
|
||||||
|
LLFastTimer* mCurTimer;
|
||||||
|
NamedTimer* mNamedTimer;
|
||||||
|
FrameState* mFrameState;
|
||||||
|
U32 mChildTime;
|
||||||
|
};
|
||||||
|
static CurTimerData sCurTimerData;
|
||||||
|
static std::string sClockType;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static U32 getCPUClockCount32();
|
||||||
|
static U64 getCPUClockCount64();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static S32 sCurFrameIndex;
|
||||||
|
static S32 sLastFrameIndex;
|
||||||
|
static U64 sLastFrameTime;
|
||||||
|
static info_list_t* sTimerInfos;
|
||||||
|
|
||||||
|
U32 mStartTime;
|
||||||
|
LLFastTimer::FrameState* mFrameState;
|
||||||
|
LLFastTimer::CurTimerData mLastTimerData;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LL_LLFASTTIMER_H
|
||||||
@@ -35,56 +35,59 @@
|
|||||||
#include "u64.h"
|
#include "u64.h"
|
||||||
|
|
||||||
#include "llframetimer.h"
|
#include "llframetimer.h"
|
||||||
|
#include "aiframetimer.h"
|
||||||
|
#include "llaprpool.h"
|
||||||
|
|
||||||
// Local constants.
|
// Local constants.
|
||||||
static F64 const USEC_PER_SECOND = 1000000.0;
|
static F64 const USEC_PER_SECOND = 1000000.0;
|
||||||
static F64 const USEC_TO_SEC_F64 = 0.000001;
|
static F64 const USEC_TO_SEC_F64 = 0.000001;
|
||||||
|
static F64 const NEVER = 1e16;
|
||||||
|
|
||||||
// Static members
|
// Static members
|
||||||
U64 const LLFrameTimer::sStartTotalTime = totalTime(); // Application start in microseconds since epoch.
|
U64 const LLFrameTimer::sStartTotalTime = totalTime(); // Application start in microseconds since epoch.
|
||||||
U64 LLFrameTimer::sTotalTime = LLFrameTimer::sStartTotalTime; // Current time in microseconds since epoch, updated at least once per frame.
|
U64 LLFrameTimer::sTotalTime = LLFrameTimer::sStartTotalTime; // Current time in microseconds since epoch, updated at least once per frame.
|
||||||
F64 LLFrameTimer::sTotalSeconds = // Current time in seconds since epoch, updated together with LLFrameTimer::sTotalTime.
|
F64 LLFrameTimer::sTotalSeconds = // Current time in seconds since epoch, updated together with LLFrameTimer::sTotalTime.
|
||||||
U64_to_F64(LLFrameTimer::sTotalTime) * USEC_TO_SEC_F64;
|
U64_to_F64(LLFrameTimer::sTotalTime) * USEC_TO_SEC_F64;
|
||||||
F64 LLFrameTimer::sFrameTime = 0.0; // Current time in seconds since application start, updated together with LLFrameTimer::sTotalTime.
|
F64 LLFrameTimer::sFrameTime = 0.0; // Current time in seconds since application start, updated together with LLFrameTimer::sTotalTime.
|
||||||
// Updated exactly once per frame:
|
// Updated exactly once per frame:
|
||||||
S32 LLFrameTimer::sFrameCount = 0; // Current frame number (number of frames since application start).
|
S32 LLFrameTimer::sFrameCount = 0; // Current frame number (number of frames since application start).
|
||||||
U64 LLFrameTimer::sPrevTotalTime = LLFrameTimer::sStartTotalTime; // Previous (frame) time in microseconds since epoch, updated once per frame.
|
U64 LLFrameTimer::sPrevTotalTime = LLFrameTimer::sStartTotalTime; // Previous (frame) time in microseconds since epoch, updated once per frame.
|
||||||
U64 LLFrameTimer::sFrameDeltaTime = 0; // Microseconds between last two calls to LLFrameTimer::updateFrameTimeAndCount.
|
U64 LLFrameTimer::sFrameDeltaTime = 0; // Microseconds between last two calls to LLFrameTimer::updateFrameTimeAndCount.
|
||||||
|
// Mutex for the above.
|
||||||
|
apr_thread_mutex_t* LLFrameTimer::sGlobalMutex;
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void LLFrameTimer::updateFrameTime()
|
void LLFrameTimer::global_initialization(void)
|
||||||
{
|
{
|
||||||
sTotalTime = totalTime();
|
apr_thread_mutex_create(&sGlobalMutex, APR_THREAD_MUTEX_UNNESTED, LLAPRRootPool::get()());
|
||||||
sTotalSeconds = U64_to_F64(sTotalTime) * USEC_TO_SEC_F64;
|
AIFrameTimer::sNextExpiration = NEVER;
|
||||||
sFrameTime = U64_to_F64(sTotalTime - sStartTotalTime) * USEC_TO_SEC_F64;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void LLFrameTimer::updateFrameTimeAndCount()
|
void LLFrameTimer::updateFrameTime(void)
|
||||||
|
{
|
||||||
|
llassert(is_main_thread());
|
||||||
|
sTotalTime = totalTime();
|
||||||
|
sTotalSeconds = U64_to_F64(sTotalTime) * USEC_TO_SEC_F64;
|
||||||
|
F64 new_frame_time = U64_to_F64(sTotalTime - sStartTotalTime) * USEC_TO_SEC_F64;
|
||||||
|
apr_thread_mutex_lock(sGlobalMutex);
|
||||||
|
sFrameTime = new_frame_time;
|
||||||
|
apr_thread_mutex_unlock(sGlobalMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void LLFrameTimer::updateFrameTimeAndCount(void)
|
||||||
{
|
{
|
||||||
updateFrameTime();
|
updateFrameTime();
|
||||||
sFrameDeltaTime = sTotalTime - sPrevTotalTime;
|
sFrameDeltaTime = sTotalTime - sPrevTotalTime;
|
||||||
sPrevTotalTime = sTotalTime;
|
sPrevTotalTime = sTotalTime;
|
||||||
++sFrameCount;
|
++sFrameCount;
|
||||||
}
|
|
||||||
|
|
||||||
void LLFrameTimer::reset(F32 expiration)
|
// Handle AIFrameTimer expiration and callbacks.
|
||||||
{
|
if (AIFrameTimer::sNextExpiration <= sFrameTime)
|
||||||
llassert(!mPaused);
|
{
|
||||||
mStartTime = sFrameTime;
|
AIFrameTimer::handleExpiration(sFrameTime);
|
||||||
mExpiry = sFrameTime + expiration;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void LLFrameTimer::start(F32 expiration)
|
|
||||||
{
|
|
||||||
reset(expiration);
|
|
||||||
mRunning = true; // Start, if not already started.
|
|
||||||
}
|
|
||||||
|
|
||||||
void LLFrameTimer::stop()
|
|
||||||
{
|
|
||||||
llassert(!mPaused);
|
|
||||||
mRunning = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't combine pause/unpause with start/stop
|
// Don't combine pause/unpause with start/stop
|
||||||
@@ -95,38 +98,38 @@ void LLFrameTimer::stop()
|
|||||||
// foo.unpause() // unpauses
|
// foo.unpause() // unpauses
|
||||||
// F32 elapsed = foo.getElapsedTimeF32() // does not include time between pause() and unpause()
|
// F32 elapsed = foo.getElapsedTimeF32() // does not include time between pause() and unpause()
|
||||||
// Note: elapsed would also be valid with no unpause() call (= time run until pause() called)
|
// Note: elapsed would also be valid with no unpause() call (= time run until pause() called)
|
||||||
void LLFrameTimer::pause()
|
void LLFrameTimer::pause(void)
|
||||||
{
|
{
|
||||||
|
llassert(is_main_thread());
|
||||||
if (!mPaused)
|
if (!mPaused)
|
||||||
{
|
{
|
||||||
|
// Only the main thread writes to sFrameTime, so there is no need for locking.
|
||||||
mStartTime = sFrameTime - mStartTime; // Abuse mStartTime to store the elapsed time so far.
|
mStartTime = sFrameTime - mStartTime; // Abuse mStartTime to store the elapsed time so far.
|
||||||
}
|
}
|
||||||
mPaused = true;
|
mPaused = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLFrameTimer::unpause()
|
void LLFrameTimer::unpause(void)
|
||||||
{
|
{
|
||||||
|
llassert(is_main_thread());
|
||||||
if (mPaused)
|
if (mPaused)
|
||||||
{
|
{
|
||||||
|
// Only the main thread writes to sFrameTime, so there is no need for locking.
|
||||||
mStartTime = sFrameTime - mStartTime; // Set mStartTime consistent with the elapsed time so far.
|
mStartTime = sFrameTime - mStartTime; // Set mStartTime consistent with the elapsed time so far.
|
||||||
}
|
}
|
||||||
mPaused = false;
|
mPaused = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLFrameTimer::setTimerExpirySec(F32 expiration)
|
|
||||||
{
|
|
||||||
llassert(!mPaused);
|
|
||||||
mExpiry = mStartTime + expiration;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LLFrameTimer::setExpiryAt(F64 seconds_since_epoch)
|
void LLFrameTimer::setExpiryAt(F64 seconds_since_epoch)
|
||||||
{
|
{
|
||||||
|
llassert(is_main_thread());
|
||||||
llassert(!mPaused);
|
llassert(!mPaused);
|
||||||
|
// Only the main thread writes to sFrameTime, so there is no need for locking.
|
||||||
mStartTime = sFrameTime;
|
mStartTime = sFrameTime;
|
||||||
mExpiry = seconds_since_epoch - (USEC_TO_SEC_F64 * sStartTotalTime);
|
mExpiry = seconds_since_epoch - (USEC_TO_SEC_F64 * sStartTotalTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
F64 LLFrameTimer::expiresAt() const
|
F64 LLFrameTimer::expiresAt(void) const
|
||||||
{
|
{
|
||||||
F64 expires_at = U64_to_F64(sStartTotalTime) * USEC_TO_SEC_F64;
|
F64 expires_at = U64_to_F64(sStartTotalTime) * USEC_TO_SEC_F64;
|
||||||
expires_at += mExpiry;
|
expires_at += mExpiry;
|
||||||
@@ -135,31 +138,47 @@ F64 LLFrameTimer::expiresAt() const
|
|||||||
|
|
||||||
bool LLFrameTimer::checkExpirationAndReset(F32 expiration)
|
bool LLFrameTimer::checkExpirationAndReset(F32 expiration)
|
||||||
{
|
{
|
||||||
if (hasExpired())
|
llassert(!mPaused);
|
||||||
|
F64 frame_time = getElapsedSeconds();
|
||||||
|
if (frame_time >= mExpiry)
|
||||||
{
|
{
|
||||||
reset(expiration);
|
mStartTime = frame_time;
|
||||||
|
mExpiry = mStartTime + expiration;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
F32 LLFrameTimer::getElapsedTimeAndResetF32(void)
|
||||||
F32 LLFrameTimer::getFrameDeltaTimeF32()
|
|
||||||
{
|
{
|
||||||
|
llassert(mRunning && !mPaused);
|
||||||
|
F64 frame_time = getElapsedSeconds();
|
||||||
|
F32 elapsed_time = (F32)(frame_time - mStartTime);
|
||||||
|
mExpiry = mStartTime = frame_time;
|
||||||
|
return elapsed_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
// Return number of seconds between the last two frames.
|
||||||
|
F32 LLFrameTimer::getFrameDeltaTimeF32(void)
|
||||||
|
{
|
||||||
|
llassert(is_main_thread());
|
||||||
|
// Only the main thread writes to sFrameDeltaTime, so there is no need for locking.
|
||||||
return (F32)(U64_to_F64(sFrameDeltaTime) * USEC_TO_SEC_F64);
|
return (F32)(U64_to_F64(sFrameDeltaTime) * USEC_TO_SEC_F64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
// static
|
|
||||||
// Return seconds since the current frame started
|
// Return seconds since the current frame started
|
||||||
F32 LLFrameTimer::getCurrentFrameTime()
|
F32 LLFrameTimer::getCurrentFrameTime(void)
|
||||||
{
|
{
|
||||||
|
llassert(is_main_thread());
|
||||||
|
// Only the main thread writes to sTotalTime, so there is no need for locking.
|
||||||
U64 frame_time = totalTime() - sTotalTime;
|
U64 frame_time = totalTime() - sTotalTime;
|
||||||
return (F32)(U64_to_F64(frame_time) * USEC_TO_SEC_F64);
|
return (F32)(U64_to_F64(frame_time) * USEC_TO_SEC_F64);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Glue code to avoid full class .h file #includes
|
// Glue code to avoid full class .h file #includes
|
||||||
F32 getCurrentFrameTime()
|
F32 getCurrentFrameTime(void)
|
||||||
{
|
{
|
||||||
return (F32)(LLFrameTimer::getCurrentFrameTime());
|
return (F32)(LLFrameTimer::getCurrentFrameTime());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,88 +43,131 @@
|
|||||||
|
|
||||||
#include "lltimer.h"
|
#include "lltimer.h"
|
||||||
#include "timing.h"
|
#include "timing.h"
|
||||||
|
#include <apr_thread_mutex.h>
|
||||||
|
|
||||||
class LL_COMMON_API LLFrameTimer
|
class LL_COMMON_API LLFrameTimer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Create an LLFrameTimer and start it. After creation it is running and in the state expired (hasExpired will return true).
|
// Create an LLFrameTimer and start it. After creation it is running and in the state expired (hasExpired will return true).
|
||||||
LLFrameTimer(void) : mStartTime(sFrameTime), mExpiry(0), mRunning(true), mPaused(false) { }
|
LLFrameTimer(void) : mExpiry(0), mRunning(true), mPaused(false) { if (!sGlobalMutex) global_initialization(); setAge(0.0); }
|
||||||
|
|
||||||
|
// Atomic reads of static variables.
|
||||||
|
|
||||||
// Return the number of seconds since the start of the application.
|
// Return the number of seconds since the start of the application.
|
||||||
static F64 getElapsedSeconds()
|
static F64 getElapsedSeconds(void)
|
||||||
{
|
{
|
||||||
// Loses msec precision after ~4.5 hours...
|
// Loses msec precision after ~4.5 hours...
|
||||||
return sFrameTime;
|
apr_thread_mutex_lock(sGlobalMutex);
|
||||||
|
F64 res = sFrameTime;
|
||||||
|
apr_thread_mutex_unlock(sGlobalMutex);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a low precision usec since epoch.
|
// Return a low precision usec since epoch.
|
||||||
static U64 getTotalTime()
|
static U64 getTotalTime(void)
|
||||||
{
|
{
|
||||||
llassert(sTotalTime);
|
// sTotalTime is only accessed by the main thread, so no locking is necessary.
|
||||||
return sTotalTime;
|
llassert(is_main_thread());
|
||||||
|
//apr_thread_mutex_lock(sGlobalMutex);
|
||||||
|
U64 res = sTotalTime;
|
||||||
|
//apr_thread_mutex_unlock(sGlobalMutex);
|
||||||
|
llassert(res);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a low precision seconds since epoch.
|
// Return a low precision seconds since epoch.
|
||||||
static F64 getTotalSeconds()
|
static F64 getTotalSeconds(void)
|
||||||
{
|
{
|
||||||
return sTotalSeconds;
|
// sTotalSeconds is only accessed by the main thread, so no locking is necessary.
|
||||||
|
llassert(is_main_thread());
|
||||||
|
//apr_thread_mutex_lock(sGlobalMutex);
|
||||||
|
F64 res = sTotalSeconds;
|
||||||
|
//apr_thread_mutex_unlock(sGlobalMutex);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return current frame number (the number of frames since application start).
|
||||||
|
static U32 getFrameCount(void)
|
||||||
|
{
|
||||||
|
// sFrameCount is only accessed by the main thread, so no locking is necessary.
|
||||||
|
llassert(is_main_thread());
|
||||||
|
//apr_thread_mutex_lock(sGlobalMutex);
|
||||||
|
U32 res = sFrameCount;
|
||||||
|
//apr_thread_mutex_unlock(sGlobalMutex);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call this method once per frame to update the current frame time.
|
// Call this method once per frame to update the current frame time.
|
||||||
// This is actually called at some other times as well.
|
// This is actually called at some other times as well.
|
||||||
static void updateFrameTime();
|
static void updateFrameTime(void);
|
||||||
|
|
||||||
// Call this method once, and only once, per frame to update the current frame count and sFrameDeltaTime.
|
// Call this method once, and only once, per frame to update the current frame count and sFrameDeltaTime.
|
||||||
static void updateFrameTimeAndCount();
|
static void updateFrameTimeAndCount(void);
|
||||||
|
|
||||||
// Return current frame number (the number of frames since application start).
|
|
||||||
static U32 getFrameCount() { return sFrameCount; }
|
|
||||||
|
|
||||||
// Return duration of last frame in seconds.
|
// Return duration of last frame in seconds.
|
||||||
static F32 getFrameDeltaTimeF32();
|
static F32 getFrameDeltaTimeF32(void);
|
||||||
|
|
||||||
// Return seconds since the current frame started
|
// Return seconds since the current frame started
|
||||||
static F32 getCurrentFrameTime();
|
static F32 getCurrentFrameTime(void);
|
||||||
|
|
||||||
// MANIPULATORS
|
// MANIPULATORS
|
||||||
|
|
||||||
void reset(F32 expiration = 0.f); // Same as start() but leaves mRunning off when called after stop().
|
void reset(F32 expiration = 0.f) // Same as start() but leaves mRunning off when called after stop().
|
||||||
void start(F32 expiration = 0.f); // Reset and (re)start with expiration.
|
{
|
||||||
void stop(); // Stop running.
|
llassert(!mPaused);
|
||||||
|
mStartTime = getElapsedSeconds();
|
||||||
|
mExpiry = mStartTime + expiration;
|
||||||
|
}
|
||||||
|
|
||||||
|
void start(F32 expiration = 0.f) // Reset and (re)start with expiration.
|
||||||
|
{
|
||||||
|
reset(expiration);
|
||||||
|
mRunning = true; // Start, if not already started.
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop(void) // Stop running.
|
||||||
|
{
|
||||||
|
llassert(!mPaused);
|
||||||
|
mRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
void pause(); // Mark elapsed time so far.
|
void pause(); // Mark elapsed time so far.
|
||||||
void unpause(); // Move 'start' time in order to decrement time between pause and unpause from ElapsedTime.
|
void unpause(); // Move 'start' time in order to decrement time between pause and unpause from ElapsedTime.
|
||||||
|
|
||||||
void setTimerExpirySec(F32 expiration);
|
void setTimerExpirySec(F32 expiration) { llassert(!mPaused); mExpiry = mStartTime + expiration; }
|
||||||
|
|
||||||
void setExpiryAt(F64 seconds_since_epoch);
|
void setExpiryAt(F64 seconds_since_epoch);
|
||||||
bool checkExpirationAndReset(F32 expiration); // Returns true when expired. Only resets if expired.
|
bool checkExpirationAndReset(F32 expiration); // Returns true when expired. Only resets if expired.
|
||||||
F32 getElapsedTimeAndResetF32() { F32 t = getElapsedTimeF32(); reset(); return t; }
|
F32 getElapsedTimeAndResetF32(void);
|
||||||
void setAge(const F64 age) { llassert(!mPaused); mStartTime = sFrameTime - age; }
|
void setAge(const F64 age) { llassert(!mPaused); mStartTime = getElapsedSeconds() - age; }
|
||||||
|
|
||||||
// ACCESSORS
|
// ACCESSORS
|
||||||
bool hasExpired() const { return sFrameTime >= mExpiry; }
|
bool hasExpired() const { return getElapsedSeconds() >= mExpiry; }
|
||||||
F32 getElapsedTimeF32() const { llassert(mRunning); return mPaused ? (F32)mStartTime : (F32)(sFrameTime - mStartTime); }
|
F32 getElapsedTimeF32() const { llassert(mRunning); return mPaused ? (F32)mStartTime : (F32)(getElapsedSeconds() - mStartTime); }
|
||||||
bool getStarted() const { return mRunning; }
|
bool getStarted() const { return mRunning; }
|
||||||
|
|
||||||
// return the seconds since epoch when this timer will expire.
|
// return the seconds since epoch when this timer will expire.
|
||||||
F64 expiresAt() const;
|
F64 expiresAt() const;
|
||||||
|
|
||||||
protected:
|
public:
|
||||||
// A single, high resolution timer that drives all LLFrameTimers
|
// Do one-time initialization of the static members.
|
||||||
// *NOTE: no longer used.
|
static void global_initialization(void);
|
||||||
//static LLTimer sInternalTimer;
|
|
||||||
|
|
||||||
|
protected:
|
||||||
//
|
//
|
||||||
// Aplication constants
|
// Application constants
|
||||||
//
|
//
|
||||||
|
|
||||||
// Application start in microseconds since epoch.
|
// Application start in microseconds since epoch.
|
||||||
static U64 const sStartTotalTime;
|
static U64 const sStartTotalTime;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Data updated per frame
|
// Global data.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// More than one thread are accessing (some of) these variables, therefore we need locking.
|
||||||
|
static apr_thread_mutex_t* sGlobalMutex;
|
||||||
|
|
||||||
// Current time in seconds since application start, updated together with sTotalTime.
|
// Current time in seconds since application start, updated together with sTotalTime.
|
||||||
static F64 sFrameTime;
|
static F64 sFrameTime;
|
||||||
|
|
||||||
|
|||||||
@@ -194,7 +194,12 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
LLInstanceTracker(KEY key) { add_(key); }
|
LLInstanceTracker(KEY key)
|
||||||
|
{
|
||||||
|
// make sure static data outlives all instances
|
||||||
|
getStatic();
|
||||||
|
add_(key);
|
||||||
|
}
|
||||||
virtual ~LLInstanceTracker()
|
virtual ~LLInstanceTracker()
|
||||||
{
|
{
|
||||||
// it's unsafe to delete instances of this type while all instances are being iterated over.
|
// it's unsafe to delete instances of this type while all instances are being iterated over.
|
||||||
@@ -282,7 +287,8 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
LLInstanceTracker()
|
LLInstanceTracker()
|
||||||
{
|
{
|
||||||
// it's safe but unpredictable to create instances of this type while all instances are being iterated over. I hate unpredictable. This assert will probably be turned on early in the next development cycle.
|
// make sure static data outlives all instances
|
||||||
|
getStatic();
|
||||||
getSet_().insert(static_cast<T*>(this));
|
getSet_().insert(static_cast<T*>(this));
|
||||||
}
|
}
|
||||||
virtual ~LLInstanceTracker()
|
virtual ~LLInstanceTracker()
|
||||||
|
|||||||
@@ -134,4 +134,7 @@ private:
|
|||||||
LL_COMMON_API bool operator==(const LLMD5& a, const LLMD5& b);
|
LL_COMMON_API bool operator==(const LLMD5& a, const LLMD5& b);
|
||||||
LL_COMMON_API bool operator!=(const LLMD5& a, const LLMD5& b);
|
LL_COMMON_API bool operator!=(const LLMD5& a, const LLMD5& b);
|
||||||
|
|
||||||
|
LL_COMMON_API bool operator==(const LLMD5& a, const LLMD5& b);
|
||||||
|
LL_COMMON_API bool operator!=(const LLMD5& a, const LLMD5& b);
|
||||||
|
|
||||||
#endif // LL_LLMD5_H
|
#endif // LL_LLMD5_H
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -40,9 +40,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "llerror.h"
|
#include "llerror.h"
|
||||||
|
#include "llmemtype.h"
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#if LL_DEBUG
|
#if LL_DEBUG
|
||||||
inline void* ll_aligned_malloc( size_t size, int align )
|
inline void* ll_aligned_malloc( size_t size, int align )
|
||||||
{
|
{
|
||||||
@@ -120,6 +118,10 @@ inline void ll_aligned_free_32(void *p)
|
|||||||
#define ll_aligned_free_32 free
|
#define ll_aligned_free_32 free
|
||||||
#endif // LL_DEBUG
|
#endif // LL_DEBUG
|
||||||
|
|
||||||
|
#ifndef __DEBUG_PRIVATE_MEM__
|
||||||
|
#define __DEBUG_PRIVATE_MEM__ 0
|
||||||
|
#endif
|
||||||
|
|
||||||
class LL_COMMON_API LLMemory
|
class LL_COMMON_API LLMemory
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -130,10 +132,395 @@ public:
|
|||||||
// Return value is zero if not known.
|
// Return value is zero if not known.
|
||||||
static U64 getCurrentRSS();
|
static U64 getCurrentRSS();
|
||||||
static U32 getWorkingSetSize();
|
static U32 getWorkingSetSize();
|
||||||
|
static void* tryToAlloc(void* address, U32 size);
|
||||||
|
static void initMaxHeapSizeGB(F32 max_heap_size_gb, BOOL prevent_heap_failure);
|
||||||
|
static void updateMemoryInfo() ;
|
||||||
|
static void logMemoryInfo(BOOL update = FALSE);
|
||||||
|
static bool isMemoryPoolLow();
|
||||||
|
|
||||||
|
static U32 getAvailableMemKB() ;
|
||||||
|
static U32 getMaxMemKB() ;
|
||||||
|
static U32 getAllocatedMemKB() ;
|
||||||
private:
|
private:
|
||||||
static char* reserveMem;
|
static char* reserveMem;
|
||||||
|
static U32 sAvailPhysicalMemInKB ;
|
||||||
|
static U32 sMaxPhysicalMemInKB ;
|
||||||
|
static U32 sAllocatedMemInKB;
|
||||||
|
static U32 sAllocatedPageSizeInKB ;
|
||||||
|
|
||||||
|
static U32 sMaxHeapSizeInKB;
|
||||||
|
static BOOL sEnableMemoryFailurePrevention;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
class LLMutex ;
|
||||||
|
#if MEM_TRACK_MEM
|
||||||
|
class LL_COMMON_API LLMemTracker
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
LLMemTracker() ;
|
||||||
|
~LLMemTracker() ;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void release() ;
|
||||||
|
static LLMemTracker* getInstance() ;
|
||||||
|
|
||||||
|
void track(const char* function, const int line) ;
|
||||||
|
void preDraw(BOOL pause) ;
|
||||||
|
void postDraw() ;
|
||||||
|
const char* getNextLine() ;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static LLMemTracker* sInstance ;
|
||||||
|
|
||||||
|
char** mStringBuffer ;
|
||||||
|
S32 mCapacity ;
|
||||||
|
U32 mLastAllocatedMem ;
|
||||||
|
S32 mCurIndex ;
|
||||||
|
S32 mCounter;
|
||||||
|
S32 mDrawnIndex;
|
||||||
|
S32 mNumOfDrawn;
|
||||||
|
BOOL mPaused;
|
||||||
|
LLMutex* mMutexp ;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MEM_TRACK_RELEASE LLMemTracker::release() ;
|
||||||
|
#define MEM_TRACK LLMemTracker::getInstance()->track(__FUNCTION__, __LINE__) ;
|
||||||
|
|
||||||
|
#else // MEM_TRACK_MEM
|
||||||
|
|
||||||
|
#define MEM_TRACK_RELEASE
|
||||||
|
#define MEM_TRACK
|
||||||
|
|
||||||
|
#endif // MEM_TRACK_MEM
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
//class LLPrivateMemoryPool defines a private memory pool for an application to use, so the application does not
|
||||||
|
//need to access the heap directly fro each memory allocation. Throught this, the allocation speed is faster,
|
||||||
|
//and reduces virtaul address space gragmentation problem.
|
||||||
|
//Note: this class is thread-safe by passing true to the constructor function. However, you do not need to do this unless
|
||||||
|
//you are sure the memory allocation and de-allocation will happen in different threads. To make the pool thread safe
|
||||||
|
//increases allocation and deallocation cost.
|
||||||
|
//
|
||||||
|
class LL_COMMON_API LLPrivateMemoryPool
|
||||||
|
{
|
||||||
|
friend class LLPrivateMemoryPoolManager ;
|
||||||
|
|
||||||
|
public:
|
||||||
|
class LL_COMMON_API LLMemoryBlock //each block is devided into slots uniformly
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LLMemoryBlock() ;
|
||||||
|
~LLMemoryBlock() ;
|
||||||
|
|
||||||
|
void init(char* buffer, U32 buffer_size, U32 slot_size) ;
|
||||||
|
void setBuffer(char* buffer, U32 buffer_size) ;
|
||||||
|
|
||||||
|
char* allocate() ;
|
||||||
|
void freeMem(void* addr) ;
|
||||||
|
|
||||||
|
bool empty() {return !mAllocatedSlots;}
|
||||||
|
bool isFull() {return mAllocatedSlots == mTotalSlots;}
|
||||||
|
bool isFree() {return !mTotalSlots;}
|
||||||
|
|
||||||
|
U32 getSlotSize()const {return mSlotSize;}
|
||||||
|
U32 getTotalSlots()const {return mTotalSlots;}
|
||||||
|
U32 getBufferSize()const {return mBufferSize;}
|
||||||
|
char* getBuffer() const {return mBuffer;}
|
||||||
|
|
||||||
|
//debug use
|
||||||
|
void resetBitMap() ;
|
||||||
|
private:
|
||||||
|
char* mBuffer;
|
||||||
|
U32 mSlotSize ; //when the block is not initialized, it is the buffer size.
|
||||||
|
U32 mBufferSize ;
|
||||||
|
U32 mUsageBits ;
|
||||||
|
U8 mTotalSlots ;
|
||||||
|
U8 mAllocatedSlots ;
|
||||||
|
U8 mDummySize ; //size of extra bytes reserved for mUsageBits.
|
||||||
|
|
||||||
|
public:
|
||||||
|
LLMemoryBlock* mPrev ;
|
||||||
|
LLMemoryBlock* mNext ;
|
||||||
|
LLMemoryBlock* mSelf ;
|
||||||
|
|
||||||
|
struct CompareAddress
|
||||||
|
{
|
||||||
|
bool operator()(const LLMemoryBlock* const& lhs, const LLMemoryBlock* const& rhs)
|
||||||
|
{
|
||||||
|
return (size_t)lhs->getBuffer() < (size_t)rhs->getBuffer();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
class LL_COMMON_API LLMemoryChunk //is divided into memory blocks.
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LLMemoryChunk() ;
|
||||||
|
~LLMemoryChunk() ;
|
||||||
|
|
||||||
|
void init(char* buffer, U32 buffer_size, U32 min_slot_size, U32 max_slot_size, U32 min_block_size, U32 max_block_size) ;
|
||||||
|
void setBuffer(char* buffer, U32 buffer_size) ;
|
||||||
|
|
||||||
|
bool empty() ;
|
||||||
|
|
||||||
|
char* allocate(U32 size) ;
|
||||||
|
void freeMem(void* addr) ;
|
||||||
|
|
||||||
|
char* getBuffer() const {return mBuffer;}
|
||||||
|
U32 getBufferSize() const {return mBufferSize;}
|
||||||
|
U32 getAllocatedSize() const {return mAlloatedSize;}
|
||||||
|
|
||||||
|
bool containsAddress(const char* addr) const;
|
||||||
|
|
||||||
|
static U32 getMaxOverhead(U32 data_buffer_size, U32 min_slot_size,
|
||||||
|
U32 max_slot_size, U32 min_block_size, U32 max_block_size) ;
|
||||||
|
|
||||||
|
void dump() ;
|
||||||
|
|
||||||
|
private:
|
||||||
|
U32 getPageIndex(char const* addr) ;
|
||||||
|
U32 getBlockLevel(U32 size) ;
|
||||||
|
U16 getPageLevel(U32 size) ;
|
||||||
|
LLMemoryBlock* addBlock(U32 blk_idx) ;
|
||||||
|
void popAvailBlockList(U32 blk_idx) ;
|
||||||
|
void addToFreeSpace(LLMemoryBlock* blk) ;
|
||||||
|
void removeFromFreeSpace(LLMemoryBlock* blk) ;
|
||||||
|
void removeBlock(LLMemoryBlock* blk) ;
|
||||||
|
void addToAvailBlockList(LLMemoryBlock* blk) ;
|
||||||
|
U32 calcBlockSize(U32 slot_size);
|
||||||
|
LLMemoryBlock* createNewBlock(LLMemoryBlock* blk, U32 buffer_size, U32 slot_size, U32 blk_idx) ;
|
||||||
|
|
||||||
|
private:
|
||||||
|
LLMemoryBlock** mAvailBlockList ;//256 by mMinSlotSize
|
||||||
|
LLMemoryBlock** mFreeSpaceList;
|
||||||
|
LLMemoryBlock* mBlocks ; //index of blocks by address.
|
||||||
|
|
||||||
|
char* mBuffer ;
|
||||||
|
U32 mBufferSize ;
|
||||||
|
char* mDataBuffer ;
|
||||||
|
char* mMetaBuffer ;
|
||||||
|
U32 mMinBlockSize ;
|
||||||
|
U32 mMinSlotSize ;
|
||||||
|
U32 mMaxSlotSize ;
|
||||||
|
U32 mAlloatedSize ;
|
||||||
|
U16 mBlockLevels;
|
||||||
|
U16 mPartitionLevels;
|
||||||
|
|
||||||
|
public:
|
||||||
|
//form a linked list
|
||||||
|
LLMemoryChunk* mNext ;
|
||||||
|
LLMemoryChunk* mPrev ;
|
||||||
|
} ;
|
||||||
|
|
||||||
|
private:
|
||||||
|
LLPrivateMemoryPool(S32 type, U32 max_pool_size) ;
|
||||||
|
~LLPrivateMemoryPool() ;
|
||||||
|
|
||||||
|
char *allocate(U32 size) ;
|
||||||
|
void freeMem(void* addr) ;
|
||||||
|
|
||||||
|
void dump() ;
|
||||||
|
U32 getTotalAllocatedSize() ;
|
||||||
|
U32 getTotalReservedSize() {return mReservedPoolSize;}
|
||||||
|
S32 getType() const {return mType; }
|
||||||
|
bool isEmpty() const {return !mNumOfChunks; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void lock() ;
|
||||||
|
void unlock() ;
|
||||||
|
S32 getChunkIndex(U32 size) ;
|
||||||
|
LLMemoryChunk* addChunk(S32 chunk_index) ;
|
||||||
|
bool checkSize(U32 asked_size) ;
|
||||||
|
void removeChunk(LLMemoryChunk* chunk) ;
|
||||||
|
U16 findHashKey(const char* addr);
|
||||||
|
void addToHashTable(LLMemoryChunk* chunk) ;
|
||||||
|
void removeFromHashTable(LLMemoryChunk* chunk) ;
|
||||||
|
void rehash() ;
|
||||||
|
bool fillHashTable(U16 start, U16 end, LLMemoryChunk* chunk) ;
|
||||||
|
LLMemoryChunk* findChunk(const char* addr) ;
|
||||||
|
|
||||||
|
void destroyPool() ;
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
SMALL_ALLOCATION = 0, //from 8 bytes to 2KB(exclusive), page size 2KB, max chunk size is 4MB.
|
||||||
|
MEDIUM_ALLOCATION, //from 2KB to 512KB(exclusive), page size 32KB, max chunk size 4MB
|
||||||
|
LARGE_ALLOCATION, //from 512KB to 4MB(inclusive), page size 64KB, max chunk size 16MB
|
||||||
|
SUPER_ALLOCATION //allocation larger than 4MB.
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
STATIC = 0 , //static pool(each alllocation stays for a long time) without threading support
|
||||||
|
VOLATILE, //Volatile pool(each allocation stays for a very short time) without threading support
|
||||||
|
STATIC_THREADED, //static pool with threading support
|
||||||
|
VOLATILE_THREADED, //volatile pool with threading support
|
||||||
|
MAX_TYPES
|
||||||
|
}; //pool types
|
||||||
|
|
||||||
|
private:
|
||||||
|
LLMutex* mMutexp ;
|
||||||
|
U32 mMaxPoolSize;
|
||||||
|
U32 mReservedPoolSize ;
|
||||||
|
|
||||||
|
LLMemoryChunk* mChunkList[SUPER_ALLOCATION] ; //all memory chunks reserved by this pool, sorted by address
|
||||||
|
U16 mNumOfChunks ;
|
||||||
|
U16 mHashFactor ;
|
||||||
|
|
||||||
|
S32 mType ;
|
||||||
|
|
||||||
|
class LLChunkHashElement
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LLChunkHashElement() {mFirst = NULL ; mSecond = NULL ;}
|
||||||
|
|
||||||
|
bool add(LLMemoryChunk* chunk) ;
|
||||||
|
void remove(LLMemoryChunk* chunk) ;
|
||||||
|
LLMemoryChunk* findChunk(const char* addr) ;
|
||||||
|
|
||||||
|
bool empty() {return !mFirst && !mSecond; }
|
||||||
|
bool full() {return mFirst && mSecond; }
|
||||||
|
bool hasElement(LLMemoryChunk* chunk) {return mFirst == chunk || mSecond == chunk;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
LLMemoryChunk* mFirst ;
|
||||||
|
LLMemoryChunk* mSecond ;
|
||||||
|
};
|
||||||
|
std::vector<LLChunkHashElement> mChunkHashList ;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LL_COMMON_API LLPrivateMemoryPoolManager
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
LLPrivateMemoryPoolManager(BOOL enabled, U32 max_pool_size) ;
|
||||||
|
~LLPrivateMemoryPoolManager() ;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static LLPrivateMemoryPoolManager* getInstance() ;
|
||||||
|
static void initClass(BOOL enabled, U32 pool_size) ;
|
||||||
|
static void destroyClass() ;
|
||||||
|
|
||||||
|
LLPrivateMemoryPool* newPool(S32 type) ;
|
||||||
|
void deletePool(LLPrivateMemoryPool* pool) ;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<LLPrivateMemoryPool*> mPoolList ;
|
||||||
|
U32 mMaxPrivatePoolSize;
|
||||||
|
|
||||||
|
static LLPrivateMemoryPoolManager* sInstance ;
|
||||||
|
static BOOL sPrivatePoolEnabled;
|
||||||
|
static std::vector<LLPrivateMemoryPool*> sDanglingPoolList ;
|
||||||
|
public:
|
||||||
|
//debug and statistics info.
|
||||||
|
void updateStatistics() ;
|
||||||
|
|
||||||
|
U32 mTotalReservedSize ;
|
||||||
|
U32 mTotalAllocatedSize ;
|
||||||
|
|
||||||
|
public:
|
||||||
|
#if __DEBUG_PRIVATE_MEM__
|
||||||
|
static char* allocate(LLPrivateMemoryPool* poolp, U32 size, const char* function, const int line) ;
|
||||||
|
|
||||||
|
typedef std::map<char*, std::string> mem_allocation_info_t ;
|
||||||
|
static mem_allocation_info_t sMemAllocationTracker;
|
||||||
|
#else
|
||||||
|
static char* allocate(LLPrivateMemoryPool* poolp, U32 size) ;
|
||||||
|
#endif
|
||||||
|
static void freeMem(LLPrivateMemoryPool* poolp, void* addr) ;
|
||||||
|
};
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------------------
|
||||||
|
#if __DEBUG_PRIVATE_MEM__
|
||||||
|
#define ALLOCATE_MEM(poolp, size) LLPrivateMemoryPoolManager::allocate((poolp), (size), __FUNCTION__, __LINE__)
|
||||||
|
#else
|
||||||
|
#define ALLOCATE_MEM(poolp, size) LLPrivateMemoryPoolManager::allocate((poolp), (size))
|
||||||
|
//#define ALLOCATE_MEM(poolp, size) new char[size]
|
||||||
|
#endif
|
||||||
|
#define FREE_MEM(poolp, addr) LLPrivateMemoryPoolManager::freeMem((poolp), (addr))
|
||||||
|
//#define FREE_MEM(poolp, addr) delete[] addr;
|
||||||
|
//-------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//
|
||||||
|
//the below singleton is used to test the private memory pool.
|
||||||
|
//
|
||||||
|
#if 0
|
||||||
|
class LL_COMMON_API LLPrivateMemoryPoolTester
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
LLPrivateMemoryPoolTester() ;
|
||||||
|
~LLPrivateMemoryPoolTester() ;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static LLPrivateMemoryPoolTester* getInstance() ;
|
||||||
|
static void destroy() ;
|
||||||
|
|
||||||
|
void run(S32 type) ;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void correctnessTest() ;
|
||||||
|
void performanceTest() ;
|
||||||
|
void fragmentationtest() ;
|
||||||
|
|
||||||
|
void test(U32 min_size, U32 max_size, U32 stride, U32 times, bool random_deletion, bool output_statistics) ;
|
||||||
|
void testAndTime(U32 size, U32 times) ;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
public:
|
||||||
|
void* operator new(size_t size)
|
||||||
|
{
|
||||||
|
return (void*)sPool->allocate(size) ;
|
||||||
|
}
|
||||||
|
void operator delete(void* addr)
|
||||||
|
{
|
||||||
|
sPool->freeMem(addr) ;
|
||||||
|
}
|
||||||
|
void* operator new[](size_t size)
|
||||||
|
{
|
||||||
|
return (void*)sPool->allocate(size) ;
|
||||||
|
}
|
||||||
|
void operator delete[](void* addr)
|
||||||
|
{
|
||||||
|
sPool->freeMem(addr) ;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
static LLPrivateMemoryPoolTester* sInstance;
|
||||||
|
static LLPrivateMemoryPool* sPool ;
|
||||||
|
static LLPrivateMemoryPool* sThreadedPool ;
|
||||||
|
};
|
||||||
|
#if 0
|
||||||
|
//static
|
||||||
|
void* LLPrivateMemoryPoolTester::operator new(size_t size)
|
||||||
|
{
|
||||||
|
return (void*)sPool->allocate(size) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
//static
|
||||||
|
void LLPrivateMemoryPoolTester::operator delete(void* addr)
|
||||||
|
{
|
||||||
|
sPool->free(addr) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
//static
|
||||||
|
void* LLPrivateMemoryPoolTester::operator new[](size_t size)
|
||||||
|
{
|
||||||
|
return (void*)sPool->allocate(size) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
//static
|
||||||
|
void LLPrivateMemoryPoolTester::operator delete[](void* addr)
|
||||||
|
{
|
||||||
|
sPool->free(addr) ;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
//EVENTUALLY REMOVE THESE:
|
//EVENTUALLY REMOVE THESE:
|
||||||
#include "llpointer.h"
|
#include "llpointer.h"
|
||||||
#include "llrefcount.h"
|
#include "llrefcount.h"
|
||||||
|
|||||||
232
indra/llcommon/llmemtype.cpp
Normal file
232
indra/llcommon/llmemtype.cpp
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
/**
|
||||||
|
* @file llmemtype.cpp
|
||||||
|
* @brief Simple memory allocation/deallocation tracking stuff here
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation;
|
||||||
|
* version 2.1 of the License only.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "llmemtype.h"
|
||||||
|
#include "llallocator.h"
|
||||||
|
|
||||||
|
std::vector<char const *> LLMemType::DeclareMemType::mNameList;
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_INIT("Init");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_STARTUP("Startup");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_MAIN("Main");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_FRAME("Frame");
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_GATHER_INPUT("GatherInput");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_JOY_KEY("JoyKey");
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_IDLE("Idle");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_PUMP("IdlePump");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_NETWORK("IdleNetwork");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_UPDATE_REGIONS("IdleUpdateRegions");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_UPDATE_VIEWER_REGION("IdleUpdateViewerRegion");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_UPDATE_SURFACE("IdleUpdateSurface");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_UPDATE_PARCEL_OVERLAY("IdleUpdateParcelOverlay");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_AUDIO("IdleAudio");
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_CACHE_PROCESS_PENDING("CacheProcessPending");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_CACHE_PROCESS_PENDING_ASKS("CacheProcessPendingAsks");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_CACHE_PROCESS_PENDING_REPLIES("CacheProcessPendingReplies");
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_MESSAGE_CHECK_ALL("MessageCheckAll");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_MESSAGE_PROCESS_ACKS("MessageProcessAcks");
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_RENDER("Render");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_SLEEP("Sleep");
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_NETWORK("Network");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PHYSICS("Physics");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_INTERESTLIST("InterestList");
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_IMAGEBASE("ImageBase");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_IMAGERAW("ImageRaw");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_IMAGEFORMATTED("ImageFormatted");
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_APPFMTIMAGE("AppFmtImage");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_APPRAWIMAGE("AppRawImage");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_APPAUXRAWIMAGE("AppAuxRawImage");
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_DRAWABLE("Drawable");
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_OBJECT("Object");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_OBJECT_PROCESS_UPDATE("ObjectProcessUpdate");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_OBJECT_PROCESS_UPDATE_CORE("ObjectProcessUpdateCore");
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY("Display");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_UPDATE("DisplayUpdate");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_UPDATE_CAMERA("DisplayUpdateCam");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_UPDATE_GEOM("DisplayUpdateGeom");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_SWAP("DisplaySwap");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_UPDATE_HUD("DisplayUpdateHud");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_GEN_REFLECTION("DisplayGenRefl");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_IMAGE_UPDATE("DisplayImageUpdate");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_STATE_SORT("DisplayStateSort");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_SKY("DisplaySky");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_RENDER_GEOM("DisplayRenderGeom");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_RENDER_FLUSH("DisplayRenderFlush");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_RENDER_UI("DisplayRenderUI");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_RENDER_ATTACHMENTS("DisplayRenderAttach");
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_DATA("VertexData");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_CONSTRUCTOR("VertexConstr");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_DESTRUCTOR("VertexDestr");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_CREATE_VERTICES("VertexCreateVerts");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_CREATE_INDICES("VertexCreateIndices");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_DESTROY_BUFFER("VertexDestroyBuff");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_DESTROY_INDICES("VertexDestroyIndices");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_UPDATE_VERTS("VertexUpdateVerts");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_UPDATE_INDICES("VertexUpdateIndices");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_ALLOCATE_BUFFER("VertexAllocateBuffer");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_RESIZE_BUFFER("VertexResizeBuffer");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_MAP_BUFFER("VertexMapBuffer");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_MAP_BUFFER_VERTICES("VertexMapBufferVerts");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_MAP_BUFFER_INDICES("VertexMapBufferIndices");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_UNMAP_BUFFER("VertexUnmapBuffer");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_SET_STRIDE("VertexSetStride");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_SET_BUFFER("VertexSetBuffer");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_SETUP_VERTEX_BUFFER("VertexSetupVertBuff");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_CLEANUP_CLASS("VertexCleanupClass");
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_SPACE_PARTITION("SpacePartition");
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE("Pipeline");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_INIT("PipelineInit");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_CREATE_BUFFERS("PipelineCreateBuffs");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RESTORE_GL("PipelineRestroGL");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_UNLOAD_SHADERS("PipelineUnloadShaders");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_LIGHTING_DETAIL("PipelineLightingDetail");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_GET_POOL_TYPE("PipelineGetPoolType");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_ADD_POOL("PipelineAddPool");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_ALLOCATE_DRAWABLE("PipelineAllocDrawable");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_ADD_OBJECT("PipelineAddObj");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_CREATE_OBJECTS("PipelineCreateObjs");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_UPDATE_MOVE("PipelineUpdateMove");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_UPDATE_GEOM("PipelineUpdateGeom");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_VISIBLE("PipelineMarkVisible");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_MOVED("PipelineMarkMoved");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_SHIFT("PipelineMarkShift");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_SHIFT_OBJECTS("PipelineShiftObjs");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_TEXTURED("PipelineMarkTextured");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_REBUILD("PipelineMarkRebuild");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_UPDATE_CULL("PipelineUpdateCull");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_STATE_SORT("PipelineStateSort");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_POST_SORT("PipelinePostSort");
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_HUD_ELS("PipelineHudEls");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_HL("PipelineRenderHL");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_GEOM("PipelineRenderGeom");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_GEOM_DEFFERRED("PipelineRenderGeomDef");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_GEOM_POST_DEF("PipelineRenderGeomPostDef");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_GEOM_SHADOW("PipelineRenderGeomShadow");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_SELECT("PipelineRenderSelect");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_REBUILD_POOLS("PipelineRebuildPools");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_QUICK_LOOKUP("PipelineQuickLookup");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_OBJECTS("PipelineRenderObjs");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_GENERATE_IMPOSTOR("PipelineGenImpostors");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_BLOOM("PipelineRenderBloom");
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_UPKEEP_POOLS("UpkeepPools");
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_AVATAR("Avatar");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_AVATAR_MESH("AvatarMesh");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PARTICLES("Particles");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_REGIONS("Regions");
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY("Inventory");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_DRAW("InventoryDraw");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_BUILD_NEW_VIEWS("InventoryBuildNewViews");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_DO_FOLDER("InventoryDoFolder");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_POST_BUILD("InventoryPostBuild");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_FROM_XML("InventoryFromXML");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_CREATE_NEW_ITEM("InventoryCreateNewItem");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_VIEW_INIT("InventoryViewInit");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_VIEW_SHOW("InventoryViewShow");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_VIEW_TOGGLE("InventoryViewToggle");
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_ANIMATION("Animation");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_VOLUME("Volume");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_PRIMITIVE("Primitive");
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_SCRIPT("Script");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_SCRIPT_RUN("ScriptRun");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_SCRIPT_BYTECODE("ScriptByteCode");
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_IO_PUMP("IoPump");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_IO_TCP("IoTCP");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_IO_BUFFER("IoBuffer");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_IO_HTTP_SERVER("IoHttpServer");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_IO_SD_SERVER("IoSDServer");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_IO_SD_CLIENT("IoSDClient");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_IO_URL_REQUEST("IOUrlRequest");
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_DIRECTX_INIT("DirectXInit");
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP1("Temp1");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP2("Temp2");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP3("Temp3");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP4("Temp4");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP5("Temp5");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP6("Temp6");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP7("Temp7");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP8("Temp8");
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_TEMP9("Temp9");
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType LLMemType::MTYPE_OTHER("Other");
|
||||||
|
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType::DeclareMemType(char const * st)
|
||||||
|
{
|
||||||
|
mID = (S32)mNameList.size();
|
||||||
|
mName = st;
|
||||||
|
|
||||||
|
mNameList.push_back(mName);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLMemType::DeclareMemType::~DeclareMemType()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LLMemType::LLMemType(LLMemType::DeclareMemType& dt)
|
||||||
|
{
|
||||||
|
mTypeIndex = dt.mID;
|
||||||
|
LLAllocator::pushMemType(dt.mID);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLMemType::~LLMemType()
|
||||||
|
{
|
||||||
|
LLAllocator::popMemType();
|
||||||
|
}
|
||||||
|
|
||||||
|
char const * LLMemType::getNameFromID(S32 id)
|
||||||
|
{
|
||||||
|
if (id < 0 || id >= (S32)DeclareMemType::mNameList.size())
|
||||||
|
{
|
||||||
|
return "INVALID";
|
||||||
|
}
|
||||||
|
|
||||||
|
return DeclareMemType::mNameList[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
@@ -36,128 +36,210 @@
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
class LLMemType;
|
|
||||||
|
|
||||||
extern void* ll_allocate (size_t size);
|
|
||||||
extern void ll_release (void *p);
|
|
||||||
|
|
||||||
#define MEM_TRACK_MEM 0
|
|
||||||
#define MEM_TRACK_TYPE (1 && MEM_TRACK_MEM)
|
|
||||||
|
|
||||||
#if MEM_TRACK_TYPE
|
|
||||||
#define MEM_DUMP_DATA 1
|
|
||||||
#define MEM_TYPE_NEW(T) \
|
|
||||||
static void* operator new(size_t s) { LLMemType mt(T); return ll_allocate(s); } \
|
|
||||||
static void operator delete(void* p) { ll_release(p); }
|
|
||||||
|
|
||||||
#else
|
|
||||||
#define MEM_TYPE_NEW(T)
|
|
||||||
#endif // MEM_TRACK_TYPE
|
|
||||||
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "linden_common.h"
|
||||||
|
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
// WARNING: Never commit with MEM_TRACK_MEM == 1
|
||||||
|
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
#define MEM_TRACK_MEM (0 && LL_WINDOWS)
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#define MEM_TYPE_NEW(T)
|
||||||
|
|
||||||
class LL_COMMON_API LLMemType
|
class LL_COMMON_API LLMemType
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Also update sTypeDesc in llmemory.cpp
|
|
||||||
enum EMemType
|
// class we'll initialize all instances of as
|
||||||
|
// static members of MemType. Then use
|
||||||
|
// to construct any new mem type.
|
||||||
|
class LL_COMMON_API DeclareMemType
|
||||||
{
|
{
|
||||||
MTYPE_INIT,
|
public:
|
||||||
MTYPE_STARTUP,
|
DeclareMemType(char const * st);
|
||||||
MTYPE_MAIN,
|
~DeclareMemType();
|
||||||
|
|
||||||
MTYPE_IMAGEBASE,
|
S32 mID;
|
||||||
MTYPE_IMAGERAW,
|
char const * mName;
|
||||||
MTYPE_IMAGEFORMATTED,
|
|
||||||
|
|
||||||
MTYPE_APPFMTIMAGE,
|
// array so we can map an index ID to Name
|
||||||
MTYPE_APPRAWIMAGE,
|
static std::vector<char const *> mNameList;
|
||||||
MTYPE_APPAUXRAWIMAGE,
|
|
||||||
|
|
||||||
MTYPE_DRAWABLE,
|
|
||||||
MTYPE_OBJECT,
|
|
||||||
MTYPE_VERTEX_DATA,
|
|
||||||
MTYPE_SPACE_PARTITION,
|
|
||||||
MTYPE_PIPELINE,
|
|
||||||
MTYPE_AVATAR,
|
|
||||||
MTYPE_AVATAR_MESH,
|
|
||||||
MTYPE_PARTICLES,
|
|
||||||
MTYPE_REGIONS,
|
|
||||||
MTYPE_INVENTORY,
|
|
||||||
MTYPE_ANIMATION,
|
|
||||||
MTYPE_VOLUME,
|
|
||||||
MTYPE_PRIMITIVE,
|
|
||||||
|
|
||||||
MTYPE_NETWORK,
|
|
||||||
MTYPE_PHYSICS,
|
|
||||||
MTYPE_INTERESTLIST,
|
|
||||||
|
|
||||||
MTYPE_SCRIPT,
|
|
||||||
MTYPE_SCRIPT_RUN,
|
|
||||||
MTYPE_SCRIPT_BYTECODE,
|
|
||||||
|
|
||||||
MTYPE_IO_PUMP,
|
|
||||||
MTYPE_IO_TCP,
|
|
||||||
MTYPE_IO_BUFFER,
|
|
||||||
MTYPE_IO_HTTP_SERVER,
|
|
||||||
MTYPE_IO_SD_SERVER,
|
|
||||||
MTYPE_IO_SD_CLIENT,
|
|
||||||
MTYPE_IO_URL_REQUEST,
|
|
||||||
|
|
||||||
MTYPE_TEMP1,
|
|
||||||
MTYPE_TEMP2,
|
|
||||||
MTYPE_TEMP3,
|
|
||||||
MTYPE_TEMP4,
|
|
||||||
MTYPE_TEMP5,
|
|
||||||
MTYPE_TEMP6,
|
|
||||||
MTYPE_TEMP7,
|
|
||||||
MTYPE_TEMP8,
|
|
||||||
MTYPE_TEMP9,
|
|
||||||
|
|
||||||
MTYPE_OTHER, // Special, used by display code
|
|
||||||
|
|
||||||
MTYPE_NUM_TYPES
|
|
||||||
};
|
};
|
||||||
enum { MTYPE_MAX_DEPTH = 64 };
|
|
||||||
|
|
||||||
public:
|
LLMemType(DeclareMemType& dt);
|
||||||
LLMemType(EMemType type)
|
~LLMemType();
|
||||||
{
|
|
||||||
#if MEM_TRACK_TYPE
|
|
||||||
if (type < 0 || type >= MTYPE_NUM_TYPES)
|
|
||||||
llerrs << "LLMemType error" << llendl;
|
|
||||||
if (sCurDepth < 0 || sCurDepth >= MTYPE_MAX_DEPTH)
|
|
||||||
llerrs << "LLMemType error" << llendl;
|
|
||||||
sType[sCurDepth] = sCurType;
|
|
||||||
sCurDepth++;
|
|
||||||
sCurType = type;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
~LLMemType()
|
|
||||||
{
|
|
||||||
#if MEM_TRACK_TYPE
|
|
||||||
sCurDepth--;
|
|
||||||
sCurType = sType[sCurDepth];
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void reset();
|
static char const * getNameFromID(S32 id);
|
||||||
static void printMem();
|
|
||||||
|
|
||||||
public:
|
static DeclareMemType MTYPE_INIT;
|
||||||
#if MEM_TRACK_TYPE
|
static DeclareMemType MTYPE_STARTUP;
|
||||||
static S32 sCurDepth;
|
static DeclareMemType MTYPE_MAIN;
|
||||||
static S32 sCurType;
|
static DeclareMemType MTYPE_FRAME;
|
||||||
static S32 sType[MTYPE_MAX_DEPTH];
|
|
||||||
static S32 sMemCount[MTYPE_NUM_TYPES];
|
static DeclareMemType MTYPE_GATHER_INPUT;
|
||||||
static S32 sMaxMemCount[MTYPE_NUM_TYPES];
|
static DeclareMemType MTYPE_JOY_KEY;
|
||||||
static S32 sNewCount[MTYPE_NUM_TYPES];
|
|
||||||
static S32 sOverheadMem;
|
static DeclareMemType MTYPE_IDLE;
|
||||||
static const char* sTypeDesc[MTYPE_NUM_TYPES];
|
static DeclareMemType MTYPE_IDLE_PUMP;
|
||||||
#endif
|
static DeclareMemType MTYPE_IDLE_NETWORK;
|
||||||
static S32 sTotalMem;
|
static DeclareMemType MTYPE_IDLE_UPDATE_REGIONS;
|
||||||
static S32 sMaxTotalMem;
|
static DeclareMemType MTYPE_IDLE_UPDATE_VIEWER_REGION;
|
||||||
|
static DeclareMemType MTYPE_IDLE_UPDATE_SURFACE;
|
||||||
|
static DeclareMemType MTYPE_IDLE_UPDATE_PARCEL_OVERLAY;
|
||||||
|
static DeclareMemType MTYPE_IDLE_AUDIO;
|
||||||
|
|
||||||
|
static DeclareMemType MTYPE_CACHE_PROCESS_PENDING;
|
||||||
|
static DeclareMemType MTYPE_CACHE_PROCESS_PENDING_ASKS;
|
||||||
|
static DeclareMemType MTYPE_CACHE_PROCESS_PENDING_REPLIES;
|
||||||
|
|
||||||
|
static DeclareMemType MTYPE_MESSAGE_CHECK_ALL;
|
||||||
|
static DeclareMemType MTYPE_MESSAGE_PROCESS_ACKS;
|
||||||
|
|
||||||
|
static DeclareMemType MTYPE_RENDER;
|
||||||
|
static DeclareMemType MTYPE_SLEEP;
|
||||||
|
|
||||||
|
static DeclareMemType MTYPE_NETWORK;
|
||||||
|
static DeclareMemType MTYPE_PHYSICS;
|
||||||
|
static DeclareMemType MTYPE_INTERESTLIST;
|
||||||
|
|
||||||
|
static DeclareMemType MTYPE_IMAGEBASE;
|
||||||
|
static DeclareMemType MTYPE_IMAGERAW;
|
||||||
|
static DeclareMemType MTYPE_IMAGEFORMATTED;
|
||||||
|
|
||||||
|
static DeclareMemType MTYPE_APPFMTIMAGE;
|
||||||
|
static DeclareMemType MTYPE_APPRAWIMAGE;
|
||||||
|
static DeclareMemType MTYPE_APPAUXRAWIMAGE;
|
||||||
|
|
||||||
|
static DeclareMemType MTYPE_DRAWABLE;
|
||||||
|
|
||||||
|
static DeclareMemType MTYPE_OBJECT;
|
||||||
|
static DeclareMemType MTYPE_OBJECT_PROCESS_UPDATE;
|
||||||
|
static DeclareMemType MTYPE_OBJECT_PROCESS_UPDATE_CORE;
|
||||||
|
|
||||||
|
static DeclareMemType MTYPE_DISPLAY;
|
||||||
|
static DeclareMemType MTYPE_DISPLAY_UPDATE;
|
||||||
|
static DeclareMemType MTYPE_DISPLAY_UPDATE_CAMERA;
|
||||||
|
static DeclareMemType MTYPE_DISPLAY_UPDATE_GEOM;
|
||||||
|
static DeclareMemType MTYPE_DISPLAY_SWAP;
|
||||||
|
static DeclareMemType MTYPE_DISPLAY_UPDATE_HUD;
|
||||||
|
static DeclareMemType MTYPE_DISPLAY_GEN_REFLECTION;
|
||||||
|
static DeclareMemType MTYPE_DISPLAY_IMAGE_UPDATE;
|
||||||
|
static DeclareMemType MTYPE_DISPLAY_STATE_SORT;
|
||||||
|
static DeclareMemType MTYPE_DISPLAY_SKY;
|
||||||
|
static DeclareMemType MTYPE_DISPLAY_RENDER_GEOM;
|
||||||
|
static DeclareMemType MTYPE_DISPLAY_RENDER_FLUSH;
|
||||||
|
static DeclareMemType MTYPE_DISPLAY_RENDER_UI;
|
||||||
|
static DeclareMemType MTYPE_DISPLAY_RENDER_ATTACHMENTS;
|
||||||
|
|
||||||
|
static DeclareMemType MTYPE_VERTEX_DATA;
|
||||||
|
static DeclareMemType MTYPE_VERTEX_CONSTRUCTOR;
|
||||||
|
static DeclareMemType MTYPE_VERTEX_DESTRUCTOR;
|
||||||
|
static DeclareMemType MTYPE_VERTEX_CREATE_VERTICES;
|
||||||
|
static DeclareMemType MTYPE_VERTEX_CREATE_INDICES;
|
||||||
|
static DeclareMemType MTYPE_VERTEX_DESTROY_BUFFER;
|
||||||
|
static DeclareMemType MTYPE_VERTEX_DESTROY_INDICES;
|
||||||
|
static DeclareMemType MTYPE_VERTEX_UPDATE_VERTS;
|
||||||
|
static DeclareMemType MTYPE_VERTEX_UPDATE_INDICES;
|
||||||
|
static DeclareMemType MTYPE_VERTEX_ALLOCATE_BUFFER;
|
||||||
|
static DeclareMemType MTYPE_VERTEX_RESIZE_BUFFER;
|
||||||
|
static DeclareMemType MTYPE_VERTEX_MAP_BUFFER;
|
||||||
|
static DeclareMemType MTYPE_VERTEX_MAP_BUFFER_VERTICES;
|
||||||
|
static DeclareMemType MTYPE_VERTEX_MAP_BUFFER_INDICES;
|
||||||
|
static DeclareMemType MTYPE_VERTEX_UNMAP_BUFFER;
|
||||||
|
static DeclareMemType MTYPE_VERTEX_SET_STRIDE;
|
||||||
|
static DeclareMemType MTYPE_VERTEX_SET_BUFFER;
|
||||||
|
static DeclareMemType MTYPE_VERTEX_SETUP_VERTEX_BUFFER;
|
||||||
|
static DeclareMemType MTYPE_VERTEX_CLEANUP_CLASS;
|
||||||
|
|
||||||
|
static DeclareMemType MTYPE_SPACE_PARTITION;
|
||||||
|
|
||||||
|
static DeclareMemType MTYPE_PIPELINE;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_INIT;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_CREATE_BUFFERS;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_RESTORE_GL;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_UNLOAD_SHADERS;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_LIGHTING_DETAIL;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_GET_POOL_TYPE;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_ADD_POOL;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_ALLOCATE_DRAWABLE;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_ADD_OBJECT;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_CREATE_OBJECTS;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_UPDATE_MOVE;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_UPDATE_GEOM;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_MARK_VISIBLE;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_MARK_MOVED;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_MARK_SHIFT;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_SHIFT_OBJECTS;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_MARK_TEXTURED;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_MARK_REBUILD;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_UPDATE_CULL;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_STATE_SORT;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_POST_SORT;
|
||||||
|
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_RENDER_HUD_ELS;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_RENDER_HL;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_DEFFERRED;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_POST_DEF;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_SHADOW;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_RENDER_SELECT;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_REBUILD_POOLS;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_QUICK_LOOKUP;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_RENDER_OBJECTS;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_GENERATE_IMPOSTOR;
|
||||||
|
static DeclareMemType MTYPE_PIPELINE_RENDER_BLOOM;
|
||||||
|
|
||||||
|
static DeclareMemType MTYPE_UPKEEP_POOLS;
|
||||||
|
|
||||||
|
static DeclareMemType MTYPE_AVATAR;
|
||||||
|
static DeclareMemType MTYPE_AVATAR_MESH;
|
||||||
|
static DeclareMemType MTYPE_PARTICLES;
|
||||||
|
static DeclareMemType MTYPE_REGIONS;
|
||||||
|
|
||||||
|
static DeclareMemType MTYPE_INVENTORY;
|
||||||
|
static DeclareMemType MTYPE_INVENTORY_DRAW;
|
||||||
|
static DeclareMemType MTYPE_INVENTORY_BUILD_NEW_VIEWS;
|
||||||
|
static DeclareMemType MTYPE_INVENTORY_DO_FOLDER;
|
||||||
|
static DeclareMemType MTYPE_INVENTORY_POST_BUILD;
|
||||||
|
static DeclareMemType MTYPE_INVENTORY_FROM_XML;
|
||||||
|
static DeclareMemType MTYPE_INVENTORY_CREATE_NEW_ITEM;
|
||||||
|
static DeclareMemType MTYPE_INVENTORY_VIEW_INIT;
|
||||||
|
static DeclareMemType MTYPE_INVENTORY_VIEW_SHOW;
|
||||||
|
static DeclareMemType MTYPE_INVENTORY_VIEW_TOGGLE;
|
||||||
|
|
||||||
|
static DeclareMemType MTYPE_ANIMATION;
|
||||||
|
static DeclareMemType MTYPE_VOLUME;
|
||||||
|
static DeclareMemType MTYPE_PRIMITIVE;
|
||||||
|
|
||||||
|
static DeclareMemType MTYPE_SCRIPT;
|
||||||
|
static DeclareMemType MTYPE_SCRIPT_RUN;
|
||||||
|
static DeclareMemType MTYPE_SCRIPT_BYTECODE;
|
||||||
|
|
||||||
|
static DeclareMemType MTYPE_IO_PUMP;
|
||||||
|
static DeclareMemType MTYPE_IO_TCP;
|
||||||
|
static DeclareMemType MTYPE_IO_BUFFER;
|
||||||
|
static DeclareMemType MTYPE_IO_HTTP_SERVER;
|
||||||
|
static DeclareMemType MTYPE_IO_SD_SERVER;
|
||||||
|
static DeclareMemType MTYPE_IO_SD_CLIENT;
|
||||||
|
static DeclareMemType MTYPE_IO_URL_REQUEST;
|
||||||
|
|
||||||
|
static DeclareMemType MTYPE_DIRECTX_INIT;
|
||||||
|
|
||||||
|
static DeclareMemType MTYPE_TEMP1;
|
||||||
|
static DeclareMemType MTYPE_TEMP2;
|
||||||
|
static DeclareMemType MTYPE_TEMP3;
|
||||||
|
static DeclareMemType MTYPE_TEMP4;
|
||||||
|
static DeclareMemType MTYPE_TEMP5;
|
||||||
|
static DeclareMemType MTYPE_TEMP6;
|
||||||
|
static DeclareMemType MTYPE_TEMP7;
|
||||||
|
static DeclareMemType MTYPE_TEMP8;
|
||||||
|
static DeclareMemType MTYPE_TEMP9;
|
||||||
|
|
||||||
|
static DeclareMemType MTYPE_OTHER; // Special; used by display code
|
||||||
|
|
||||||
|
S32 mTypeIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
|||||||
102
indra/llcommon/llptrto.cpp
Normal file
102
indra/llcommon/llptrto.cpp
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
/**
|
||||||
|
* @file llptrto.cpp
|
||||||
|
* @author Nat Goodspeed
|
||||||
|
* @date 2008-08-20
|
||||||
|
* @brief Test for llptrto.h
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation;
|
||||||
|
* version 2.1 of the License only.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Precompiled header
|
||||||
|
#include "linden_common.h"
|
||||||
|
// associated header
|
||||||
|
#include "llptrto.h"
|
||||||
|
// STL headers
|
||||||
|
// std headers
|
||||||
|
// external library headers
|
||||||
|
#include <boost/type_traits/is_same.hpp>
|
||||||
|
#include <boost/static_assert.hpp>
|
||||||
|
// other Linden headers
|
||||||
|
#include "llmemory.h"
|
||||||
|
|
||||||
|
// a refcounted class
|
||||||
|
class RCFoo: public LLRefCount
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RCFoo() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// a refcounted subclass
|
||||||
|
class RCSubFoo: public RCFoo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RCSubFoo() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// a refcounted class using the other refcount base class
|
||||||
|
class TSRCFoo: public LLThreadSafeRefCount
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TSRCFoo() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// a non-refcounted class
|
||||||
|
class Bar
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Bar() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// a non-refcounted subclass
|
||||||
|
class SubBar: public Bar
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SubBar() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
// test LLPtrTo<>
|
||||||
|
BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<RCFoo>::type, LLPointer<RCFoo> >::value));
|
||||||
|
BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<RCSubFoo>::type, LLPointer<RCSubFoo> >::value));
|
||||||
|
BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<TSRCFoo>::type, LLPointer<TSRCFoo> >::value));
|
||||||
|
BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<Bar>::type, Bar*>::value));
|
||||||
|
BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<SubBar>::type, SubBar*>::value));
|
||||||
|
BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<int>::type, int*>::value));
|
||||||
|
|
||||||
|
// Test LLRemovePointer<>. Note that we remove both pointer variants from
|
||||||
|
// each kind of type, regardless of whether the variant makes sense.
|
||||||
|
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<RCFoo*>::type, RCFoo>::value));
|
||||||
|
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<RCFoo> >::type, RCFoo>::value));
|
||||||
|
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<RCSubFoo*>::type, RCSubFoo>::value));
|
||||||
|
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<RCSubFoo> >::type, RCSubFoo>::value));
|
||||||
|
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<TSRCFoo*>::type, TSRCFoo>::value));
|
||||||
|
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<TSRCFoo> >::type, TSRCFoo>::value));
|
||||||
|
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<Bar*>::type, Bar>::value));
|
||||||
|
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<Bar> >::type, Bar>::value));
|
||||||
|
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<SubBar*>::type, SubBar>::value));
|
||||||
|
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<SubBar> >::type, SubBar>::value));
|
||||||
|
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<int*>::type, int>::value));
|
||||||
|
BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<int> >::type, int>::value));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
87
indra/llcommon/llptrto.h
Normal file
87
indra/llcommon/llptrto.h
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
/**
|
||||||
|
* @file llptrto.h
|
||||||
|
* @author Nat Goodspeed
|
||||||
|
* @date 2008-08-19
|
||||||
|
* @brief LLPtrTo<TARGET> is a template helper to pick either TARGET* or -- when
|
||||||
|
* TARGET is a subclass of LLRefCount or LLThreadSafeRefCount --
|
||||||
|
* LLPointer<TARGET>. LLPtrTo<> chooses whichever pointer type is best.
|
||||||
|
*
|
||||||
|
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
|
||||||
|
* Second Life Viewer Source Code
|
||||||
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation;
|
||||||
|
* version 2.1 of the License only.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
|
* $/LicenseInfo$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if ! defined(LL_LLPTRTO_H)
|
||||||
|
#define LL_LLPTRTO_H
|
||||||
|
|
||||||
|
#include "llpointer.h"
|
||||||
|
#include "llrefcount.h" // LLRefCount
|
||||||
|
#include "llthread.h" // LLThreadSafeRefCount
|
||||||
|
#include <boost/type_traits/is_base_of.hpp>
|
||||||
|
#include <boost/type_traits/remove_pointer.hpp>
|
||||||
|
#include <boost/utility/enable_if.hpp>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LLPtrTo<TARGET>::type is either of two things:
|
||||||
|
*
|
||||||
|
* * When TARGET is a subclass of either LLRefCount or LLThreadSafeRefCount,
|
||||||
|
* LLPtrTo<TARGET>::type is LLPointer<TARGET>.
|
||||||
|
* * Otherwise, LLPtrTo<TARGET>::type is TARGET*.
|
||||||
|
*
|
||||||
|
* This way, a class template can use LLPtrTo<TARGET>::type to select an
|
||||||
|
* appropriate pointer type to store.
|
||||||
|
*/
|
||||||
|
template <class T, class ENABLE=void>
|
||||||
|
struct LLPtrTo
|
||||||
|
{
|
||||||
|
typedef T* type;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// specialize for subclasses of LLRefCount
|
||||||
|
template <class T>
|
||||||
|
struct LLPtrTo<T, typename boost::enable_if< boost::is_base_of<LLRefCount, T> >::type>
|
||||||
|
{
|
||||||
|
typedef LLPointer<T> type;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// specialize for subclasses of LLThreadSafeRefCount
|
||||||
|
template <class T>
|
||||||
|
struct LLPtrTo<T, typename boost::enable_if< boost::is_base_of<LLThreadSafeRefCount, T> >::type>
|
||||||
|
{
|
||||||
|
typedef LLPointer<T> type;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LLRemovePointer<PTRTYPE>::type gets you the underlying (pointee) type.
|
||||||
|
*/
|
||||||
|
template <typename PTRTYPE>
|
||||||
|
struct LLRemovePointer
|
||||||
|
{
|
||||||
|
typedef typename boost::remove_pointer<PTRTYPE>::type type;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// specialize for LLPointer<SOMECLASS>
|
||||||
|
template <typename SOMECLASS>
|
||||||
|
struct LLRemovePointer< LLPointer<SOMECLASS> >
|
||||||
|
{
|
||||||
|
typedef SOMECLASS type;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* ! defined(LL_LLPTRTO_H) */
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user