Compare commits
2205 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c4d99f93db | |||
| 801adb0947 | |||
| b3f5d6d85b | |||
| 04c808b8ab | |||
| e55d216619 | |||
| e334a43169 | |||
| cca6a19cdd | |||
| 64aff7f019 | |||
| 2962e05f26 | |||
| 27aeaf66bc | |||
| 9672ad53cd | |||
| b6b7d7b48c | |||
| ec8c5453bc | |||
| fd839243ec | |||
| ede816387f | |||
| 796a6646f7 | |||
| dd79a5efb7 | |||
| e41b234dc4 | |||
| d2eba2387b | |||
| 6811ec562c | |||
| fcbf91b7cb | |||
| 4ec3e968ff | |||
| 3dd6961ee5 | |||
| 232e815f17 | |||
| ebdb0781fa | |||
| 84e44d3372 | |||
| 2a37a4bcea | |||
| 928719b658 | |||
| 7a00b5f809 | |||
| cfdd298693 | |||
| 7b0d834477 | |||
| e4a7e8c058 | |||
| 1d3c18f4e2 | |||
| 9a2338a5ca | |||
| 38a06c4b14 | |||
| cf44038435 | |||
| 8304043d00 | |||
| a921fe3351 | |||
| 35a3bed91a | |||
| 3317e13b64 | |||
| 32b5b11a83 | |||
| eed32cf3a4 | |||
| 3e28e4b1e0 | |||
| fe6ff8622a | |||
| f9aea076ae | |||
| 0aea33687e | |||
| 9d981de96d | |||
| b70908c8f3 | |||
| 6165bd68c0 | |||
| 94dd6c2007 | |||
| 896cfd2ade | |||
| 0b720aeb59 | |||
| 2d29a33ea8 | |||
| b3c1759853 | |||
| ec9b896fbf | |||
| c43a126f62 | |||
| 8cf025bf71 | |||
| 2b55bde104 | |||
| 02fe1ef7ff | |||
| 4ac1e2d8f5 | |||
| 93d5c0a38e | |||
| 2740fafa64 | |||
| 54952f6ad6 | |||
| a85a8796d3 | |||
| 606601441d | |||
| 2882220cbd | |||
| 8517ccfdfa | |||
| 7db2e2537f | |||
| 7a3aa02dc1 | |||
| 7c5bde4aeb | |||
| c67c97844e | |||
| 5d1b7eb945 | |||
| ca8b0da141 | |||
| 141072023b | |||
| 9cd5325962 | |||
| be98554b40 | |||
| 67dac94704 | |||
| 35e61c77e0 | |||
| 9c64318e8a | |||
| b0d0f51d8d | |||
| bd8cbcdb21 | |||
| dca29def5e | |||
| 5e299111cc | |||
| dfe709d464 | |||
| 5817e2a359 | |||
| 87ffa04a2b | |||
| b3aff3a1c6 | |||
| eedf96e1f4 | |||
| 7408f50d71 | |||
| 86a8b5924a | |||
| 8a31c7a7c8 | |||
| 62414cee15 | |||
| 960c8e6538 | |||
| 0af342a978 | |||
| dd86aa472d | |||
| 6535fb0603 | |||
| 11a43e25d7 | |||
| 92e6dee60f | |||
| c5440c95c8 | |||
| 4cfff8ea4a | |||
| 1444492eab | |||
| 050339bb3e | |||
| 500a4fa2fb | |||
| 51188db717 | |||
| 0d88f037ff | |||
| d8402008dd | |||
| 0dce6be8cb | |||
| 80827aa31a | |||
| 6540fecdc3 | |||
| f342f4b826 | |||
| 56011cbddd | |||
| 42ff93d30c | |||
| f2a2dfda3a | |||
| f0c267ecb0 | |||
| a0f0c418d7 | |||
| 7f85b18b46 | |||
| 9f854dba98 | |||
| 503d43889d | |||
| 0dc7841d0f | |||
| 13367071b0 | |||
| 2040a11585 | |||
| 8debdfcd09 | |||
| 08c581947d | |||
| 4df1c16099 | |||
| 9f9309055f | |||
| d41d7b5e24 | |||
| f8f3067deb | |||
| d45c743877 | |||
| 4f22559ff0 | |||
| cf16ee5eb5 | |||
| ae46333e99 | |||
| 87bf0386c4 | |||
| b3f4cd3a95 | |||
| 7fda07eb5b | |||
| 519fe9309e | |||
| 527b1bb071 | |||
| 19825366f5 | |||
| a902874b01 | |||
| 95f22b76c6 | |||
| a4896eb6df | |||
| 30c72b105e | |||
| 14ac70dfbf | |||
| 4b82006984 | |||
| 4f00af6000 | |||
| 6d3fc3a386 | |||
| a6c94a0eaf | |||
| cd4e895c04 | |||
| 629d8e3a51 | |||
| 6d0f2ae58a | |||
| a560498cd4 | |||
| d48d424b19 | |||
| 0a5b6ad928 | |||
| a832581201 | |||
| ecb3b62787 | |||
| 96527eee5b | |||
| 5c904bf3e2 | |||
| cbcd06d1dc | |||
| 3ce18fd960 | |||
| 94d33003a3 | |||
| 25e47e0de0 | |||
| 8866a3d6a9 | |||
| 8150095e8f | |||
| f051f53e15 | |||
| 2f12226ff9 | |||
| d4c635a83c | |||
| 697128464b | |||
| 0559e929d4 | |||
| efcb46658b | |||
| 01720afba9 | |||
| e21b8eb95b | |||
| 033836d88c | |||
| 112ad9dda2 | |||
| 05813c7e90 | |||
| 6e89e4ebbf | |||
| 4ad784d614 | |||
| c219417f62 | |||
| 5bd17a44c9 | |||
| 72fb3ba794 | |||
| f0bd0b5a7c | |||
| d689a6e47b | |||
| e40b76b253 | |||
| 7508ae6e65 | |||
| 1e5e0f3b5e | |||
| 949f33c8a4 | |||
| c6993d3f1c | |||
| e1db6cef6f | |||
| 829047ecdb | |||
| 8886414013 | |||
| f63e89f9b4 | |||
| ec2db8edbc | |||
| 997e3046e3 | |||
| 65134f73d6 | |||
| 76a1fd5c88 | |||
| 1de90bdb1f | |||
| 738cfccbba | |||
| 230c3d55b6 | |||
| fcc75c8b0f | |||
| 22994d15fb | |||
| c53f0dd7b5 | |||
| b56b425b19 | |||
| b44b44b3c4 | |||
| 71c56b719c | |||
| 090c71aa7c | |||
| 49aefc0795 | |||
| 85b1152e29 | |||
| d17be2c2ec | |||
| cf4d715284 | |||
| 2b721b9e29 | |||
| 492c9e3c7c | |||
| 070c3af50f | |||
| a98a2b79d0 | |||
| 6128fffd3e | |||
| 66c1143a65 | |||
| f5f0a5aa19 | |||
| 78ecacabcb | |||
| 9a7c2784f0 | |||
| 4e4c896136 | |||
| e2574ff100 | |||
| 79ddf55937 | |||
| b5a2f0df68 | |||
| 1b140c8e97 | |||
| 95b6883ad4 | |||
| d9c311737d | |||
| a268415121 | |||
| ea2f4d57fa | |||
| 113a49e00c | |||
| 34b06453f9 | |||
| 998717659f | |||
| 009e01a347 | |||
| 397e5dd1ea | |||
| 736ec4035e | |||
| c062000288 | |||
| cb5c26f2b5 | |||
| 1e2f81dd33 | |||
| c01f1a8968 | |||
| cfa1416d64 | |||
| 1a1eff66bc | |||
| bc1617758c | |||
| 6126a98111 | |||
| e1293b14f8 | |||
| f5ee8c1292 | |||
| 0a88a23bf5 | |||
| cbfa6cc91f | |||
| 38d4e3c184 | |||
| 7398cfc950 | |||
| 22e4ef147a | |||
| 9ff6486c85 | |||
| c727760537 | |||
| 3de885c7a6 | |||
| 3d6c211382 | |||
| 94af3b3f03 | |||
| ae055f6792 | |||
| 17c8ac9ab8 | |||
| c4d8ef4340 | |||
| efbae02a22 | |||
| f3551cedb7 | |||
| 6c6b973342 | |||
| 2898309f68 | |||
| fc09572648 | |||
| d8f753f1e8 | |||
| 537d3f2548 | |||
| d7a2d42d8f | |||
| 0979207cc6 | |||
| 2016a33277 | |||
| ab228d6407 | |||
| 78e447e28c | |||
| 2548057ea0 | |||
| 956270d9be | |||
| c072c511a1 | |||
| 9ca6546dec | |||
| 0cc672dcb3 | |||
| 219a5382f7 | |||
| 7840cd914e | |||
| c47f4fd59e | |||
| b9130dd663 | |||
| d1db6a6827 | |||
| 6fba3dd90c | |||
| 9d9e158b9a | |||
| d57b4dc8f3 | |||
| db4bc6f6be | |||
| 92c08be387 | |||
| 50736263d2 | |||
| 20dd7c0200 | |||
| b6cfa5c9b9 | |||
| 6d65d3424f | |||
| 4c2fb54b99 | |||
| ad10eb391e | |||
| 71e809a78b | |||
| 0f11939faf | |||
| ae93b417ec | |||
| d69e8288ad | |||
| ce04a797c3 | |||
| b5d5113803 | |||
| 690cdff0d3 | |||
| 1e0289bcb2 | |||
| 503e023815 | |||
| d57124d075 | |||
| 146e43b6ec | |||
| 13a557fe67 | |||
| 3d8389457e | |||
| 2bce367488 | |||
| 43f919c04b | |||
| 1ee7b91646 | |||
| e043412be4 | |||
| 232a35b6fc | |||
| 948c1df969 | |||
| a124ec4a26 | |||
| d0eae7bab1 | |||
| 22917084d9 | |||
| ebb1ae6408 | |||
| 23094b48bb | |||
| be521ff0ab | |||
| 32f8f40a4a | |||
| 1fcf62fdf0 | |||
| 27fca2ed33 | |||
| 1641be5e0c | |||
| 31afd046b0 | |||
| 9ce7b8a401 | |||
| 009c55fcba | |||
| 4ff77a8555 | |||
| 8249d51aa8 | |||
| 1ee675e1f4 | |||
| 2b9c315374 | |||
| d26b25816d | |||
| b893a75002 | |||
| c32c1b0a62 | |||
| 6a32f716db | |||
| 59ee74a275 | |||
| 0fa3bcc336 | |||
| 358169507c | |||
| b645ebdb04 | |||
| 25bba9bf56 | |||
| f113cfe5c0 | |||
| c9501b60ab | |||
| 6be7c58fa4 | |||
| c963c51a60 | |||
| 23432d420d | |||
| a070a414a6 | |||
| e9e87b8bd9 | |||
| 499035512b | |||
| 8e942eb50c | |||
| 806a7bbf04 | |||
| cd0ef99df5 | |||
| ade291e73d | |||
| 517a2bc34a | |||
| a935203a18 | |||
| b9b591bf02 | |||
| 3602d45cb7 | |||
| a0f2ade55d | |||
| 5753b3222e | |||
| d021d9e14a | |||
| 89a31292dc | |||
| 5d45a3e47d | |||
| 17d0dcb7a2 | |||
| d52953fca6 | |||
| 0fe26f8293 | |||
| 185fd3d257 | |||
| 5928dd826c | |||
| b94730fa95 | |||
| b29a89807e | |||
| 3f00b485a0 | |||
| 8dafc8e542 | |||
| e515d9b83a | |||
| c90186cf35 | |||
| 98156d2a2c | |||
| 4104d7a6a1 | |||
| 86119f58d6 | |||
| 9d166c5bed | |||
| 296e0105c4 | |||
| 9d0f5aedf3 | |||
| e4671c29a6 | |||
| 8a1cf2c913 | |||
| ca82dd7200 | |||
| c655036920 | |||
| 5f34c0c59a | |||
| 9d314ca4ca | |||
| bca5f94b3f | |||
| ea8409dcfd | |||
| acb14320da | |||
| 4a5bbba277 | |||
| f6f3b40ecc | |||
| 655eff29e8 | |||
| db5d56a22d | |||
| 0d906d6974 | |||
| ac3982d2b0 | |||
| 595ad15499 | |||
| 13671d9684 | |||
| 1e327ad31b | |||
| 370dcd9d6e | |||
| 0b97d12a7b | |||
| f1e939936a | |||
| 44265aa27d | |||
| 635695ac78 | |||
| a555504142 | |||
| cba828384d | |||
| a02a8642b0 | |||
| b91bcdbbca | |||
| 349e7c8708 | |||
| 35dc98be06 | |||
| ac7e89660f | |||
| 801fa0113f | |||
| c9b0a4afd0 | |||
| 8f2647555a | |||
| 331fe01762 | |||
| db926ee671 | |||
| 81a3b49c2f | |||
| c4667133c4 | |||
| 497f0c26e7 | |||
| 65581acbf9 | |||
| d6db61c594 | |||
| b3513cd7fa | |||
| d361eedbec | |||
| 57196f0504 | |||
| 527f7a437a | |||
| 652e32341d | |||
| 8646e90832 | |||
| b190e1f50b | |||
| 39fa1d7031 | |||
| f9ad635856 | |||
| 2bc0ea37ab | |||
| e8a516529b | |||
| 1dedeb37bf | |||
| b2e08100ff | |||
| 9dc0368079 | |||
| 447029a700 | |||
| 31994dd3b2 | |||
| 0333942795 | |||
| dfd50d0185 | |||
| 790962425c | |||
| 99c1f87289 | |||
| 66946a83e6 | |||
| 8a6b5ca71f | |||
| 09c9996f31 | |||
| bf557ea6e6 | |||
| 78c7ef3039 | |||
| f90f68d129 | |||
| cafa4521ee | |||
| 141d78344a | |||
| d9d7634f8b | |||
| 32776db8a6 | |||
| ef587d4cdc | |||
| f2cac26154 | |||
| 6e92516070 | |||
| e118c9e5da | |||
| c638e82678 | |||
| fb1d8cb548 | |||
| 5760aa2e57 | |||
| e4fca8274e | |||
| f48589005d | |||
| 8ebe02ce32 | |||
| 3a5d1c6b15 | |||
| 34964e0e4f | |||
| d9ab2c7104 | |||
| 57eb892153 | |||
| 71b71537a0 | |||
| b839cc9d5b | |||
| 507ec8252b | |||
| c348fec84b | |||
| 6ab3011eef | |||
| d616ce223c | |||
| 370b9e196d | |||
| 46ba53f122 | |||
| c3b234f972 | |||
| 9a26c0abda | |||
| 7de3c410cf | |||
| 9abb303569 | |||
| ceaa669494 | |||
| a6cde36dc4 | |||
| 8009e53642 | |||
| a245d9fb24 | |||
| 0ef7b2aaff | |||
| dc749d3975 | |||
| 85920253b3 | |||
| 6416fee986 | |||
| bf7ee2de4c | |||
| 2cf139c7a8 | |||
| 164a7636bf | |||
| e3f68c66d8 | |||
| edb1a32bb1 | |||
| 32bdd8ef7b | |||
| 4037225e98 | |||
| 9aa08313e3 | |||
| cb78522620 | |||
| 45a13d0319 | |||
| 05eb1e9193 | |||
| 214bb3ec87 | |||
| 88290d9fab | |||
| 4182f9984d | |||
| 3c7d8a1099 | |||
| 9f0090772a | |||
| 4b443be881 | |||
| b840f6da28 | |||
| e3cf3ab6b8 | |||
| e02e27b2b3 | |||
| 1c439f6198 | |||
| 88c20afd3a | |||
| a016728a9e | |||
| a69329fe02 | |||
| 26d131ef29 | |||
| 91897fa69d | |||
| f95ec8a37c | |||
| b138d25b97 | |||
| a921af1e96 | |||
| 43ae4b3f33 | |||
| 007108100e | |||
| 4e75d2c2f7 | |||
| adc7d96683 | |||
| 16401722f1 | |||
| b2d0ca83fb | |||
| 09eb633f69 | |||
| 3753d27aba | |||
| c481472faf | |||
| 82295d131a | |||
| ea554ae828 | |||
| 1eed421774 | |||
| 2f6707d0a0 | |||
| f33b81545e | |||
| a74fd27e3d | |||
| cc8c635855 | |||
| 7cf11c7637 | |||
| 6b01c2f9f5 | |||
| 22f61caf9f | |||
| 786dc6ef40 | |||
| 6c5ea068c9 | |||
| 1f82a26a9c | |||
| 3623f4343f | |||
| 8194c92f1c | |||
| 5d33d9a3d9 | |||
| 7492f335e9 | |||
| fce9d6a7b8 | |||
| 7ead021aa7 | |||
| feb77793df | |||
| f1ff75d815 | |||
| 13d8e33d9a | |||
| 4e2650af91 | |||
| ca08418dc1 | |||
| 5141590729 | |||
| fd2bc95a7b | |||
| 9aa9cbf1d8 | |||
| 87d493fe1b | |||
| e883f4f880 | |||
| 767eaaa82a | |||
| caa6895310 | |||
| ea0f04e130 | |||
| f480ba13c0 | |||
| c1e245ae73 | |||
| b639584acc | |||
| b0a24665e5 | |||
| 8a4778ba8c | |||
| 9c346c92f3 | |||
| 2bec304cca | |||
| f246a8666a | |||
| a703460fc6 | |||
| ba1ec1d5d6 | |||
| 45ce8db6cb | |||
| 4bd022f778 | |||
| 6c34d7104e | |||
| 54dc617f39 | |||
| 15bd3b8724 | |||
| 2b4da18709 | |||
| d48dc29e55 | |||
| 69d2ea35b9 | |||
| a4c4ee9cb2 | |||
| bc42719c48 | |||
| 6b7cd458e3 | |||
| 1d5c52f476 | |||
| 48cb265c2c | |||
| 63f9b8e0c6 | |||
| ec9aa74008 | |||
| 4e3ec162e2 | |||
| 6feffe6ff6 | |||
| f56a6548b0 | |||
| 1bc5e27507 | |||
| 1294e0d189 | |||
| e09c6ea4b4 | |||
| 716737ecf2 | |||
| c24b74398f | |||
| 301dd82819 | |||
| 3bd29b8bac | |||
| ea1c9a2e17 | |||
| 468e1e90c8 | |||
| be6d9af1ab | |||
| 243df38360 | |||
| 3cbccdd760 | |||
| 4cd1c25729 | |||
| 32396ba366 | |||
| f12bd8068c | |||
| abe7188acf | |||
| 60f3059d22 | |||
| 463b1b220d | |||
| 7ad1646c2c | |||
| 06a85f00d1 | |||
| a668560c68 | |||
| ee319f7c13 | |||
| 2ade3c594c | |||
| aca61fdcf9 | |||
| 8754bbd444 | |||
| dd9bd1338b | |||
| 3e0b45719d | |||
| b02503963e | |||
| c83e65f29e | |||
| b7b93eae13 | |||
| 2eac59f59a | |||
| ac473eb400 | |||
| 42448cf3e5 | |||
| 524aac75ed | |||
| 3925cb59ac | |||
| dc9118ef50 | |||
| 467ef2afca | |||
| cb5dd1353b | |||
| bb0609372d | |||
| 258df80bf6 | |||
| a24ede4f40 | |||
| 3a5f4ed757 | |||
| 450e2c9a0e | |||
| ae5a4b697e | |||
| 1e9d3346d1 | |||
| 9446bd2d3f | |||
| 6f76e34104 | |||
| 67f098627a | |||
| ff11d9a3bd | |||
| 9497270da5 | |||
| 2b4bc588dc | |||
| 888cb9d673 | |||
| f25587d24c | |||
| 777a99d01b | |||
| 7865982208 | |||
| 52c589ed3d | |||
| 9060177dbd | |||
| 1152317b47 | |||
| b929c13c45 | |||
| 092cfef060 | |||
| 8f333117eb | |||
| baf42430d6 | |||
| f995771143 | |||
| bad0415f05 | |||
| 136fc8cfe3 | |||
| b97a1aeca2 | |||
| c1edae73c5 | |||
| 8deb20e928 | |||
| e064b92058 | |||
| d52df9352c | |||
| fe593de6d7 | |||
| df3b46a611 | |||
| 0d415407c7 | |||
| f40320bcae | |||
| c34821ded2 | |||
| 38cc92ec45 | |||
| 8da78c098c | |||
| eba8e2284b | |||
| 969af86eba | |||
| b307aff9eb | |||
| 1c462abc37 | |||
| 3304e3b0b7 | |||
| f028737db8 | |||
| a9014a8cac | |||
| e21504d52d | |||
| aa44ef1f44 | |||
| d555eeb0f4 | |||
| af86d1d77a | |||
| 52a12185a0 | |||
| 1d947daa81 | |||
| ec2d882199 | |||
| 40142420c1 | |||
| 0736fc8b28 | |||
| a54d9c527f | |||
| 9736773c04 | |||
| 3354f068fc | |||
| 12dc3c1872 | |||
| a4495c35b7 | |||
| a5ed30a8c0 | |||
| f04a0a2bb6 | |||
| 0a604e39f1 | |||
| cc3809fbfe | |||
| e4e86455f2 | |||
| 3249000511 | |||
| 0f7af391d7 | |||
| 366e4c2422 | |||
| b7470cfc1a | |||
| 443fde760f | |||
| f30af3ccd2 | |||
| 7625f7df11 | |||
| 8dbf2638e2 | |||
| d7ad991b7e | |||
| 3bfe921a7a | |||
| 2119566da7 | |||
| 182054b8af | |||
| 3c0564c9b7 | |||
| 90490f775d | |||
| 8185ef7610 | |||
| 801e6114b6 | |||
| a3f65084df | |||
| 1ed2055ec1 | |||
| 16f86b2458 | |||
| f2bbae9db4 | |||
| 8eb59271a5 | |||
| d6025c6764 | |||
| 47ca1b1dda | |||
| 6502d933df | |||
| f15eb88f59 | |||
| 1916cc1691 | |||
| f369aeab7a | |||
| 0e9313d2df | |||
| d873802b9c | |||
| 7e0b941e9f | |||
| d77fed6105 | |||
| a254a203bb | |||
| 71b88cdc82 | |||
| a9eb321814 | |||
| 956d039415 | |||
| 581b205f73 | |||
| 6c40b9f3e0 | |||
| d6bf18eabc | |||
| 32ae7e466c | |||
| 7ea04d5d76 | |||
| 4b381fbbb1 | |||
| 1880a17f79 | |||
| 31304f4234 | |||
| 3794f65bb6 | |||
| 849411693a | |||
| 466d58ccef | |||
| 41b87cf577 | |||
| 86ad204636 | |||
| dbc8e70ddd | |||
| 3a4e3fa53a | |||
| d731c07ade | |||
| adc59f9810 | |||
| b879b32271 | |||
| 03fca73cf4 | |||
| 1a13d3ae4e | |||
| 1f845a3634 | |||
| 3eb2bcad39 | |||
| 63bbe459ea | |||
| f2aaad11bb | |||
| 3ef16bee47 | |||
| bb65e45614 | |||
| 5260f4b47d | |||
| 385485204b | |||
| 74eeacd091 | |||
| 1a44446250 | |||
| a49a0f2a86 | |||
| 85cb703633 | |||
| 3a4083263e | |||
| 612160a8ff | |||
| 17c49d21a5 | |||
| 745f8f9627 | |||
| e9b6beadfc | |||
| 388d49db80 | |||
| 980c3d6fe8 | |||
| 3b01e59cd4 | |||
| a08cd0fe38 | |||
| 8623899499 | |||
| 6d4c9f2d41 | |||
| 30636f0199 | |||
| 0aa9243557 | |||
| eae5b9e049 | |||
| 688aabc6c9 | |||
| 4a28319edf | |||
| 1875dc3f18 | |||
| 358558aaa7 | |||
| 04dda44225 | |||
| 21bc6c7a87 | |||
| 9d4d3d9443 | |||
| f4ebcb0029 | |||
| 7c379432dd | |||
| dccb4a4888 | |||
| b9e7b085fe | |||
| a80f1a6ed4 | |||
| 0395fb9955 | |||
| 10dc3dadee | |||
| 563e205a72 | |||
| fa628f0ac4 | |||
| 3a071a9c07 | |||
| 6ef9dcd62e | |||
| ef071ebb6b | |||
| a43e7c172c | |||
| 7470388e5a | |||
| 26bcd0a4de | |||
| bd259c8ae4 | |||
| fc253165e2 | |||
| b7da3ea5cd | |||
| ce8c92262d | |||
| f272a5f779 | |||
| 61bd2ea799 | |||
| 3c9f03968c | |||
| 4ea76def7c | |||
| 986c750fdc | |||
| efe067bfa0 | |||
| e38b7aee5a | |||
| d82362fa1d | |||
| ebb24375d1 | |||
| fb64b28886 | |||
| 7f6a410770 | |||
| 765d15f23f | |||
| f143035af1 | |||
| 9ee2867247 | |||
| 7b344b7654 | |||
| 6c5d9fffaa | |||
| 240ba39d16 | |||
| de5379a69f | |||
| a22297f205 | |||
| d4459af4b3 | |||
| ad6b0ee122 | |||
| b107718869 | |||
| f5163cc4ff | |||
| 923700a167 | |||
| c325534f07 | |||
| 14ddb0c5b4 | |||
| 4c72f7c1de | |||
| 3b9f9dd4c5 | |||
| 45c31a99a3 | |||
| 63a9421634 | |||
| 92ff7c4c24 | |||
| 56e24f8993 | |||
| a8621ebc22 | |||
| d4888a4973 | |||
| 4a1499e0be | |||
| fe79e541dd | |||
| 17a5e0bc98 | |||
| 982b2ab88f | |||
| d6995f40c7 | |||
| fa647bc121 | |||
| 84d42ecb65 | |||
| 0861226271 | |||
| e3ee481f01 | |||
| 19ff95da70 | |||
| 38887bc03e | |||
| 4c593959fd | |||
| 67cdec577f | |||
| a36d3af3b4 | |||
| 27af05da1a | |||
| b36cb66129 | |||
| 12990f3ca3 | |||
| 9e39e2d2c4 | |||
| 3653c2eb0d | |||
| faf5221b0d | |||
| c434e0ce27 | |||
| 9ed9d7e947 | |||
| 9d4b19b97a | |||
| 8920eda321 | |||
| f4afd56e67 | |||
| f47d3a761b | |||
| 27f6f497a2 | |||
| 09a8f7ae53 | |||
| 3bfa564ef8 | |||
| 987b607cb0 | |||
| d4a24433e8 | |||
| 400718dfd9 | |||
| 25aa5f80a6 | |||
| b61492ca30 | |||
| 6f770c8e35 | |||
| 039f8e1f9d | |||
| f50d9cc136 | |||
| 58358e85dd | |||
| 2b66abaf10 | |||
| 50922faac9 | |||
| 4292997a01 | |||
| d1f544c755 | |||
| 587fe421ee | |||
| 56b7581ade | |||
| 660bfeabae | |||
| fa9b448686 | |||
| 2163a59649 | |||
| 521969bcc3 | |||
| 5a4bea8c4f | |||
| 7e33cdcb08 | |||
| fb778e4821 | |||
| 736415fcd9 | |||
| 6fdc0fd7f0 | |||
| 10a17feda2 | |||
| 7eec702c6d | |||
| 8093c9b573 | |||
| 417a2e6731 | |||
| afeacc171f | |||
| d5eda98e49 | |||
| 4f152ad126 | |||
| 8d5d44141e | |||
| 688d5a9919 | |||
| fa00fb3eac | |||
| d4591b1508 | |||
| 59e0296281 | |||
| cc430769c6 | |||
| a11d76249d | |||
| 5ed718110a | |||
| f902a7867c | |||
| fc92ae4085 | |||
| f19fd23227 | |||
| 58a22d1461 | |||
| c5026f7109 | |||
| a419e98acb | |||
| aefee04c4a | |||
| 74d684b57e | |||
| 81c50bad69 | |||
| ac775cd75e | |||
| 3fed4dcc5a | |||
| 435f60d503 | |||
| cfdf50dcff | |||
| 453344c232 | |||
| bd9131ae1c | |||
| 20efed66e9 | |||
| bc27f5f75c | |||
| 022550a43b | |||
| d7cc97433d | |||
| fce9f352a9 | |||
| f9c1b15bf4 | |||
| 3406acd8c9 | |||
| 49fbf9bf0f | |||
| 1e4513e2e3 | |||
| fe9062671e | |||
| 5e3311746c | |||
| 48db0430d4 | |||
| 650bc0c1f2 | |||
| f17f984721 | |||
| 8f6043b568 | |||
| 67f9397746 | |||
| 4f39457858 | |||
| f90646ec88 | |||
| 892deb1552 | |||
| 5159d3559e | |||
| 2addbe6be2 | |||
| 56962aa707 | |||
| e18e9909af | |||
| fad80ed443 | |||
| e3ea29599d | |||
| d9d5f45e9e | |||
| eb4d612bb9 | |||
| e5aede7aa7 | |||
| 17058113df | |||
| 4a8b30c625 | |||
| e0ac244fed | |||
| d5303b0b64 | |||
| 7cc4239cc2 | |||
| cc30b4e5be | |||
| 16f2975792 | |||
| 1b2e512179 | |||
| 0236b994e4 | |||
| d7cd280827 | |||
| 52ac0a901a | |||
| 89c9c54743 | |||
| 95245bdd83 | |||
| 475cc99117 | |||
| c80c7f06bb | |||
| 2a62fa892b | |||
| 0f64583c7a | |||
| 1ffadbe086 | |||
| 9c45ce6d37 | |||
| d8e45c86e6 | |||
| 3be4b474d9 | |||
| a95a44cf66 | |||
| 474d0f61a2 | |||
| 362eea09a1 | |||
| 15d5507154 | |||
| 97704d1396 | |||
| 50c07833e4 | |||
| 6d932b042b | |||
| 64a8829614 | |||
| 15488eb247 | |||
| a13986ec5c | |||
| 6c10cb2eb5 | |||
| 6fe615605a | |||
| bb464b0b64 | |||
| daee2a27a3 | |||
| 5f9b441dd7 | |||
| 37c621ebbf | |||
| 7a4282b4c0 | |||
| 3b83e223d8 | |||
| 0e7fd45504 | |||
| ad5cdc38cd | |||
| 6f7b25de90 | |||
| 8ad14c4ada | |||
| a4f67ccb87 | |||
| 17d413c110 | |||
| 54d41f6805 | |||
| 632b0f489c | |||
| a77bd41b50 | |||
| 2db16c2c04 | |||
| 7ebd3bbcbf | |||
| 483ee5e488 | |||
| 79a6a2c7fb | |||
| 2ea061b9c7 | |||
| c0eb4965f2 | |||
| be75a564ca | |||
| d1be1150c2 | |||
| c6e47a573d | |||
| db784556aa | |||
| 26d47afec6 | |||
| 568206d11a | |||
| a1dd6e080c | |||
| 64ea7bf867 | |||
| 8d308bb4c0 | |||
| 33f7a39f5d | |||
| cfe0709867 | |||
| aa306dd356 | |||
| 4b65c3943c | |||
| 653a4ef0df | |||
| 7c74bafaf3 | |||
| dbd4814584 | |||
| 1dd3a4b3f0 | |||
| d7be0a96f3 | |||
| f750b4c420 | |||
| 522f5ea645 | |||
| 9c46386dd4 | |||
| 7555be232f | |||
| 10d96a60f1 | |||
| f48abde14b | |||
| 4b79ef1ad9 | |||
| 696b91cb9b | |||
| c75749f8ce | |||
| 25b8a42519 | |||
| fc25df84f8 | |||
| eba364c64c | |||
| b99a88afe2 | |||
| e80f2793eb | |||
| 6dcf66b064 | |||
| bdb5606317 | |||
| 6e323622a5 | |||
| 91d834ce73 | |||
| f617e47152 | |||
| e80574cbd2 | |||
| 79003cd089 | |||
| 9717e19be2 | |||
| c775e5fb97 | |||
| b5cbe02f4d | |||
| d643dcb43b | |||
| 69fc1f2613 | |||
| 6b1d80ec25 | |||
| ae85c40a0a | |||
| bc3ab7a9d9 | |||
| 5a40c1802b | |||
| c50d459b1e | |||
| 098d634328 | |||
| c2aa10cccd | |||
| 45ed58cdaf | |||
| 87988e9da8 | |||
| 835a552d8d | |||
| fd894d4c69 | |||
| 415b995a54 | |||
| 8cbaa8627c | |||
| 2e7e2bb07e | |||
| 76f94940cd | |||
| 54bfe2e102 | |||
| 18b9ee4541 | |||
| 5ab7296665 | |||
| bcdf91edbb | |||
| 9d1cdccb1a | |||
| af360b78f2 | |||
| 1ca02c7d17 | |||
| ead03caedc | |||
| 077e710e3a | |||
| d6afba96f1 | |||
| d98420fbc3 | |||
| fb8302817f | |||
| 5207b00973 | |||
| 92be67b402 | |||
| 476090a747 | |||
| 90265edfcd | |||
| 45942c4962 | |||
| fa5652fceb | |||
| ce500c75c4 | |||
| 6ecf2fb3d0 | |||
| 28e4a9e0d0 | |||
| d1d3ac984e | |||
| 0a1da14a15 | |||
| 4ddafc481e | |||
| b48a4b6459 | |||
| 32059bfaa2 | |||
| afdf0b74a0 | |||
| 87be1abb9a | |||
| 6f086fd5af | |||
| a56ba737b5 | |||
| 504ab5a6d4 | |||
| f4c28eceef | |||
| 9d0042f509 | |||
| 24a8d0aeef | |||
| 7f3eaed435 | |||
| 1001831c8e | |||
| 25414953e0 | |||
| 8afc035aac | |||
| 62c9920f2e | |||
| ec768afbd9 | |||
| 2168159d03 | |||
| b96ed5cd4e | |||
| f1c3b46d60 | |||
| 5b6e1420f3 | |||
| 8a186bb97e | |||
| 1d19f71a46 | |||
| 718a04c84f | |||
| 833c25894f | |||
| 5193c99973 | |||
| 7b065d7781 | |||
| 846daadd5d | |||
| 2e32777375 | |||
| 9deecd506a | |||
| 26a56ef1f1 | |||
| d6b4440ef9 | |||
| 435afcb865 | |||
| 2d88e41583 | |||
| 54ec363e88 | |||
| 1ad76ad331 | |||
| b0a6b72ce8 | |||
| 677b16f5c6 | |||
| e01d2f08c9 | |||
| 96cabeadff | |||
| f442a8a84c | |||
| febe4d4a10 | |||
| 7f1cbb1136 | |||
| 4baa159523 | |||
| 0526bfd7de | |||
| bf0e6ca174 | |||
| f505ac7b63 | |||
| 3c7f02d99d | |||
| 634a5fa31c | |||
| 65ca934452 | |||
| 12ceceff19 | |||
| 1fdd013e4b | |||
| c719ae360d | |||
| 225310e04b | |||
| f4fb5200a0 | |||
| 0044eb44e2 | |||
| 0e0a82e536 | |||
| 2e1aec4de8 | |||
| fada8b6594 | |||
| 57b7892de6 | |||
| 37b6afaf2c | |||
| 986bb24326 | |||
| 7180c1f2d0 | |||
| 6a30d5a6c1 | |||
| 2fd384ae95 | |||
| c17a98030a | |||
| 90f816595a | |||
| 3332a10052 | |||
| 3fd17e43a3 | |||
| 3faaf9e7fb | |||
| 777f36e604 | |||
| 2abbedcc50 | |||
| 11681f02cc | |||
| 5f67c6050d | |||
| 9831bc6bc9 | |||
| 488928eca2 | |||
| f06e6be2c1 | |||
| eaa17b7f7c | |||
| 6104685ad6 | |||
| 06253c8489 | |||
| 75221a6078 | |||
| 2487a03648 | |||
| 43b75ccf04 | |||
| 98f2967aac | |||
| fa134337d9 | |||
| 3a7f5b970f | |||
| 7056b5fc70 | |||
| 68a8efdc33 | |||
| fb30c8a937 | |||
| 5270dc1c9e | |||
| 4069470585 | |||
| 2058d024ce | |||
| d56c85fe01 | |||
| 1cf62e9ded | |||
| 586c0ea1d0 | |||
| 36d4614d12 | |||
| dfacdd8a40 | |||
| 5ee351234c | |||
| eba2216c4a | |||
| d14c8a03b3 | |||
| 966c3fea5b | |||
| aed83e69c9 | |||
| 6e08d8e746 | |||
| c9d8cd3613 | |||
| da5385686c | |||
| db40cbda58 | |||
| 56ea45f9d5 | |||
| 6829fa0286 | |||
| f588454d26 | |||
| 1b37e775be | |||
| 8ca6c9fff0 | |||
| 430e2243ac | |||
| 73d35b0236 | |||
| 4ab777b429 | |||
| 48416e81eb | |||
| 90688c4970 | |||
| 8cb0f5d2dd | |||
| 8540399ecb | |||
| 623f1b35f6 | |||
| d8424a1f35 | |||
| e52d338552 | |||
| 83b6c98563 | |||
| e361e0daa6 | |||
| 39272eef45 | |||
| cc846eb670 | |||
| 49e5212a8f | |||
| 76da3fa907 | |||
| 9693d1c3a3 | |||
| 348db050ae | |||
| 62f97f2e5f | |||
| fa723f6dc4 | |||
| 4174d7274d | |||
| a92ae827c1 | |||
| 6586090307 | |||
| eacd1b8f13 | |||
| 1b740995a4 | |||
| a8a9c11bf8 | |||
| f6f72c1cf8 | |||
| bc87c5808c | |||
| 4bef0f8dab | |||
| c9adb52f63 | |||
| 1456678316 | |||
| 19a64e0e94 | |||
| 3359e9a51b | |||
| 7a024f3355 | |||
| 8a1b5abee1 | |||
| 4a252e750f | |||
| da6ce80f4f | |||
| 72ba062b1a | |||
| a47ebad24c | |||
| 306f95a9ae | |||
| 4ef384a161 | |||
| 680aaf21f0 | |||
| b2f29cd4d4 | |||
| bb524db236 | |||
| a605dd0a3f | |||
| a8a8cd88a0 | |||
| 9e5b633779 | |||
| bdf85ed900 | |||
| 6a9f234dc7 | |||
| f056ef7a80 | |||
| 5b0ed1e2eb | |||
| 8e268aedc7 | |||
| f72157bcec | |||
| dab30c0051 | |||
| a3d44b5e1f | |||
| 699eadc84f | |||
| 35139ebf5d | |||
| 8b3d1c2c91 | |||
| f3029b2b42 | |||
| a07cbaca8e | |||
| 13faf4e816 | |||
| 06572c6011 | |||
| e0a676a3fe | |||
| e8474145a5 | |||
| b7e010bbd0 | |||
| 0da556e1f8 | |||
| efb3b08f87 | |||
| 1c8fc6852a | |||
| 1133fbb017 | |||
| dafc914bcc | |||
| 2b6e6a9ae9 | |||
| fc486a1bac | |||
| 23dc9d54e3 | |||
| a21bdb9f45 | |||
| afa7eacfac | |||
| dfd3035955 | |||
| 1a6c819176 | |||
| e6b68b260a | |||
| a8ea576b22 | |||
| 972de4c730 | |||
| 59090f6d26 | |||
| dce14a359a | |||
| 26da91c972 | |||
| 11e297c975 | |||
| 9a457400e7 | |||
| 566c88802e | |||
| d6dc1493cb | |||
| 9f61fb5a78 | |||
| 7aef811ff7 | |||
| e37da61a1c | |||
| 1a361e1f29 | |||
| 05cd3712a3 | |||
| 4cebf431fd | |||
| a64beb583b | |||
| 9de31fc5ce | |||
| 0d4f0f95cc | |||
| ed8aa774bb | |||
| b94e98aed5 | |||
| c5676e5649 | |||
| f2229a5f53 | |||
| d347145855 | |||
| 445e5def42 | |||
| e8b5555630 | |||
| 16dcbe8c74 | |||
| ed7012c9db | |||
| d24f9194f7 | |||
| b9ad578b00 | |||
| d28c0d16f8 | |||
| 4cb98014a2 | |||
| cd11ae5d8b | |||
| efbd77deb4 | |||
| 7c4693e271 | |||
| 89ddeda24a | |||
| f294729b28 | |||
| 635ecbeb90 | |||
| 3072571a4f | |||
| c8c8ce5e9e | |||
| f413996362 | |||
| 2226125728 | |||
| 3ac1b46df2 | |||
| 5237779136 | |||
| fb5cdf9769 | |||
| ed9fb8405b | |||
| ee96807305 | |||
| 99ffc3fca9 | |||
| d750e955c3 | |||
| ea4a3b2476 | |||
| 4e09573937 | |||
| b454dde871 | |||
| d7b0344091 | |||
| 27474316fd | |||
| 46812d46ba | |||
| 66610a28af | |||
| 346b97f871 | |||
| 343656f66d | |||
| 549a5370d8 | |||
| 2834c88de7 | |||
| a745f02a07 | |||
| f83d381e1e | |||
| c4cff9b543 | |||
| d7bb59cd99 | |||
| 7724161c14 | |||
| 02ba529eea | |||
| ca5158a03e | |||
| 5e556a87ff | |||
| ce4c4696dd | |||
| 7bccdbf157 | |||
| 8abe6489ed | |||
| a6d06b2e20 | |||
| 01b4c68238 | |||
| 69f0ad0d68 | |||
| dea5193fd7 | |||
| 1175658bd5 | |||
| 2e0ecb556c | |||
| 5a08b690d5 | |||
| 815f4aa63b | |||
| e23435a190 | |||
| 85d38b5751 | |||
| 6399833182 | |||
| 9acaee563b | |||
| bf89b709cb | |||
| c2973d2b82 | |||
| 7d0e94ab0a | |||
| 6c62d042c5 | |||
| 79cf2832ae | |||
| cd6bf37d06 | |||
| 85fa0942e7 | |||
| c49c7de427 | |||
| f933947a27 | |||
| e53ca429a1 | |||
| 1fbd918d59 | |||
| 685eaedbf9 | |||
| 4357892081 | |||
| 4c1459534d | |||
| 686d3eb1df | |||
| 00b31c27a3 | |||
| 7208aa37cc | |||
| d4285fb196 | |||
| f9cab3270f | |||
| b5dbd44087 | |||
| 37ee0a2f55 | |||
| fc50e6abcb | |||
| c452b43ebc | |||
| eaf20295ac | |||
| acf1286b49 | |||
| 38a72cc6ee | |||
| e279bdb304 | |||
| 23f5cee653 | |||
| 7b4f70390d | |||
| 93a9201316 | |||
| 84abb9923a | |||
| 363811981d | |||
| 57f394e156 | |||
| 1d86c60548 | |||
| a93a40e8d9 | |||
| 955be34914 | |||
| 1c6c24f8ac | |||
| 923314aef5 | |||
| bb19feca96 | |||
| 6bbec61856 | |||
| c30a7c0041 | |||
| 3aa6741b8c | |||
| 56a165ecdc | |||
| 5afd12e8a4 | |||
| 76bac80dde | |||
| 6cc7d1fb26 | |||
| fba4dbf498 | |||
| 21e1911112 | |||
| 76f102e865 | |||
| b095a0be90 | |||
| f0a91484a0 | |||
| c6e35706a3 | |||
| f3d3a1a4a5 | |||
| 874d18f761 | |||
| 56d553f10d | |||
| 18ec05c070 | |||
| 158019b50f | |||
| d1d1b2effd | |||
| 6fc869e21b | |||
| 25ec3789fe | |||
| 386e301ea0 | |||
| e8d144f399 | |||
| 6ec4a88eb5 | |||
| 182c04b59d | |||
| 7cf82d2511 | |||
| 7915dcb23c | |||
| dd21e43ed5 | |||
| 937f1e8cd0 | |||
| 4b2b662c3a | |||
| ec31157bc7 | |||
| 8c93db342f | |||
| cca488d0cf | |||
| f8a9b1fa30 | |||
| ba5b59dc59 | |||
| 0f36e87010 | |||
| 38191c3013 | |||
| 741ee9ac41 | |||
| cc7848b3ef | |||
| e9d6096356 | |||
| 373e02e91c | |||
| 1200ca8172 | |||
| d0165290b6 | |||
| 0267221586 | |||
| 9f0fada17a | |||
| 8800c10476 | |||
| 3b47e43380 | |||
| 23b52e1b1c | |||
| fa6a5761b3 | |||
| b534d49e48 | |||
| f82ce06fdc | |||
| 9d8c9c2460 | |||
| 061c787e56 | |||
| 6c62f42d8d | |||
| 5e20d4b481 | |||
| a3daa99a33 | |||
| aa4040bb7b | |||
| 9578e1e923 | |||
| 62f9a2dc4b | |||
| 441541a42f | |||
| 57ba9d3939 | |||
| 1a5b950d8b | |||
| aaea9d6946 | |||
| 449888b9db | |||
| f349695a75 | |||
| 9e380a4a4a | |||
| c80eb1ba02 | |||
| 801089cf44 | |||
| 359bfa88b7 | |||
| fdd4f03b93 | |||
| d1fd4d5000 | |||
| 14e6577700 | |||
| 711e0f75ee | |||
| d01bf3bcb0 | |||
| d3ad44aec4 | |||
| a424fcfcf7 | |||
| fdcc5adc8d | |||
| 745ed8331c | |||
| 05c1f192f5 | |||
| 451b3f3c9d | |||
| 1625f4bcc9 | |||
| 69af607491 | |||
| 456aa4ab8d | |||
| f8c8a3a26c | |||
| 27820f0371 | |||
| bff038f554 | |||
| 6629983ca7 | |||
| 56a9966d69 | |||
| 8cb749110f | |||
| 4f8b131e64 | |||
| 52e1a06f8c | |||
| d057c79733 | |||
| f82739afb0 | |||
| 2211e95e41 | |||
| 67aff85e8e | |||
| bb8fa28521 | |||
| eecb984689 | |||
| 451d64407b | |||
| f8d396ac9e | |||
| 7788007cbd | |||
| ab84a67511 | |||
| 3ed98be6cd | |||
| 50dd704f2b | |||
| 525c257c6a | |||
| b0f464d110 | |||
| 01d8c3f616 | |||
| e29ea61f7c | |||
| f84883223b | |||
| c39bd490ba | |||
| 2f6957c9c8 | |||
| 847da3951c | |||
| b0b4998050 | |||
| f6a3659a14 | |||
| e58b7cbe1f | |||
| 9ca8ec8ec7 | |||
| 622c8c2788 | |||
| eef900ef41 | |||
| 68202eb2b7 | |||
| 948bd3673e | |||
| c5c0f68b3e | |||
| 19d808d378 | |||
| 8695f95267 | |||
| 8b693ed7bb | |||
| d13e71eeb8 | |||
| 52bfe6d4fb | |||
| 12b8908a00 | |||
| 9187911a29 | |||
| 89fe3705a4 | |||
| 9b4e63df6d | |||
| 03e6725fcf | |||
| d2d07abcc5 | |||
| 6caffd0e32 | |||
| 5828367013 | |||
| 8bfe4c908c | |||
| 60a276e9ce | |||
| 05c5763492 | |||
| 20e9ae069c | |||
| 064d0619e8 | |||
| a19113025c | |||
| 29270ed673 | |||
| de693b0da1 | |||
| 65d270e5d8 | |||
| e18e6576f3 | |||
| b36d0fd59f | |||
| b09321354a | |||
| 4babe9076b | |||
| 46abe0f315 | |||
| c35d0d16e7 | |||
| 77e2af8c00 | |||
| e7f30640ef | |||
| ac99fd764d | |||
| fa55a8072c | |||
| c5b2469fe7 | |||
| d3596fbc95 | |||
| a5d4e58ddd | |||
| c5dcef52fa | |||
| d266a25618 | |||
| 1ddcad4fa4 | |||
| 1fdc070e4e | |||
| 1b0f5b1ed9 | |||
| 2ed6e51d24 | |||
| 733efc6b0d | |||
| f1e6532ec0 | |||
| 1c9769670c | |||
| 0d72889c52 | |||
| f0a9799b4b | |||
| 4d40ed9dbd | |||
| d78957d1cf | |||
| 2c34195954 | |||
| 2ca935a26b | |||
| bd915bfebd | |||
| 66854b78fa | |||
| dceba8a92b | |||
| d856dc89a8 | |||
| 2f0dc9bd0f | |||
| 9d9869e803 | |||
| caf827344f | |||
| b9c026d441 | |||
| 2b9fa7ed23 | |||
| 253e8b4466 | |||
| 622f2f7f66 | |||
| c959ab2698 | |||
| 20900ebea2 | |||
| a0c0d8b993 | |||
| afd5af04f6 | |||
| 712632d28a | |||
| c1385558ad | |||
| 0910acad42 | |||
| 513bac0304 | |||
| a0bf103e8b | |||
| 41eab62ed7 | |||
| e009bbac10 | |||
| 1ac203a958 | |||
| c86dc133cf | |||
| 8ad05ce10e | |||
| a999bccc03 | |||
| ded9d1ddfd | |||
| 88d0316aad | |||
| 6a62de6d68 | |||
| 544f8711f0 | |||
| b9c9bb30ba | |||
| c69bb3ca04 | |||
| e6d49798ce | |||
| 3a5ce90148 | |||
| 1270ee4b38 | |||
| 62cec24235 | |||
| 124122839d | |||
| 20d653b58a | |||
| ed4caf0fbe | |||
| ff9024ae70 | |||
| 01dc09c436 | |||
| 19514128e3 | |||
| 23b10fc16d | |||
| 4bddb6a9e3 | |||
| cb3933dcab | |||
| 4db79c458d | |||
| 24699f0f96 | |||
| 3b64e0fb3f | |||
| cd59bc3d6d | |||
| e2ee959ac8 | |||
| 3d8223fccc | |||
| 5d3b0e7352 | |||
| ca30131faa | |||
| a545e23b00 | |||
| 345f92ab0a | |||
| 42851a93d4 | |||
| 24f4c92759 | |||
| c3d75e208f | |||
| df2137781d | |||
| 1e375e5210 | |||
| 1730708f47 | |||
| bda65f93a6 | |||
| d484253136 | |||
| 5fb4009a07 | |||
| f45dcfe18a | |||
| 278cb52f19 | |||
| 3b8eab87dd | |||
| b9fd6ad37d | |||
| 00c9b323c2 | |||
| e2c288ff8a | |||
| edeb858138 | |||
| 095d02e41b | |||
| b3a933ba84 | |||
| b456955688 | |||
| 3935cef691 | |||
| 83f096c435 | |||
| acd83673e9 | |||
| 6229b3ca8d | |||
| 8b449ce76c | |||
| d62b0c88b0 | |||
| cd92d9bcdf | |||
| b0cc1cf472 | |||
| 78ef205b4f | |||
| 4464951c77 | |||
| 64c04769d2 | |||
| dada341d4e | |||
| a96a8a9db0 | |||
| dab82b70f7 | |||
| c3916fee9c | |||
| e7a02f1506 | |||
| 68bb1bd6ee | |||
| cad017a64f | |||
| 81e5f3b7f2 | |||
| e4da284176 | |||
| 3e433ef05c | |||
| 8f1c36d79f | |||
| c776ff5020 | |||
| 0ffaf6c1ce | |||
| 4419e5d838 | |||
| 27a94308a7 | |||
| 0c4cdaa7d3 | |||
| 87194e5887 | |||
| 392a4bdc0b | |||
| dd0684b58a | |||
| 58f4b26134 | |||
| 84d1cf5065 | |||
| 07731d0228 | |||
| 44449dd9e9 | |||
| 9d431e77a4 | |||
| c60f7b89ba | |||
| 0d61bae1b9 | |||
| d77e62c94e | |||
| 2afcf369ec | |||
| 0d5c071eee | |||
| dd4c13b625 | |||
| af62c92b7f | |||
| af71cf0922 | |||
| 2bc4cb4e58 | |||
| b2a4e759fc | |||
| 903da117e5 | |||
| 9f420081fd | |||
| ddded7ba5c | |||
| 79dd62cda2 | |||
| d013524ea9 | |||
| 61a517cc1a | |||
| fddd39d508 | |||
| 661d19e33f | |||
| 21a7f1a550 | |||
| 6f3a63df94 | |||
| 968f3995ff | |||
| f491dc1413 | |||
| 14dc1fead6 | |||
| ea8397fbab | |||
| 3a9778668a | |||
| 4db0cf005b | |||
| 67a6db8325 | |||
| 09c8162235 | |||
| cc22d25490 | |||
| 88b6d51643 | |||
| eb081bbcfa | |||
| 0a58464f01 | |||
| 626836f95b | |||
| a8f19fbfae | |||
| 2b8cab906c | |||
| 88bfdb0c05 | |||
| 3dd45fc859 | |||
| 53ca2526ed | |||
| db9a6113d7 | |||
| 5454f57dd0 | |||
| 4003aacc6a | |||
| 17250bc2d4 | |||
| b9b6bd85a6 | |||
| edc9e9b4ec | |||
| 7aa2cddb05 | |||
| 38e88253cc | |||
| da9affb348 | |||
| 00eaf39c01 | |||
| 63f803b68a | |||
| c9e367befd | |||
| 1c5667f0ce | |||
| e9c3ab7ae6 | |||
| 1b9c9267f0 | |||
| fc18e17ba6 | |||
| 3150619320 | |||
| 0f24379c0e | |||
| fb20c27e7b | |||
| 7b5dcd9d6a | |||
| dde4a9c6e7 | |||
| 73f7ab29bc | |||
| 83b09cf047 | |||
| 824ad4fea7 | |||
| 5c9d0e4b46 | |||
| 5d4c5ecc1c | |||
| 12eee6a19e | |||
| 11bdb4102a | |||
| df489d786a | |||
| ec6d1fb1ba | |||
| a5f4e6ecef | |||
| 9fe03a94d8 | |||
| 7242387a42 | |||
| 1e107487da | |||
| 05e24f38f9 | |||
| ea1bb3b90e | |||
| 4d3b60cf46 | |||
| 311cd0ce76 | |||
| 84ae643692 | |||
| 9dea602496 | |||
| 39e3fb4638 | |||
| dcd7f442fa | |||
| 400079a006 | |||
| 87b510d5bf | |||
| 9f9e1b5fe0 | |||
| b0ee369135 | |||
| a2253e840f | |||
| 37577714fa | |||
| 759370ea1b | |||
| 55e9d437a9 | |||
| bdf6545571 | |||
| bebb1bdeda | |||
| 1193ab0b89 | |||
| 944e89058e | |||
| c5709f71b3 | |||
| cc50d503ef | |||
| cf2cb7978b | |||
| f543fb0243 | |||
| 6362942928 | |||
| 3d632a3774 | |||
| c8d8a81ccd | |||
| c2e17d04e1 | |||
| 33b01d9306 | |||
| 8981227644 | |||
| 5defa9c7b0 | |||
| 4576a31f47 | |||
| 8826948c43 | |||
| 23b380eb41 | |||
| ce98c962f8 | |||
| c2b2dee56d | |||
| 97515a0941 | |||
| 0a7902d313 | |||
| 8eab9fe36c | |||
| 824340363d | |||
| d48f391b41 | |||
| 83f9be2524 | |||
| be13a776f4 | |||
| 2adb2ebb00 | |||
| 2bfafe4b0d | |||
| fed94e3823 | |||
| c09c792636 | |||
| 0fc67aa2f6 | |||
| 493d4e8613 | |||
| b030d1900c | |||
| f38f61d110 | |||
| 92b5a705d8 | |||
| b957eac6e8 | |||
| d2bf04796f | |||
| f8d999b384 | |||
| fbf6581249 | |||
| d283200e13 | |||
| 3f14bc6961 | |||
| f2a3167193 | |||
| 8fcd5c1e5a | |||
| f8a0be8c3e | |||
| 00f5335895 | |||
| c5d8d50908 | |||
| 3e580afe21 | |||
| 2270b8d15c | |||
| b230b0df0d | |||
| 62efe6e5be | |||
| 7c031c2330 | |||
| 40d2341ef1 | |||
| f1670a9ea0 | |||
| 325048a134 | |||
| bff9a06f9f | |||
| 31101b4828 | |||
| 598008868e | |||
| 72574b11ff | |||
| 182259e4a5 | |||
| c9669818eb | |||
| 6fd402bcf8 | |||
| fb0f5f0c8b | |||
| 7a6db3dd7e | |||
| 295f782b30 | |||
| a33f297315 | |||
| 8d5bbfb850 | |||
| 33a1c743a4 | |||
| f6f45b8699 | |||
| 68fff54a8b | |||
| c61049b76f | |||
| f8bbdc39c5 | |||
| 4c03348e60 | |||
| 05efa7d957 | |||
| e1c9d8cb59 | |||
| 95951c31f8 | |||
| 67637dfaa4 | |||
| bf2f00a163 | |||
| 6c5c89a4a3 | |||
| 4e8ce7fac0 | |||
| 6b3eebc907 | |||
| 9269d8a370 | |||
| 99c52a0d0c | |||
| d1a9afbd67 | |||
| 2b8f96dcec | |||
| 9c025be831 | |||
| aee03114fd | |||
| bb8e89f84a | |||
| 6f0bad34f4 | |||
| d275c36e0a | |||
| 4e5cceb03e | |||
| 67391322f4 | |||
| 0b1c8bc676 | |||
| 39088e5005 | |||
| 7afda26c69 | |||
| 08cd8e0cb1 | |||
| 58e4d49bb5 | |||
| ae0f27a18e | |||
| 06a6cfb9f7 | |||
| 3918a9b9fd | |||
| 8f975e2329 | |||
| 3dfe46fc42 | |||
| 580c8b8803 | |||
| e1e0af5f81 | |||
| 5ef1cab30b | |||
| fdef77d779 | |||
| 2eae06454b | |||
| 29318803a6 | |||
| ff2c05da50 | |||
| cafcac9ccd | |||
| e43afa7148 | |||
| 9a1c0e4577 | |||
| baec681c9e | |||
| 8b54552ddf | |||
| 1cff1ca03e | |||
| 3ba133f485 | |||
| 28e19a1d14 | |||
| 49a4015d5d | |||
| 3e71284d56 | |||
| a68dac7282 | |||
| 253248349f | |||
| afd55da8cb | |||
| 2bb5354fd9 | |||
| 70e2873e69 | |||
| 705525510e | |||
| 367f039523 | |||
| fd525ae1cf | |||
| fd8aae5725 | |||
| 7518200c7e | |||
| b97a9d22f5 | |||
| 196f985283 | |||
| 27004eb934 | |||
| 036448ac3f | |||
| 04ad49de4d | |||
| fc23243d01 | |||
| 2cfb2428d2 | |||
| 21dc05265d | |||
| 819ed801a7 | |||
| a46ef4f29a | |||
| 3c70645f0b | |||
| 84a4671a0e | |||
| a39384cadd | |||
| 4836a03a7d | |||
| ad108720fb | |||
| 9d1f4a2538 | |||
| bff6060bd6 | |||
| 1aa44ede31 | |||
| eccb57d4b8 | |||
| b7979d3f57 | |||
| 440a31ef18 | |||
| a52ea7f870 | |||
| 75b40931fc | |||
| 177da2984d | |||
| 7cb4672344 | |||
| 4c7afb6952 | |||
| a77896c1b6 | |||
| be5c18cc85 | |||
| 737db90058 | |||
| 5d15d64ec8 | |||
| fe4c3c4d84 | |||
| f5ee6fb113 | |||
| acfe22e5bc | |||
| cfa9b163fd | |||
| eae78a8711 | |||
| 88dc6b7700 | |||
| 8f91917e8c | |||
| 1ca51a023c | |||
| a975ecdc4f | |||
| 8b704588d0 | |||
| a7c34123a6 | |||
| 18f9781ea8 | |||
| db229dffb0 | |||
| 16f5167aed | |||
| 767dfa271e | |||
| df453d6d4f | |||
| 2fecddcde2 | |||
| 4d60d85db5 | |||
| f4af3f2987 | |||
| 753d8170d1 | |||
| dca6f1f863 | |||
| ff1b0d73b7 | |||
| 25ec899cfe | |||
| f27f7d50c9 | |||
| 98b715d8c8 | |||
| ffa62918aa | |||
| f3210a9a33 | |||
| 58035697d5 | |||
| 7e32e7343a | |||
| 291a828217 | |||
| 40ae5e605d | |||
| c5d1f30e82 | |||
| 91a6091d26 | |||
| da99f3cb9a | |||
| a42b00488d | |||
| 63d9cb37ec | |||
| 9abbbb79ae | |||
| bb5622401c | |||
| 6d46ac1ad6 | |||
| cf1b700ebc | |||
| c5cd758700 | |||
| bd1d4de422 | |||
| da6afb11f0 | |||
| ef0196d791 | |||
| 841d8955de | |||
| 738a7cac4f | |||
| 9516250bec | |||
| 2418de7e8b | |||
| 00ce814501 | |||
| 9ad5fc8a08 | |||
| 3ffafb741c | |||
| 8ca33bcb94 | |||
| 477522210e | |||
| 633004c820 | |||
| 8f4fa8a5b6 | |||
| 3b24e7e685 | |||
| 3d551482a9 | |||
| c6f44299c3 | |||
| eb3dfb6bb4 | |||
| cecfc59040 | |||
| 6baf675205 | |||
| da6f98f310 | |||
| 2bb0387fc5 | |||
| d957531dab | |||
| 41f4bb9016 | |||
| 69b5f25644 | |||
| 78bb9a7278 | |||
| 7303f04bc5 | |||
| ea82b732a3 | |||
| 64992f758d | |||
| 8a51af0b56 | |||
| c77b310422 | |||
| 0797164fac | |||
| 3681507136 | |||
| 2b5cf2455f | |||
| d392379c7a | |||
| e88508b679 | |||
| 90e0bade77 | |||
| 568118634e | |||
| cfd0b8a975 | |||
| 93b2776604 | |||
| 53f2533a08 | |||
| e291494282 | |||
| b0352d6ef7 | |||
| 8c19ff4fb9 | |||
| da9baac842 | |||
| 0e74f2e340 | |||
| 402ca480cd | |||
| d871675b3b | |||
| bb0cfe383d | |||
| 717f2b0ac8 | |||
| 2bf405e36d | |||
| c42b25f10e | |||
| b9318f73e6 | |||
| ff89f7ca45 | |||
| ed7d514c31 | |||
| d97d30ea2e | |||
| 4899e35fbb | |||
| 92ec846375 | |||
| 755ceb50a4 | |||
| 706faa1d32 | |||
| 4620fa8bd8 | |||
| 1dc87a0704 | |||
| bba7184090 | |||
| d973337c4e | |||
| 898637f830 | |||
| 78dbc4fb5f | |||
| a3db2774f1 | |||
| c3a5e3f491 | |||
| 7359774c76 | |||
| bca46e92d4 | |||
| 2c16dddd1e | |||
| 6d6d6fa827 | |||
| 3e863f2189 | |||
| 2d68aed68f | |||
| ee0fe28ddc | |||
| 7627ad04f1 | |||
| f559c088a1 | |||
| a17025c465 | |||
| 70cc2e3665 | |||
| fca6c4fb7f | |||
| 05fc49506a | |||
| e6cb4df877 | |||
| 2172e61ce5 | |||
| a36acf49d8 | |||
| 5aac8aa424 | |||
| 887ea43e39 | |||
| 9fce5b0f7a | |||
| 22af70d0d0 | |||
| 321c360dcb | |||
| 9bb1ed78f9 | |||
| 9fc1740608 | |||
| c6a2525c9b | |||
| aeebeed0f2 | |||
| 9f416e5ce1 | |||
| 27552891ad | |||
| 5817f9fe3f | |||
| a35bcb131e | |||
| 63fa50080d | |||
| d27a375363 | |||
| fad9cd6ef1 | |||
| 3066a05df0 | |||
| 50446f7fef | |||
| c5caaa9211 | |||
| 32d38a4633 | |||
| 1ae724f739 | |||
| 50ff987850 | |||
| 44cddda4b4 | |||
| d79a544320 | |||
| e5b4710471 | |||
| e6010ba2ca | |||
| fd8d2ecbf4 | |||
| 78cfba158d | |||
| 20785b00f6 | |||
| 2c6c8f1a25 | |||
| 19944eeed0 | |||
| 9d961f620b | |||
| 9fcba58537 | |||
| 9f1fe5775e | |||
| baea538c32 | |||
| c10e195dba | |||
| 5492e0eae1 | |||
| 17d2124a71 | |||
| af89b62194 | |||
| 526228442b | |||
| 4e2a8a3cdb | |||
| 252ddf8085 | |||
| fc3698f831 | |||
| 3426531e38 | |||
| ce674dbc5d | |||
| 92fb828d52 | |||
| d13584f858 | |||
| f66d5adf5f | |||
| b232409cc5 | |||
| da635af0d6 | |||
| 5f047034ae | |||
| ffe6c9ba5a | |||
| 910fc54ee2 | |||
| f00d9a7c7f | |||
| 2dce55d036 | |||
| 315798b1f4 | |||
| dece1e01f4 | |||
| a19ffba8e8 | |||
| e230867492 | |||
| 0c8e4c0887 | |||
| 84b8a08d26 | |||
| 2855869530 | |||
| d41b49d8b4 | |||
| 4804efc17d | |||
| 053ab585f4 | |||
| 20d4c09a1c | |||
| 68b3229756 | |||
| 87741141f1 | |||
| 705820c430 | |||
| 6b23e733d0 | |||
| 07cbf3da48 | |||
| af1b13550b | |||
| 8ccaabb502 | |||
| 01349b8cee | |||
| 4487cc8e7a | |||
| cb9e6e75db | |||
| e487480ca9 | |||
| bc9594ed57 | |||
| 817dfe8651 | |||
| ba54f7ae39 | |||
| f328cdbaef | |||
| f2e5b6c350 | |||
| ac82ecf387 | |||
| 4b1c052030 | |||
| c0a0cb98c5 | |||
| 4fd69a09c7 | |||
| eea2c20420 | |||
| f4d2fccdfe | |||
| c8a6bc6e24 | |||
| 7f542a5c99 | |||
| c1f32aa570 | |||
| 7bbadfd08b | |||
| 3a9428a88e | |||
| 7233640cf0 | |||
| effdb65ae4 | |||
| 91d0511154 | |||
| d4e88aa11c | |||
| ec0cd9f006 | |||
| 8db7ef4d5a | |||
| 504e3112dd | |||
| f96e9b6ed7 | |||
| f056b2f4ab | |||
| 445b7c0758 | |||
| 53f8b03acc | |||
| 900329a1de | |||
| 7c6c33eef8 | |||
| dacc9af38f | |||
| 8d7620d95f | |||
| 3043c73745 | |||
| f786c078c2 | |||
| 31cee7e4c8 | |||
| 6e8ccbcf17 | |||
| 72e54e8b60 | |||
| e0f53ace19 | |||
| 2444385763 | |||
| 6141bc5598 | |||
| 9138d537c8 | |||
| 05354698f0 | |||
| 14e15b2feb | |||
| 324d21d849 | |||
| aa30e865c2 | |||
| 0772b9c879 | |||
| 296052b1ff | |||
| be834a67d1 | |||
| b63461f599 | |||
| 1fb7cc4002 | |||
| a766478e15 | |||
| b61a69c877 | |||
| 7753ac60b0 | |||
| aabf3c6695 | |||
| d78b0467d8 | |||
| dc55d5be8f | |||
| 06546b5195 | |||
| ca9c1674f7 | |||
| ede76033a7 | |||
| 41bd07274f | |||
| 5266dd6c60 | |||
| cf1c45dfe8 | |||
| ae7e275a9d | |||
| b60f29282a | |||
| bda466c3bd | |||
| 4c6ceeff11 | |||
| 77f2b521e1 | |||
| 1b26ec56ed | |||
| 7c0fd5094e | |||
| d5b9eb0caa | |||
| 1a0b2bf050 | |||
| f52fa5e46c | |||
| 77b8f3a0f9 | |||
| 95d232db4f | |||
| 00b1ff752d | |||
| 877b296fc3 | |||
| ee88e7f948 | |||
| ebb9fc7cd9 | |||
| 32f0e91893 | |||
| a8ab408f64 | |||
| 661b8b006f | |||
| 1bd77c2f51 | |||
| c191a98ec3 | |||
| 3bbecd998a | |||
| 3406cc9886 | |||
| bd297d079d | |||
| a5500ebfa4 | |||
| a64bea1286 | |||
| b67f28e00d | |||
| 89c548b5d3 | |||
| 02e571adbc | |||
| 98e50d3064 | |||
| f20fd217f8 | |||
| 95c6ac699b | |||
| 9caceeacd7 | |||
| 2f7f8a629d | |||
| a994c46256 | |||
| c689a9ba8e | |||
| 777cd9a8ae | |||
| 6dd2c78b1b | |||
| 372cb39ce8 | |||
| d2652aedd2 | |||
| fa04ff6d90 | |||
| cce0ad0c35 | |||
| 76be7d40ac |
+3
-5
@@ -11,11 +11,11 @@ BreakConstructorInitializersBeforeComma: true
|
||||
BreakBeforeBinaryOperators: false
|
||||
BreakBeforeTernaryOperators: false
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
AllowShortIfStatementsOnASingleLine: true
|
||||
AllowShortBlocksOnASingleLine: true
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
Cpp11BracedListStyle: true
|
||||
IndentCaseLabels: false
|
||||
SortIncludes: false
|
||||
@@ -29,5 +29,3 @@ BinPackParameters: true
|
||||
AlwaysBreakAfterReturnType: None
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
IndentWrappedFunctionNames: false
|
||||
|
||||
#SpaceAfterTemplateKeyword: false
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
root = true
|
||||
|
||||
[*.{h,cpp,hpp}]
|
||||
charset = utf-8
|
||||
charset = utf-8-bom
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
patreon: Nekotekina
|
||||
custom: https://rpcs3.net/alipay
|
||||
@@ -6,7 +6,7 @@ or our discord:
|
||||
https://discord.me/RPCS3
|
||||
|
||||
PLEASE READ THE GUIDELINES BEFORE OPENING AN ISSUE:
|
||||
https://github.com/RPCS3/rpcs3/blob/master/CONTRIBUTING.md
|
||||
https://github.com/RPCS3/rpcs3/blob/master/.github/CONTRIBUTING.md
|
||||
|
||||
====================================================
|
||||
|
||||
|
||||
+28
-28
@@ -31,7 +31,6 @@
|
||||
*.wav
|
||||
|
||||
/build
|
||||
/bin
|
||||
/lib
|
||||
/tmp
|
||||
/ipch
|
||||
@@ -41,46 +40,31 @@
|
||||
/Vulkan/Vulkan-build
|
||||
/Vulkan/glslang-build
|
||||
|
||||
!/bin
|
||||
/bin/*
|
||||
|
||||
# Themes
|
||||
!/bin/GuiConfigs/
|
||||
/bin/GuiConfigs/*.ini
|
||||
|
||||
/bin/rpcs3.ini
|
||||
/bin/rpcs3.ipdb
|
||||
/bin/rpcs3.iobj
|
||||
/bin/FragmentProgram.txt
|
||||
/bin/VertexProgram.txt
|
||||
/bin/*.hlsl
|
||||
/bin/BreakPoints.dat
|
||||
/bin/textures
|
||||
/bin/*.lib
|
||||
/bin/*.exp
|
||||
rpcs3/git-version.h
|
||||
/bin/GuiConfigs/*.ini.*
|
||||
/bin/GuiConfigs/*.dat
|
||||
/bin/GuiConfigs/*.dat.*
|
||||
|
||||
# Visual Studio Files
|
||||
.vs/*
|
||||
.vscode/*
|
||||
*.ipch
|
||||
*.vspx
|
||||
*.psess
|
||||
*.VC.*
|
||||
*.vcxproj.user
|
||||
|
||||
# Copyrighted files
|
||||
/bin/data/
|
||||
/bin/dev_flash/data/font
|
||||
/bin/dev_flash/sys
|
||||
/bin/dev_flash/vsh
|
||||
|
||||
# Ignore installed games except test homebrews
|
||||
!/bin/dev_hdd0/game/
|
||||
/bin/dev_hdd0/game/*
|
||||
!/bin/dev_hdd0/game/TEST12345/
|
||||
/bin/dev_hdd0/disc/*
|
||||
enc_temp_folder/*
|
||||
CMakeSettings.json
|
||||
|
||||
# Ignore other system generated files
|
||||
bin/dev_hdd0/*.txt
|
||||
x64/*
|
||||
rpcs3/x64/*
|
||||
rpcs3-tests/x64/*
|
||||
rpcs3/git-version.h
|
||||
|
||||
# cmake
|
||||
Makefile
|
||||
@@ -92,8 +76,24 @@ CMakeCache.txt
|
||||
rpcs3/cotire/*
|
||||
rpcs3/rpcs3_*_cotire.cmake
|
||||
|
||||
# kdevelop
|
||||
*.kdev4
|
||||
.kdev4/*
|
||||
|
||||
# Qt
|
||||
moc_*.cpp
|
||||
qrc_resources.cpp
|
||||
qrc_*.cpp
|
||||
rpcs3_automoc.cpp
|
||||
ui_*.h
|
||||
rpcs3/rpcs3_autogen/*
|
||||
|
||||
# QtCreator
|
||||
CMakeLists.txt.user
|
||||
|
||||
# CLion
|
||||
/.idea/*
|
||||
/cmake-build-*/
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
|
||||
+22
-12
@@ -1,6 +1,7 @@
|
||||
[submodule "rpcs3-ffmpeg"]
|
||||
path = 3rdparty/ffmpeg
|
||||
url = https://github.com/hrydgard/ppsspp-ffmpeg
|
||||
ignore = dirty
|
||||
[submodule "asmjit"]
|
||||
path = asmjit
|
||||
url = https://github.com/kobalicek/asmjit
|
||||
@@ -9,36 +10,45 @@
|
||||
path = llvm
|
||||
url = https://github.com/RPCS3/llvm
|
||||
branch = release_60
|
||||
ignore = dirty
|
||||
[submodule "GSL"]
|
||||
path = 3rdparty/GSL
|
||||
url = https://github.com/Microsoft/GSL.git
|
||||
[submodule "libpng"]
|
||||
path = 3rdparty/libpng
|
||||
url = https://github.com/RPCS3/libpng
|
||||
ignore = dirty
|
||||
[submodule "Vulkan/glslang"]
|
||||
path = Vulkan/glslang
|
||||
url = https://github.com/KhronosGroup/glslang.git
|
||||
[submodule "Vulkan/Vulkan-LoaderAndValidationLayers"]
|
||||
path = Vulkan/Vulkan-LoaderAndValidationLayers
|
||||
url = https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers
|
||||
[submodule "Utilities/yaml-cpp"]
|
||||
path = Utilities/yaml-cpp
|
||||
url = https://github.com/RPCS3/yaml-cpp
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/cereal"]
|
||||
path = 3rdparty/cereal
|
||||
url = https://github.com/USCiLab/cereal.git
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/zlib"]
|
||||
path = 3rdparty/zlib
|
||||
url = https://github.com/madler/zlib
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/hidapi"]
|
||||
path = 3rdparty/hidapi
|
||||
url = https://github.com/RPCS3/hidapi
|
||||
branch = master
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/Optional"]
|
||||
path = 3rdparty/Optional
|
||||
url = https://github.com/akrzemi1/Optional.git
|
||||
[submodule "3rdparty/pugixml"]
|
||||
path = 3rdparty/pugixml
|
||||
url = https://github.com/zeux/pugixml
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/xxHash"]
|
||||
path = 3rdparty/xxHash
|
||||
url = https://github.com/Cyan4973/xxHash
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/yaml-cpp"]
|
||||
path = 3rdparty/yaml-cpp
|
||||
url = https://github.com/jbeder/yaml-cpp.git
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/libpng"]
|
||||
path = 3rdparty/libpng
|
||||
url = https://github.com/glennrp/libpng.git
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/libusb"]
|
||||
path = 3rdparty/libusb
|
||||
url = https://github.com/RPCS3/libusb.git
|
||||
ignore = dirty
|
||||
|
||||
+29
-118
@@ -1,124 +1,35 @@
|
||||
language: cpp
|
||||
sudo: required
|
||||
|
||||
os:
|
||||
- linux
|
||||
#- osx
|
||||
|
||||
# osx_image: xcode6.4
|
||||
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
|
||||
cache:
|
||||
ccache: true
|
||||
directories:
|
||||
- $HOME/hombebrew_cache
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- os: osx
|
||||
compiler: gcc
|
||||
include:
|
||||
# - os: linux
|
||||
# env:
|
||||
# - NAME="Linux build"
|
||||
# - COMPILER="gcc"
|
||||
# services: docker
|
||||
# cache: ccache
|
||||
# install: "docker pull rpcs3/rpcs3-travis-trusty:1.1"
|
||||
# script: 'travis_wait docker run -v $(pwd):/rpcs3 -v "$HOME/.ccache":/root/.ccache --env-file .travis/travis.env rpcs3/rpcs3-travis-trusty:1.1 /bin/bash -ex /rpcs3/.travis/build-linux.bash'
|
||||
- os: linux
|
||||
env:
|
||||
- NAME="Linux build"
|
||||
- COMPILER="clang"
|
||||
- DEPLOY_APPIMAGE="true"
|
||||
services: docker
|
||||
cache: ccache
|
||||
install: "docker pull rpcs3/rpcs3-travis-trusty:1.1"
|
||||
script: 'travis_wait docker run -v $(pwd):/rpcs3 -v "$HOME/.ccache":/root/.ccache --env-file .travis/travis.env rpcs3/rpcs3-travis-trusty:1.1 /bin/bash -ex /rpcs3/.travis/build-linux.bash'
|
||||
- os: osx
|
||||
osx_image: xcode10.2
|
||||
addons:
|
||||
homebrew:
|
||||
packages:
|
||||
- ccache
|
||||
- glew
|
||||
- ninja
|
||||
- qt
|
||||
script: "/bin/bash -ex .travis/build-mac.bash"
|
||||
cache: ccache
|
||||
|
||||
git:
|
||||
depth: false # Unshallow clone to obtain proper GIT_VERSION
|
||||
submodules: false
|
||||
|
||||
before_install:
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$CXX" = "g++" ]; then
|
||||
export CXX="g++-5" CC="gcc-5" CXXFLAGS="-Wno-format-security";
|
||||
fi;
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
wget http://download.qt.io/official_releases/online_installers/qt-unified-linux-x64-online.run ;
|
||||
chmod a+x ./qt-unified-linux-x64-online.run ;
|
||||
export QT_QPA_PLATFORM=minimal ;
|
||||
travis_wait 60 ./qt-unified-linux-x64-online.run --script qt-installer-noninteractive.qs --no-force-installations --verbose ;
|
||||
fi;
|
||||
|
||||
# Install updated libglew-dev since the version provided by trusty is outdated
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
wget https://mirrors.kernel.org/ubuntu/pool/universe/g/glew/libglew-dev_2.0.0-5_amd64.deb;
|
||||
wget https://mirrors.kernel.org/ubuntu/pool/universe/g/glew/libglew2.0_2.0.0-5_amd64.deb;
|
||||
wget https://mirrors.kernel.org/ubuntu/pool/universe/v/vulkan/libvulkan1_1.0.61.1+dfsg1-1ubuntu1~16.04.1_amd64.deb;
|
||||
wget https://mirrors.kernel.org/ubuntu/pool/universe/v/vulkan/libvulkan-dev_1.0.61.1+dfsg1-1ubuntu1~16.04.1_amd64.deb;
|
||||
sudo dpkg -i libglew2.0_2.0.0-5_amd64.deb libglew-dev_2.0.0-5_amd64.deb libvulkan1_1.0.61.1+dfsg1-1ubuntu1~16.04.1_amd64.deb libvulkan-dev_1.0.61.1+dfsg1-1ubuntu1~16.04.1_amd64.deb;
|
||||
else
|
||||
brew update;
|
||||
brew install ccache glew llvm40;
|
||||
fi;
|
||||
|
||||
before_script:
|
||||
- git submodule update --init asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng Utilities/yaml-cpp 3rdparty/cereal 3rdparty/hidapi 3rdparty/Optional Vulkan/glslang Vulkan/Vulkan-LoaderAndValidationLayers
|
||||
- mkdir build
|
||||
- cd build
|
||||
- export CMAKE_PREFIX_PATH=~/Qt/5.10.0/gcc_64/lib/cmake
|
||||
- export CXXFLAGS="$CXXFLAGS -DTRAVIS=true";
|
||||
- if [ "$TRAVIS_PULL_REQUEST" = false ]; then
|
||||
export CXXFLAGS="$CXXFLAGS -DBRANCH=$TRAVIS_REPO_SLUG/$TRAVIS_BRANCH";
|
||||
else
|
||||
export CXXFLAGS="$CXXFLAGS -DBRANCH=$TRAVIS_REPO_SLUG/$TRAVIS_BRANCH/#$TRAVIS_PULL_REQUEST";
|
||||
fi;
|
||||
- cmake .. -DCMAKE_INSTALL_PREFIX=/usr -G Ninja;
|
||||
- ninja
|
||||
- # AppImage generation
|
||||
- if [ -n "$UPLOAD_URL" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$CC" = "clang" ] && [ "$TRAVIS_PULL_REQUEST" = false ]; then
|
||||
export LD_LIBRARY_PATH=~/Qt/5.10.0/gcc_64/lib;
|
||||
DESTDIR=appdir ninja install ; find appdir/ ;
|
||||
find ../bin ;
|
||||
wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage" ;
|
||||
chmod a+x linuxdeployqt*.AppImage ;
|
||||
export PATH=~/Qt/5.10.0/gcc_64/bin/:${PATH} ;
|
||||
./linuxdeployqt*.AppImage --appimage-extract ;
|
||||
./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -bundle-non-qt-libs ;
|
||||
mkdir ./appdir/usr/plugins/xcbglintegrations/ ;
|
||||
mkdir ./appdir/usr/plugins/imageformats/ ;
|
||||
cp ~/Qt/5.10.0/gcc_64/lib/libQt5Svg.so.5 ./appdir/usr/lib/ ;
|
||||
cp ~/Qt/5.10.0/gcc_64/plugins/xcbglintegrations/* ./appdir/usr/plugins/xcbglintegrations/ ;
|
||||
cp ~/Qt/5.10.0/gcc_64/plugins/imageformats/* ./appdir/usr/plugins/imageformats/ ;
|
||||
cp ~/Qt/5.10.0/gcc_64/plugins/platforms/* ./appdir/usr/plugins/platforms/ ;
|
||||
rm ./appdir/usr/lib/libfreetype.so.6 ;
|
||||
export PATH=${TRAVIS_BUILD_DIR}/build/squashfs-root/usr/bin/:${PATH} ;
|
||||
./squashfs-root/usr/bin/appimagetool ${TRAVIS_BUILD_DIR}/build/appdir ;
|
||||
find ./appdir -executable -type f -exec ldd {} \; | grep " => /usr" | cut -d " " -f 2-3 | sort | uniq ;
|
||||
curl ${UPLOAD_URL}${TRAVIS_COMMIT:0:7}-${TRAVIS_BUILD_NUMBER}_linux64 --upload-file ./RPCS3*.AppImage;
|
||||
fi;
|
||||
|
||||
script:
|
||||
# Add a command to show all the variables. May be useful for debugging Travis.
|
||||
#- echo "--Shell Export Lists START--" ; export -p; echo "--Shell Export Lists STOP--";
|
||||
# And to ensure the versions of toolchain
|
||||
- echo "--CXX version?"; "$CXX" --version; echo "--CXX version confirmed";
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-trusty-4.0
|
||||
- sourceline: 'ppa:jonathonf/binutils' # We need to update binutils to a newer version to link against the ffmpeg libs on.
|
||||
packages:
|
||||
- binutils
|
||||
- cmake
|
||||
- ninja-build
|
||||
- libasound2-dev
|
||||
- libopenal-dev
|
||||
- freeglut3-dev
|
||||
#- libglew-dev
|
||||
#- libvulkan1
|
||||
#- libvulkan-dev
|
||||
- libc6-dev
|
||||
- llvm-4.0
|
||||
- llvm-4.0-dev
|
||||
# Clang 5.0 is now bundled in travis, so we no longer need the ppa version.
|
||||
#- clang-4.0
|
||||
- libedit-dev
|
||||
- g++-5
|
||||
- gcc-5
|
||||
- libstdc++-5-dev
|
||||
- lib32stdc++6
|
||||
- zlib1g-dev
|
||||
# We need to install qt 5.10.0 manually because the version trusty provides is too old.
|
||||
#- qtbase5-dev
|
||||
- libudev-dev
|
||||
- libevdev-dev
|
||||
- libpulse-dev
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#!/bin/env bash -ex
|
||||
shopt -s nocasematch
|
||||
|
||||
# Setup Qt variables
|
||||
export QT_BASE_DIR=/opt/qt${QTVERMIN}
|
||||
export PATH=$QT_BASE_DIR/bin:$PATH
|
||||
export LD_LIBRARY_PATH=$QT_BASE_DIR/lib/x86_64-linux-gnu:$QT_BASE_DIR/lib
|
||||
|
||||
cd rpcs3
|
||||
|
||||
git submodule update --quiet --init asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng 3rdparty/cereal 3rdparty/hidapi 3rdparty/xxHash 3rdparty/yaml-cpp 3rdparty/libusb Vulkan/glslang
|
||||
|
||||
# Download pre-compiled llvm libs
|
||||
curl -sLO https://github.com/RPCS3/llvm/releases/download/continuous-linux-master/llvmlibs-linux.tar.gz
|
||||
mkdir llvmlibs
|
||||
tar -xzf ./llvmlibs-linux.tar.gz -C llvmlibs
|
||||
|
||||
mkdir build ; cd build
|
||||
|
||||
if [ $COMPILER = "gcc" ]; then
|
||||
# These are set in the dockerfile
|
||||
export CC=${GCC_BINARY}
|
||||
export CXX=${GXX_BINARY}
|
||||
else
|
||||
export CC=${CLANG_BINARY}
|
||||
export CXX=${CLANGXX_BINARY}
|
||||
fi
|
||||
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_LLVM_SUBMODULE=OFF -DLLVM_DIR=llvmlibs/lib/cmake/llvm/ -DUSE_NATIVE_INSTRUCTIONS=OFF -G Ninja
|
||||
|
||||
ninja; build_status=$?;
|
||||
|
||||
cd ..
|
||||
# If it compiled succesfully let's deploy
|
||||
if [ $build_status -eq 0 ] && [ -n "$GITHUB_TOKEN" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = false ]; then /bin/bash -ex .travis/deploy-linux.bash ; fi
|
||||
@@ -0,0 +1,22 @@
|
||||
export CCACHE_SLOPPINESS=pch_defines,time_macros
|
||||
export CMAKE_PREFIX_PATH=/usr/local/opt/qt5/
|
||||
export PATH="/usr/local/opt/ccache/libexec:$PATH"
|
||||
|
||||
# Setup vulkan and gfx-rs/portability
|
||||
curl -sLO https://github.com/gfx-rs/portability/releases/download/latest/gfx-portability-macos-latest.zip
|
||||
unzip -: gfx-portability-macos-latest.zip
|
||||
curl -sLO https://github.com/KhronosGroup/Vulkan-Headers/archive/sdk-1.1.106.0.zip
|
||||
unzip -: sdk-*.zip
|
||||
mkdir vulkan-sdk
|
||||
ln -s ${PWD}/Vulkan-Headers*/include vulkan-sdk/include
|
||||
mkdir vulkan-sdk/lib
|
||||
cp target/release/libportability.dylib vulkan-sdk/lib/libVulkan.dylib
|
||||
# Let macdeployqt locate and install Vulkan library
|
||||
install_name_tool -id ${PWD}/vulkan-sdk/lib/libVulkan.dylib vulkan-sdk/lib/libVulkan.dylib
|
||||
export VULKAN_SDK=${PWD}/vulkan-sdk
|
||||
|
||||
git submodule update --quiet --init asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng 3rdparty/cereal 3rdparty/hidapi 3rdparty/libusb 3rdparty/xxHash 3rdparty/yaml-cpp Vulkan/glslang
|
||||
|
||||
mkdir build; cd build
|
||||
cmake .. -DWITH_LLVM=OFF -DUSE_NATIVE_INSTRUCTIONS=OFF -G Ninja
|
||||
ninja
|
||||
@@ -0,0 +1,53 @@
|
||||
#!/bin/env bash -ex
|
||||
shopt -s nocasematch
|
||||
cd build
|
||||
if [ "$DEPLOY_APPIMAGE" = "true" ]; then
|
||||
DESTDIR=appdir ninja install
|
||||
curl -sLO "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage"
|
||||
chmod a+x linuxdeployqt*.AppImage
|
||||
./linuxdeployqt*.AppImage --appimage-extract
|
||||
./squashfs-root/AppRun ./appdir/usr/share/applications/*.desktop -bundle-non-qt-libs
|
||||
ls ./appdir/usr/lib/
|
||||
rm -r ./appdir/usr/share/doc
|
||||
rm ./appdir/usr/lib/libxcb*
|
||||
cp $(readlink -f /lib/x86_64-linux-gnu/libnsl.so.1) ./appdir/usr/lib/libnsl.so.1
|
||||
export PATH=/rpcs3/build/squashfs-root/usr/bin/:${PATH}
|
||||
|
||||
# Embed newer libstdc++ for distros that don't come with it (ubuntu 14.04, 16.04)
|
||||
mkdir -p appdir/usr/optional/ ; mkdir -p appdir/usr/optional/libstdc++/
|
||||
cp /usr/lib/x86_64-linux-gnu/libstdc++.so.6 ./appdir/usr/optional/libstdc++/
|
||||
rm ./appdir/AppRun
|
||||
curl -sL https://github.com/RPCS3/AppImageKit-checkrt/releases/download/continuous2/AppRun-patched-x86_64 -o ./appdir/AppRun
|
||||
chmod a+x ./appdir/AppRun
|
||||
curl -sL https://github.com/RPCS3/AppImageKit-checkrt/releases/download/continuous2/exec-x86_64.so -o ./appdir/usr/optional/exec.so
|
||||
|
||||
# Compile checker binary for AppImageKit-checkrt
|
||||
|
||||
# This may need updating if you update the compiler or rpcs3 uses newer c++ features
|
||||
# See https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/config/abi/pre/gnu.ver
|
||||
# for which definitions correlate to which CXXABI version.
|
||||
printf "#include <memory>\nint main(){std::make_exception_ptr(0);}" | $CXX -x c++ -o ./appdir/usr/optional/checker -
|
||||
|
||||
# Package it up and send it off
|
||||
./squashfs-root/usr/bin/appimagetool /rpcs3/build/appdir
|
||||
ls
|
||||
COMM_TAG="$(git describe --tags $(git rev-list --tags --max-count=1))"
|
||||
COMM_COUNT="$(git rev-list --count HEAD)"
|
||||
curl -sLO https://github.com/hcorion/uploadtool/raw/master/upload.sh
|
||||
|
||||
mv ./RPCS3*.AppImage rpcs3-${COMM_TAG}-${COMM_COUNT}-${TRAVIS_COMMIT:0:8}_linux64.AppImage
|
||||
|
||||
FILESIZEMB=$(bc <<< "scale=6; $(stat -c %s ./rpcs3*.AppImage)/1000000")
|
||||
SHA256SUM=($(sha256sum ./rpcs3*.AppImage))
|
||||
|
||||
unset TRAVIS_REPO_SLUG
|
||||
REPO_SLUG=RPCS3/rpcs3-binaries-linux \
|
||||
UPLOADTOOL_BODY="$SHA256SUM;${FILESIZEMB}MB"\
|
||||
RELEASE_NAME=build-${TRAVIS_COMMIT}\
|
||||
RELEASE_TITLE=${COMM_TAG}-${COMM_COUNT}\
|
||||
REPO_COMMIT=d812f1254a1157c80fd402f94446310560f54e5f\
|
||||
bash upload.sh rpcs3*.AppImage
|
||||
fi
|
||||
if [ "$DEPLOY_PPA" = "true" ]; then
|
||||
export DEBFULLNAME="RPCS3 Build Bot"
|
||||
fi
|
||||
@@ -0,0 +1,9 @@
|
||||
# Variables set by Travis CI
|
||||
TRAVIS_PULL_REQUEST
|
||||
TRAVIS_BRANCH
|
||||
TRAVIS_COMMIT
|
||||
# Variables for Travis build matrix
|
||||
COMPILER
|
||||
DEPLOY_APPIMAGE
|
||||
# Private Travis CI variables
|
||||
GITHUB_TOKEN
|
||||
Vendored
+378
@@ -0,0 +1,378 @@
|
||||
find_package(PkgConfig)
|
||||
include(ExternalProject)
|
||||
|
||||
if(APPLE)
|
||||
set(PLATFORM_ARCH "macosx/x86_64")
|
||||
elseif(WIN32)
|
||||
set(PLATFORM_ARCH "Windows/x86_64")
|
||||
else()
|
||||
set(PLATFORM_ARCH "linux/x86_64")
|
||||
endif()
|
||||
|
||||
# Dummy target to use when lib isn't available
|
||||
add_library(3rdparty_dummy_lib INTERFACE)
|
||||
|
||||
|
||||
# ZLib
|
||||
if (USE_SYSTEM_ZLIB)
|
||||
find_package(ZLIB QUIET)
|
||||
endif()
|
||||
|
||||
if (NOT ZLIB_FOUND)
|
||||
message(STATUS "Using builtin ZLIB")
|
||||
set(SKIP_INSTALL_ALL ON)
|
||||
add_subdirectory(zlib EXCLUDE_FROM_ALL)
|
||||
set(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/zlib" "${CMAKE_CURRENT_BINARY_DIR}/zlib")
|
||||
set(ZLIB_LIBRARY zlibstatic)
|
||||
endif()
|
||||
|
||||
add_library(3rdparty_zlib INTERFACE)
|
||||
target_link_libraries(3rdparty_zlib INTERFACE ${ZLIB_LIBRARY})
|
||||
target_include_directories(3rdparty_zlib INTERFACE ${ZLIB_INCLUDE_DIR})
|
||||
|
||||
# libPNG
|
||||
# Select the version of libpng to use, default is builtin
|
||||
if (NOT USE_SYSTEM_LIBPNG)
|
||||
# We use libpng's static library and don't need to build the shared library and run the tests
|
||||
set(PNG_SHARED OFF CACHE BOOL "Build shared lib" FORCE)
|
||||
set(PNG_TESTS OFF CACHE BOOL "Build libpng tests" FORCE)
|
||||
set(PNG_BUILD_ZLIB ON CACHE BOOL "ZLIB is already build or package is found" FORCE)
|
||||
set(SKIP_INSTALL_ALL ON)
|
||||
add_subdirectory(libpng EXCLUDE_FROM_ALL)
|
||||
|
||||
set(LIBPNG_TARGET png_static)
|
||||
|
||||
target_include_directories(png_static INTERFACE "${libpng_BINARY_DIR}" "${libpng_SOURCE_DIR}")
|
||||
else()
|
||||
find_package(PNG REQUIRED)
|
||||
|
||||
add_library(3rdparty_system_libpng INTERFACE)
|
||||
target_include_directories(3rdparty_system_libpng INTERFACE ${PNG_INCLUDE_DIR})
|
||||
target_link_libraries(3rdparty_system_libpng INTERFACE ${PNG_LIBRARY})
|
||||
target_compile_definitions(3rdparty_system_libpng INTERFACE ${PNG_DEFINITIONS})
|
||||
|
||||
set(LIBPNG_TARGET 3rdparty_system_libpng)
|
||||
endif()
|
||||
|
||||
|
||||
# pugixml
|
||||
add_subdirectory(pugixml EXCLUDE_FROM_ALL)
|
||||
|
||||
|
||||
# hidapi
|
||||
add_library(3rdparty_hidapi INTERFACE)
|
||||
target_include_directories(3rdparty_hidapi INTERFACE hidapi/hidapi)
|
||||
|
||||
if(APPLE)
|
||||
add_subdirectory(hidapi/mac EXCLUDE_FROM_ALL)
|
||||
target_include_directories(hidapi-mac PUBLIC hidapi/hidapi)
|
||||
|
||||
target_link_libraries(3rdparty_hidapi INTERFACE hidapi-mac "-framework CoreFoundation" "-framework IOKit")
|
||||
elseif(CMAKE_SYSTEM MATCHES "Linux")
|
||||
add_subdirectory(hidapi/linux EXCLUDE_FROM_ALL)
|
||||
target_include_directories(hidapi-hidraw PUBLIC hidapi/hidapi)
|
||||
|
||||
target_link_libraries(3rdparty_hidapi INTERFACE hidapi-hidraw udev)
|
||||
elseif(WIN32)
|
||||
add_subdirectory(hidapi/windows EXCLUDE_FROM_ALL)
|
||||
target_include_directories(hidapi-hid PUBLIC hidapi/hidapi)
|
||||
|
||||
target_link_libraries(3rdparty_hidapi INTERFACE hidapi-hid Shlwapi.lib)
|
||||
else()
|
||||
add_subdirectory(hidapi/libusb EXCLUDE_FROM_ALL)
|
||||
target_include_directories(hidapi-libusb PUBLIC hidapi/hidapi)
|
||||
|
||||
target_link_libraries(3rdparty_hidapi INTERFACE hidapi-libusb usb)
|
||||
endif()
|
||||
|
||||
|
||||
# libusb
|
||||
if(CMAKE_SYSTEM MATCHES "DragonFly|FreeBSD")
|
||||
# Always use system libusb as reference implementation isn't supported
|
||||
add_library(usb-1.0-static INTERFACE)
|
||||
target_link_libraries(usb-1.0-static INTERFACE usb)
|
||||
elseif(MSVC)
|
||||
# Windows time.h defines timespec but doesn't add any flag for it, which makes libusb attempt to define it again
|
||||
add_definitions(-DHAVE_STRUCT_TIMESPEC=1)
|
||||
add_subdirectory(libusb EXCLUDE_FROM_ALL)
|
||||
else()
|
||||
add_subdirectory(libusb EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
|
||||
# yaml-cpp
|
||||
# We don't want to install yaml-cpp but its cmake file doesn't have option
|
||||
# to disable it...
|
||||
# So we just install it to a different directory
|
||||
set(CMAKE_INSTALL_PREFIX_OLD ${CMAKE_INSTALL_PREFIX})
|
||||
set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/yaml-cpp_install)
|
||||
|
||||
set(YAML_CPP_BUILD_TESTS OFF CACHE BOOL "Enable testing" FORCE)
|
||||
set(YAML_CPP_BUILD_TOOLS OFF CACHE BOOL "Enable parse tools" FORCE)
|
||||
set(YAML_CPP_BUILD_CONTRIB OFF CACHE BOOL "Enable contrib stuff in library" FORCE)
|
||||
add_subdirectory(yaml-cpp EXCLUDE_FROM_ALL)
|
||||
|
||||
set(CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX_OLD})
|
||||
|
||||
|
||||
# xxHash
|
||||
set(XXHASH_BUNDLED_MODE ON)
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Make xxHash build static libs")
|
||||
add_subdirectory(xxHash/cmake_unofficial EXCLUDE_FROM_ALL)
|
||||
target_include_directories(xxhash INTERFACE xxHash)
|
||||
|
||||
|
||||
# cereal
|
||||
add_library(3rdparty_cereal INTERFACE)
|
||||
target_include_directories(3rdparty_cereal INTERFACE cereal/include)
|
||||
|
||||
|
||||
# OpenGL
|
||||
|
||||
# Prefer GLVND for OpenGL rather than legacy
|
||||
set(OpenGL_GL_PREFERENCE GLVND)
|
||||
find_package(OpenGL REQUIRED)
|
||||
|
||||
add_library(3rdparty_opengl INTERFACE)
|
||||
target_include_directories(3rdparty_opengl INTERFACE GL)
|
||||
|
||||
if (WIN32)
|
||||
if(NOT MSVC)
|
||||
target_link_libraries(3rdparty_opengl INTERFACE ${OPENGL_LIBRARIES} opengl32.lib glu32.lib)
|
||||
else()
|
||||
target_link_libraries(3rdparty_opengl INTERFACE dxgi.lib d2d1.lib dwrite.lib)
|
||||
endif()
|
||||
else()
|
||||
target_link_libraries(3rdparty_opengl INTERFACE ${OPENGL_LIBRARIES})
|
||||
|
||||
target_compile_definitions(3rdparty_opengl
|
||||
INTERFACE
|
||||
-DGL_GLEXT_PROTOTYPES
|
||||
-DGLX_GLXEXT_PROTOTYPES)
|
||||
endif()
|
||||
|
||||
|
||||
# GSL
|
||||
add_library(3rdparty_gsl INTERFACE)
|
||||
target_include_directories(3rdparty_gsl INTERFACE GSL/include)
|
||||
|
||||
|
||||
# stblib
|
||||
add_library(3rdparty_stblib INTERFACE)
|
||||
target_include_directories(3rdparty_stblib INTERFACE stblib)
|
||||
|
||||
|
||||
# discord_rpc
|
||||
add_library(3rdparty_discord-rpc INTERFACE)
|
||||
|
||||
# We don't want Discord Rich Presence on the BSDs and other OSes
|
||||
if (USE_DISCORD_RPC AND (WIN32 OR CMAKE_SYSTEM MATCHES "Linux" OR APPLE))
|
||||
if (WIN32 AND NOT MSVC)
|
||||
ExternalProject_Add(discord-rpc
|
||||
GIT_REPOSITORY https://github.com/discordapp/discord-rpc
|
||||
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/discord-rpc
|
||||
INSTALL_COMMAND ""
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
target_include_directories(3rdparty_discord-rpc INTERFACE discord-rpc/include)
|
||||
target_compile_definitions(3rdparty_discord-rpc INTERFACE -DWITH_DISCORD_RPC)
|
||||
|
||||
set(DISCORD_RPC_LIB NOTFOUND)
|
||||
if (WIN32)
|
||||
if (NOT MSVC)
|
||||
set(DISCORD_RPC_LIB ${CMAKE_CURRENT_BINARY_DIR}/discord-rpc/src/libdiscord-rpc.a)
|
||||
else()
|
||||
find_library(DISCORD_RPC_LIB discord-rpc PATHS discord-rpc/lib/ NO_DEFAULT_PATH)
|
||||
endif()
|
||||
elseif(CMAKE_SYSTEM MATCHES "Linux")
|
||||
find_library(DISCORD_RPC_LIB discord-rpc-linux PATHS discord-rpc/lib/ NO_DEFAULT_PATH)
|
||||
elseif(APPLE)
|
||||
find_library(DISCORD_RPC_LIB discord-rpc-mac PATHS discord-rpc/lib/ NO_DEFAULT_PATH)
|
||||
endif()
|
||||
target_link_libraries(3rdparty_discord-rpc INTERFACE ${DISCORD_RPC_LIB})
|
||||
if(APPLE)
|
||||
target_link_libraries(3rdparty_discord-rpc INTERFACE "objc" "-framework Foundation" "-framework CoreServices")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# ALSA
|
||||
set(ALSA_TARGET 3rdparty_dummy_lib)
|
||||
|
||||
if(USE_ALSA)
|
||||
find_package(ALSA)
|
||||
if(ALSA_FOUND)
|
||||
add_library(3rdparty_alsa INTERFACE)
|
||||
target_compile_definitions(3rdparty_alsa INTERFACE -DHAVE_ALSA)
|
||||
target_include_directories(3rdparty_alsa SYSTEM INTERFACE ${ALSA_INCLUDE_DIRS})
|
||||
target_link_libraries(3rdparty_alsa INTERFACE ${ALSA_LIBRARIES})
|
||||
|
||||
set(ALSA_TARGET 3rdparty_alsa)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# Pulse
|
||||
set(PULSE_TARGET 3rdparty_dummy_lib)
|
||||
if(USE_PULSE)
|
||||
pkg_check_modules(PULSE libpulse-simple)
|
||||
|
||||
if(PULSE_FOUND)
|
||||
add_library(3rdparty_pulse INTERFACE)
|
||||
target_compile_definitions(3rdparty_pulse INTERFACE -DHAVE_PULSE)
|
||||
target_include_directories(3rdparty_pulse SYSTEM
|
||||
INTERFACE ${PULSE_INCLUDE_DIRS})
|
||||
target_link_libraries(3rdparty_pulse INTERFACE ${PULSE_LDFLAGS})
|
||||
|
||||
set(PULSE_TARGET 3rdparty_pulse)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# libevdev
|
||||
set(LIBEVDEV_TARGET 3rdparty_dummy_lib)
|
||||
if(USE_LIBEVDEV)
|
||||
pkg_check_modules(LIBEVDEV libevdev)
|
||||
if(LIBEVDEV_FOUND)
|
||||
add_library(3rdparty_libevdev INTERFACE)
|
||||
target_compile_definitions(3rdparty_libevdev INTERFACE -DHAVE_LIBEVDEV)
|
||||
target_include_directories(3rdparty_libevdev SYSTEM
|
||||
INTERFACE ${LIBEVDEV_INCLUDE_DIRS})
|
||||
target_link_libraries(3rdparty_libevdev INTERFACE ${LIBEVDEV_LDFLAGS})
|
||||
|
||||
set(LIBEVDEV_TARGET 3rdparty_libevdev)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# Vulkan
|
||||
set(VULKAN_TARGET 3rdparty_dummy_lib)
|
||||
if(USE_VULKAN)
|
||||
find_package(Vulkan)
|
||||
if(VULKAN_FOUND)
|
||||
add_library(3rdparty_vulkan INTERFACE)
|
||||
target_compile_definitions(3rdparty_vulkan INTERFACE -DHAVE_VULKAN)
|
||||
target_link_libraries(3rdparty_vulkan INTERFACE SPIRV Vulkan::Vulkan)
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
find_package(Wayland)
|
||||
if (WAYLAND_FOUND)
|
||||
target_include_directories(3rdparty_vulkan
|
||||
INTERFACE ${WAYLAND_INCLUDE_DIR})
|
||||
|
||||
target_compile_definitions(3rdparty_vulkan
|
||||
INTERFACE -DVK_USE_PLATFORM_WAYLAND_KHR)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(VULKAN_TARGET 3rdparty_vulkan)
|
||||
else()
|
||||
message("WARNING! USE_VULKAN was enabled, but libvulkan was not found. RPCS3 will be compiled without Vulkan support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# OpenAL
|
||||
if (MSVC)
|
||||
find_path(OPENAL_INCLUDE_DIR al.h PATHS OpenAL/include)
|
||||
find_library(OPENAL_LIBRARY OpenAL32 PATHS OpenAL/libs/Win64/)
|
||||
else()
|
||||
find_package(OpenAL REQUIRED)
|
||||
endif()
|
||||
|
||||
add_library(3rdparty_openal INTERFACE)
|
||||
target_include_directories(3rdparty_openal INTERFACE ${OPENAL_INCLUDE_DIR})
|
||||
target_link_libraries(3rdparty_openal INTERFACE ${OPENAL_LIBRARY})
|
||||
|
||||
|
||||
# FFMPEG
|
||||
add_library(3rdparty_ffmpeg INTERFACE)
|
||||
|
||||
# Select the version of ffmpeg to use, default is builtin
|
||||
if(USE_SYSTEM_FFMPEG)
|
||||
message("-- RPCS3: using shared ffmpeg")
|
||||
find_package(FFMPEG REQUIRED)
|
||||
|
||||
target_include_directories(3rdparty_ffmpeg INTERFACE ${FFMPEG_INCLUDE_DIR})
|
||||
target_link_libraries(3rdparty_ffmpeg INTERFACE ${FFMPEG_LIBRARIES})
|
||||
else()
|
||||
set(FFMPEG_PLATFORM_DIR "ffmpeg/${PLATFORM_ARCH}")
|
||||
|
||||
if (NOT MSVC AND WIN32)
|
||||
message("-- RPCS3: building ffmpeg submodule")
|
||||
|
||||
ExternalProject_Add(ffmpeg-mingw
|
||||
DOWNLOAD_COMMAND ""
|
||||
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg
|
||||
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/ffmpeg
|
||||
CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg/configure --prefix=./Windows/x86_64 --arch=x86_64 --disable-avdevice --disable-programs --disable-avfilter --disable-postproc --disable-doc --disable-pthreads --enable-w32threads --disable-network --disable-everything --disable-encoders --disable-muxers --disable-hwaccels --disable-parsers --disable-protocols --enable-dxva2 --enable-static --disable-shared --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=atrac3 --enable-decoder=atrac3p --enable-decoder=mp3 --enable-decoder=pcm_s16le --enable-decoder=pcm_s8 --enable-decoder=h264 --enable-decoder=mpeg4 --enable-decoder=mpeg2video --enable-decoder=mjpeg --enable-decoder=mjpegb --enable-encoder=pcm_s16le --enable-encoder=ffv1 --enable-encoder=mpeg4 --enable-parser=h264 --enable-parser=mpeg4video --enable-parser=mpegaudio --enable-parser=mpegvideo --enable-parser=mjpeg --enable-parser=aac --enable-parser=aac_latm --enable-muxer=avi --enable-demuxer=h264 --enable-demuxer=m4v --enable-demuxer=mp3 --enable-demuxer=mpegvideo --enable-demuxer=mpegps --enable-demuxer=mjpeg --enable-demuxer=avi --enable-demuxer=aac --enable-demuxer=pmp --enable-demuxer=oma --enable-demuxer=pcm_s16le --enable-demuxer=pcm_s8 --enable-demuxer=wav --enable-hwaccel=h264_dxva2 --enable-indev=dshow --enable-protocol=file
|
||||
BUILD_COMMAND make -j 4
|
||||
INSTALL_COMMAND make install
|
||||
)
|
||||
|
||||
set(FFMPEG_LIB_AVFORMAT "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/${PLATFORM_ARCH}/lib/libavformat.a")
|
||||
set(FFMPEG_LIB_AVCODEC "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/${PLATFORM_ARCH}/lib/libavcodec.a")
|
||||
set(FFMPEG_LIB_AVUTIL "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/${PLATFORM_ARCH}/lib/libavutil.a")
|
||||
set(FFMPEG_LIB_SWSCALE "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/${PLATFORM_ARCH}/lib/libswscale.a")
|
||||
else()
|
||||
message("-- RPCS3: using builtin ffmpeg")
|
||||
|
||||
set(FFMPEG_LIB_DIR "${FFMPEG_PLATFORM_DIR}/lib")
|
||||
find_library(FFMPEG_LIB_AVFORMAT avformat PATHS ${FFMPEG_LIB_DIR} NO_DEFAULT_PATH)
|
||||
find_library(FFMPEG_LIB_AVCODEC avcodec PATHS ${FFMPEG_LIB_DIR} NO_DEFAULT_PATH)
|
||||
find_library(FFMPEG_LIB_AVUTIL avutil PATHS ${FFMPEG_LIB_DIR} NO_DEFAULT_PATH)
|
||||
find_library(FFMPEG_LIB_SWSCALE swscale PATHS ${FFMPEG_LIB_DIR} NO_DEFAULT_PATH)
|
||||
endif()
|
||||
|
||||
target_include_directories(3rdparty_ffmpeg INTERFACE "${FFMPEG_PLATFORM_DIR}/include")
|
||||
|
||||
target_link_libraries(3rdparty_ffmpeg
|
||||
INTERFACE
|
||||
${FFMPEG_LIB_AVFORMAT}
|
||||
${FFMPEG_LIB_AVCODEC}
|
||||
${FFMPEG_LIB_AVUTIL}
|
||||
${FFMPEG_LIB_SWSCALE})
|
||||
endif()
|
||||
|
||||
|
||||
# dx12
|
||||
add_library(3rdparty_dx12 INTERFACE)
|
||||
if (WIN32)
|
||||
target_link_libraries(3rdparty_dx12 INTERFACE dxgi.lib d2d1.lib dwrite.lib)
|
||||
endif()
|
||||
|
||||
|
||||
# GLEW
|
||||
add_library(3rdparty_glew INTERFACE)
|
||||
if(NOT MSVC)
|
||||
find_package(GLEW 1.13.0 REQUIRED)
|
||||
target_link_libraries(3rdparty_glew INTERFACE GLEW::GLEW)
|
||||
endif()
|
||||
|
||||
|
||||
# LLVM
|
||||
include(llvm.cmake)
|
||||
|
||||
|
||||
# add nice ALIAS targets for ease of use
|
||||
add_library(3rdparty::libusb ALIAS usb-1.0-static)
|
||||
add_library(3rdparty::zlib ALIAS 3rdparty_zlib)
|
||||
add_library(3rdparty::pugixml ALIAS pugixml)
|
||||
add_library(3rdparty::yaml-cpp ALIAS yaml-cpp)
|
||||
add_library(3rdparty::xxhash ALIAS xxhash)
|
||||
add_library(3rdparty::hidapi ALIAS 3rdparty_hidapi)
|
||||
add_library(3rdparty::libpng ALIAS ${LIBPNG_TARGET})
|
||||
add_library(3rdparty::cereal ALIAS 3rdparty_cereal)
|
||||
add_library(3rdparty::opengl ALIAS 3rdparty_opengl)
|
||||
add_library(3rdparty::gsl ALIAS 3rdparty_gsl)
|
||||
add_library(3rdparty::stblib ALIAS 3rdparty_stblib)
|
||||
add_library(3rdparty::discord-rpc ALIAS 3rdparty_discord-rpc)
|
||||
add_library(3rdparty::alsa ALIAS ${ALSA_TARGET})
|
||||
add_library(3rdparty::pulse ALIAS ${PULSE_TARGET})
|
||||
add_library(3rdparty::libevdev ALIAS ${LIBEVDEV_TARGET})
|
||||
add_library(3rdparty::vulkan ALIAS ${VULKAN_TARGET})
|
||||
add_library(3rdparty::openal ALIAS 3rdparty_openal)
|
||||
add_library(3rdparty::ffmpeg ALIAS 3rdparty_ffmpeg)
|
||||
add_library(3rdparty::dx12 ALIAS 3rdparty_dx12)
|
||||
add_library(3rdparty::glew ALIAS 3rdparty_glew)
|
||||
Vendored
+699
-25
File diff suppressed because it is too large
Load Diff
+16763
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
Submodule 3rdparty/GSL updated: fc5fce4f4f...1995e86d1a
Vendored
+118
-2
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -97,6 +97,31 @@ extern "C" {
|
||||
|
||||
#ifndef AL_EXT_MCFORMATS
|
||||
#define AL_EXT_MCFORMATS 1
|
||||
/* Provides support for surround sound buffer formats with 8, 16, and 32-bit
|
||||
* samples.
|
||||
*
|
||||
* QUAD8: Unsigned 8-bit, Quadraphonic (Front Left, Front Right, Rear Left,
|
||||
* Rear Right).
|
||||
* QUAD16: Signed 16-bit, Quadraphonic.
|
||||
* QUAD32: 32-bit float, Quadraphonic.
|
||||
* REAR8: Unsigned 8-bit, Rear Stereo (Rear Left, Rear Right).
|
||||
* REAR16: Signed 16-bit, Rear Stereo.
|
||||
* REAR32: 32-bit float, Rear Stereo.
|
||||
* 51CHN8: Unsigned 8-bit, 5.1 Surround (Front Left, Front Right, Front Center,
|
||||
* LFE, Side Left, Side Right). Note that some audio systems may label
|
||||
* 5.1's Side channels as Rear or Surround; they are equivalent for the
|
||||
* purposes of this extension.
|
||||
* 51CHN16: Signed 16-bit, 5.1 Surround.
|
||||
* 51CHN32: 32-bit float, 5.1 Surround.
|
||||
* 61CHN8: Unsigned 8-bit, 6.1 Surround (Front Left, Front Right, Front Center,
|
||||
* LFE, Rear Center, Side Left, Side Right).
|
||||
* 61CHN16: Signed 16-bit, 6.1 Surround.
|
||||
* 61CHN32: 32-bit float, 6.1 Surround.
|
||||
* 71CHN8: Unsigned 8-bit, 7.1 Surround (Front Left, Front Right, Front Center,
|
||||
* LFE, Rear Left, Rear Right, Side Left, Side Right).
|
||||
* 71CHN16: Signed 16-bit, 7.1 Surround.
|
||||
* 71CHN32: 32-bit float, 7.1 Surround.
|
||||
*/
|
||||
#define AL_FORMAT_QUAD8 0x1204
|
||||
#define AL_FORMAT_QUAD16 0x1205
|
||||
#define AL_FORMAT_QUAD32 0x1206
|
||||
@@ -393,6 +418,97 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef AL_EXT_BFORMAT
|
||||
#define AL_EXT_BFORMAT 1
|
||||
/* Provides support for B-Format ambisonic buffers (first-order, FuMa scaling
|
||||
* and layout).
|
||||
*
|
||||
* BFORMAT2D_8: Unsigned 8-bit, 3-channel non-periphonic (WXY).
|
||||
* BFORMAT2D_16: Signed 16-bit, 3-channel non-periphonic (WXY).
|
||||
* BFORMAT2D_FLOAT32: 32-bit float, 3-channel non-periphonic (WXY).
|
||||
* BFORMAT3D_8: Unsigned 8-bit, 4-channel periphonic (WXYZ).
|
||||
* BFORMAT3D_16: Signed 16-bit, 4-channel periphonic (WXYZ).
|
||||
* BFORMAT3D_FLOAT32: 32-bit float, 4-channel periphonic (WXYZ).
|
||||
*/
|
||||
#define AL_FORMAT_BFORMAT2D_8 0x20021
|
||||
#define AL_FORMAT_BFORMAT2D_16 0x20022
|
||||
#define AL_FORMAT_BFORMAT2D_FLOAT32 0x20023
|
||||
#define AL_FORMAT_BFORMAT3D_8 0x20031
|
||||
#define AL_FORMAT_BFORMAT3D_16 0x20032
|
||||
#define AL_FORMAT_BFORMAT3D_FLOAT32 0x20033
|
||||
#endif
|
||||
|
||||
#ifndef AL_EXT_MULAW_BFORMAT
|
||||
#define AL_EXT_MULAW_BFORMAT 1
|
||||
#define AL_FORMAT_BFORMAT2D_MULAW 0x10031
|
||||
#define AL_FORMAT_BFORMAT3D_MULAW 0x10032
|
||||
#endif
|
||||
|
||||
#ifndef ALC_SOFT_HRTF
|
||||
#define ALC_SOFT_HRTF 1
|
||||
#define ALC_HRTF_SOFT 0x1992
|
||||
#define ALC_DONT_CARE_SOFT 0x0002
|
||||
#define ALC_HRTF_STATUS_SOFT 0x1993
|
||||
#define ALC_HRTF_DISABLED_SOFT 0x0000
|
||||
#define ALC_HRTF_ENABLED_SOFT 0x0001
|
||||
#define ALC_HRTF_DENIED_SOFT 0x0002
|
||||
#define ALC_HRTF_REQUIRED_SOFT 0x0003
|
||||
#define ALC_HRTF_HEADPHONES_DETECTED_SOFT 0x0004
|
||||
#define ALC_HRTF_UNSUPPORTED_FORMAT_SOFT 0x0005
|
||||
#define ALC_NUM_HRTF_SPECIFIERS_SOFT 0x1994
|
||||
#define ALC_HRTF_SPECIFIER_SOFT 0x1995
|
||||
#define ALC_HRTF_ID_SOFT 0x1996
|
||||
typedef const ALCchar* (ALC_APIENTRY*LPALCGETSTRINGISOFT)(ALCdevice *device, ALCenum paramName, ALCsizei index);
|
||||
typedef ALCboolean (ALC_APIENTRY*LPALCRESETDEVICESOFT)(ALCdevice *device, const ALCint *attribs);
|
||||
#ifdef AL_ALEXT_PROTOTYPES
|
||||
ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index);
|
||||
ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef AL_SOFT_gain_clamp_ex
|
||||
#define AL_SOFT_gain_clamp_ex 1
|
||||
#define AL_GAIN_LIMIT_SOFT 0x200E
|
||||
#endif
|
||||
|
||||
#ifndef AL_SOFT_source_resampler
|
||||
#define AL_SOFT_source_resampler
|
||||
#define AL_NUM_RESAMPLERS_SOFT 0x1210
|
||||
#define AL_DEFAULT_RESAMPLER_SOFT 0x1211
|
||||
#define AL_SOURCE_RESAMPLER_SOFT 0x1212
|
||||
#define AL_RESAMPLER_NAME_SOFT 0x1213
|
||||
typedef const ALchar* (AL_APIENTRY*LPALGETSTRINGISOFT)(ALenum pname, ALsizei index);
|
||||
#ifdef AL_ALEXT_PROTOTYPES
|
||||
AL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef AL_SOFT_source_spatialize
|
||||
#define AL_SOFT_source_spatialize
|
||||
#define AL_SOURCE_SPATIALIZE_SOFT 0x1214
|
||||
#define AL_AUTO_SOFT 0x0002
|
||||
#endif
|
||||
|
||||
#ifndef ALC_SOFT_output_limiter
|
||||
#define ALC_SOFT_output_limiter
|
||||
#define ALC_OUTPUT_LIMITER_SOFT 0x199A
|
||||
#endif
|
||||
|
||||
#ifndef ALC_SOFT_device_clock
|
||||
#define ALC_SOFT_device_clock 1
|
||||
typedef int64_t ALCint64SOFT;
|
||||
typedef uint64_t ALCuint64SOFT;
|
||||
#define ALC_DEVICE_CLOCK_SOFT 0x1600
|
||||
#define ALC_DEVICE_LATENCY_SOFT 0x1601
|
||||
#define ALC_DEVICE_CLOCK_LATENCY_SOFT 0x1602
|
||||
#define AL_SAMPLE_OFFSET_CLOCK_SOFT 0x1202
|
||||
#define AL_SEC_OFFSET_CLOCK_SOFT 0x1203
|
||||
typedef void (ALC_APIENTRY*LPALCGETINTEGER64VSOFT)(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values);
|
||||
#ifdef AL_ALEXT_PROTOTYPES
|
||||
ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
+1
-1
@@ -345,7 +345,7 @@ typedef struct {
|
||||
/* Driving Presets */
|
||||
|
||||
#define EFX_REVERB_PRESET_DRIVING_COMMENTATOR \
|
||||
{ 1.0000f, 0.0000f, 3.1623f, 0.5623f, 0.5012f, 2.4200f, 0.8800f, 0.6800f, 0.1995f, 0.0930f, { 0.0000f, 0.0000f, 0.0000f }, 0.2512f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9886f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
|
||||
{ 1.0000f, 0.0000f, 0.3162f, 0.5623f, 0.5012f, 2.4200f, 0.8800f, 0.6800f, 0.1995f, 0.0930f, { 0.0000f, 0.0000f, 0.0000f }, 0.2512f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9886f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
|
||||
|
||||
#define EFX_REVERB_PRESET_DRIVING_PITGARAGE \
|
||||
{ 0.4287f, 0.5900f, 0.3162f, 0.7079f, 0.5623f, 1.7200f, 0.9300f, 0.8700f, 0.5623f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
|
||||
|
||||
-91
@@ -1,84 +1,35 @@
|
||||
EXPORTS
|
||||
alAuxiliaryEffectSlotf
|
||||
alAuxiliaryEffectSlotfv
|
||||
alAuxiliaryEffectSloti
|
||||
alAuxiliaryEffectSlotiv
|
||||
alBuffer3f
|
||||
alBuffer3i
|
||||
alBufferData
|
||||
alBufferSamplesSOFT
|
||||
alBufferSubDataSOFT
|
||||
alBufferSubSamplesSOFT
|
||||
alBufferf
|
||||
alBufferfv
|
||||
alBufferi
|
||||
alBufferiv
|
||||
alDeferUpdatesSOFT
|
||||
alDeleteAuxiliaryEffectSlots
|
||||
alDeleteBuffers
|
||||
alDeleteEffects
|
||||
alDeleteFilters
|
||||
alDeleteFontsoundsSOFT
|
||||
alDeletePresetsSOFT
|
||||
alDeleteSoundfontsSOFT
|
||||
alDeleteSources
|
||||
alDisable
|
||||
alDistanceModel
|
||||
alDopplerFactor
|
||||
alDopplerVelocity
|
||||
alEffectf
|
||||
alEffectfv
|
||||
alEffecti
|
||||
alEffectiv
|
||||
alEnable
|
||||
alFilterf
|
||||
alFilterfv
|
||||
alFilteri
|
||||
alFilteriv
|
||||
alFontsound2iSOFT
|
||||
alFontsoundModulatoriSOFT
|
||||
alFontsoundiSOFT
|
||||
alFontsoundivSOFT
|
||||
alGenAuxiliaryEffectSlots
|
||||
alGenBuffers
|
||||
alGenEffects
|
||||
alGenFilters
|
||||
alGenFontsoundsSOFT
|
||||
alGenPresetsSOFT
|
||||
alGenSoundfontsSOFT
|
||||
alGenSources
|
||||
alGetAuxiliaryEffectSlotf
|
||||
alGetAuxiliaryEffectSlotfv
|
||||
alGetAuxiliaryEffectSloti
|
||||
alGetAuxiliaryEffectSlotiv
|
||||
alGetBoolean
|
||||
alGetBooleanv
|
||||
alGetBuffer3f
|
||||
alGetBuffer3i
|
||||
alGetBufferSamplesSOFT
|
||||
alGetBufferf
|
||||
alGetBufferfv
|
||||
alGetBufferi
|
||||
alGetBufferiv
|
||||
alGetDouble
|
||||
alGetDoublev
|
||||
alGetEffectf
|
||||
alGetEffectfv
|
||||
alGetEffecti
|
||||
alGetEffectiv
|
||||
alGetEnumValue
|
||||
alGetError
|
||||
alGetFilterf
|
||||
alGetFilterfv
|
||||
alGetFilteri
|
||||
alGetFilteriv
|
||||
alGetFloat
|
||||
alGetFloatv
|
||||
alGetFontsoundModulatorivSOFT
|
||||
alGetFontsoundivSOFT
|
||||
alGetInteger
|
||||
alGetInteger64SOFT
|
||||
alGetInteger64vSOFT
|
||||
alGetIntegerv
|
||||
alGetListener3f
|
||||
alGetListener3i
|
||||
@@ -86,32 +37,17 @@ EXPORTS
|
||||
alGetListenerfv
|
||||
alGetListeneri
|
||||
alGetListeneriv
|
||||
alGetPresetivSOFT
|
||||
alGetProcAddress
|
||||
alGetSoundfontivSOFT
|
||||
alGetSource3dSOFT
|
||||
alGetSource3f
|
||||
alGetSource3i
|
||||
alGetSource3i64SOFT
|
||||
alGetSourcedSOFT
|
||||
alGetSourcedvSOFT
|
||||
alGetSourcef
|
||||
alGetSourcefv
|
||||
alGetSourcei
|
||||
alGetSourcei64SOFT
|
||||
alGetSourcei64vSOFT
|
||||
alGetSourceiv
|
||||
alGetString
|
||||
alIsAuxiliaryEffectSlot
|
||||
alIsBuffer
|
||||
alIsBufferFormatSupportedSOFT
|
||||
alIsEffect
|
||||
alIsEnabled
|
||||
alIsExtensionPresent
|
||||
alIsFilter
|
||||
alIsFontsoundSOFT
|
||||
alIsPresetSOFT
|
||||
alIsSoundfontSOFT
|
||||
alIsSource
|
||||
alListener3f
|
||||
alListener3i
|
||||
@@ -119,25 +55,8 @@ EXPORTS
|
||||
alListenerfv
|
||||
alListeneri
|
||||
alListeneriv
|
||||
alLoadSoundfontSOFT
|
||||
alMidiEventSOFT
|
||||
alMidiGainSOFT
|
||||
alMidiPauseSOFT
|
||||
alMidiPlaySOFT
|
||||
alMidiResetSOFT
|
||||
alMidiSoundfontSOFT
|
||||
alMidiSoundfontvSOFT
|
||||
alMidiStopSOFT
|
||||
alMidiSysExSOFT
|
||||
alPresetFontsoundsSOFT
|
||||
alPresetiSOFT
|
||||
alPresetivSOFT
|
||||
alProcessUpdatesSOFT
|
||||
alSoundfontPresetsSOFT
|
||||
alSource3dSOFT
|
||||
alSource3f
|
||||
alSource3i
|
||||
alSource3i64SOFT
|
||||
alSourcePause
|
||||
alSourcePausev
|
||||
alSourcePlay
|
||||
@@ -148,13 +67,9 @@ EXPORTS
|
||||
alSourceStop
|
||||
alSourceStopv
|
||||
alSourceUnqueueBuffers
|
||||
alSourcedSOFT
|
||||
alSourcedvSOFT
|
||||
alSourcef
|
||||
alSourcefv
|
||||
alSourcei
|
||||
alSourcei64SOFT
|
||||
alSourcei64vSOFT
|
||||
alSourceiv
|
||||
alSpeedOfSound
|
||||
alcCaptureCloseDevice
|
||||
@@ -165,23 +80,17 @@ EXPORTS
|
||||
alcCloseDevice
|
||||
alcCreateContext
|
||||
alcDestroyContext
|
||||
alcDevicePauseSOFT
|
||||
alcDeviceResumeSOFT
|
||||
alcGetContextsDevice
|
||||
alcGetCurrentContext
|
||||
alcGetEnumValue
|
||||
alcGetError
|
||||
alcGetInteger64vSOFT
|
||||
alcGetIntegerv
|
||||
alcGetProcAddress
|
||||
alcGetString
|
||||
alcGetThreadContext
|
||||
alcIsExtensionPresent
|
||||
alcIsRenderFormatSupportedSOFT
|
||||
alcLoopbackOpenDeviceSOFT
|
||||
alcMakeContextCurrent
|
||||
alcOpenDevice
|
||||
alcProcessContext
|
||||
alcRenderSamplesSOFT
|
||||
alcSetThreadContext
|
||||
alcSuspendContext
|
||||
|
||||
BIN
Binary file not shown.
Vendored
-1
Submodule 3rdparty/Optional deleted from f27e79084a
+26
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(DISCORD_DYNAMIC_LIB)
|
||||
# if defined(_WIN32)
|
||||
# if defined(DISCORD_BUILDING_SDK)
|
||||
# define DISCORD_EXPORT __declspec(dllexport)
|
||||
# else
|
||||
# define DISCORD_EXPORT __declspec(dllimport)
|
||||
# endif
|
||||
# else
|
||||
# define DISCORD_EXPORT __attribute__((visibility("default")))
|
||||
# endif
|
||||
#else
|
||||
# define DISCORD_EXPORT
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
DISCORD_EXPORT void Discord_Register(const char* applicationId, const char* command);
|
||||
DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, const char* steamId);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
// clang-format off
|
||||
|
||||
#if defined(DISCORD_DYNAMIC_LIB)
|
||||
# if defined(_WIN32)
|
||||
# if defined(DISCORD_BUILDING_SDK)
|
||||
# define DISCORD_EXPORT __declspec(dllexport)
|
||||
# else
|
||||
# define DISCORD_EXPORT __declspec(dllimport)
|
||||
# endif
|
||||
# else
|
||||
# define DISCORD_EXPORT __attribute__((visibility("default")))
|
||||
# endif
|
||||
#else
|
||||
# define DISCORD_EXPORT
|
||||
#endif
|
||||
|
||||
// clang-format on
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct DiscordRichPresence {
|
||||
const char* state; /* max 128 bytes */
|
||||
const char* details; /* max 128 bytes */
|
||||
int64_t startTimestamp;
|
||||
int64_t endTimestamp;
|
||||
const char* largeImageKey; /* max 32 bytes */
|
||||
const char* largeImageText; /* max 128 bytes */
|
||||
const char* smallImageKey; /* max 32 bytes */
|
||||
const char* smallImageText; /* max 128 bytes */
|
||||
const char* partyId; /* max 128 bytes */
|
||||
int partySize;
|
||||
int partyMax;
|
||||
const char* matchSecret; /* max 128 bytes */
|
||||
const char* joinSecret; /* max 128 bytes */
|
||||
const char* spectateSecret; /* max 128 bytes */
|
||||
int8_t instance;
|
||||
} DiscordRichPresence;
|
||||
|
||||
typedef struct DiscordJoinRequest {
|
||||
const char* userId;
|
||||
const char* username;
|
||||
const char* discriminator;
|
||||
const char* avatar;
|
||||
} DiscordJoinRequest;
|
||||
|
||||
typedef struct DiscordEventHandlers {
|
||||
void (*ready)(void);
|
||||
void (*disconnected)(int errorCode, const char* message);
|
||||
void (*errored)(int errorCode, const char* message);
|
||||
void (*joinGame)(const char* joinSecret);
|
||||
void (*spectateGame)(const char* spectateSecret);
|
||||
void (*joinRequest)(const DiscordJoinRequest* request);
|
||||
} DiscordEventHandlers;
|
||||
|
||||
#define DISCORD_REPLY_NO 0
|
||||
#define DISCORD_REPLY_YES 1
|
||||
#define DISCORD_REPLY_IGNORE 2
|
||||
|
||||
DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
|
||||
DiscordEventHandlers* handlers,
|
||||
int autoRegister,
|
||||
const char* optionalSteamId);
|
||||
DISCORD_EXPORT void Discord_Shutdown(void);
|
||||
|
||||
/* checks for incoming messages, dispatches callbacks */
|
||||
DISCORD_EXPORT void Discord_RunCallbacks(void);
|
||||
|
||||
/* If you disable the lib starting its own io thread, you'll need to call this from your own */
|
||||
#ifdef DISCORD_DISABLE_IO_THREAD
|
||||
DISCORD_EXPORT void Discord_UpdateConnection(void);
|
||||
#endif
|
||||
|
||||
DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence);
|
||||
DISCORD_EXPORT void Discord_ClearPresence(void);
|
||||
|
||||
DISCORD_EXPORT void Discord_Respond(const char* userid, /* DISCORD_REPLY_ */ int reply);
|
||||
|
||||
DISCORD_EXPORT void Discord_UpdateHandlers(DiscordEventHandlers* handlers);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
+1
-1
Submodule 3rdparty/hidapi updated: ca39ce8939...9220f5e77c
Vendored
+105
@@ -0,0 +1,105 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{A107C21C-418A-4697-BB10-20C3AA60E2E4}</ProjectGuid>
|
||||
<RootNamespace>hidapi</RootNamespace>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\common_default.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="..\common_default_macros.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>15.0.26323.1</_ProjectFileVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)lib/</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)lib/</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>hidapi\hidapi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalDependencies>setupapi.lib</AdditionalDependencies>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<AdditionalIncludeDirectories>hidapi\hidapi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalDependencies>setupapi.lib</AdditionalDependencies>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="hidapi\windows\hid.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="hidapi\hidapi\hidapi.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="hidapi\windows\hid.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="hidapi\hidapi\hidapi.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Vendored
+1
-1
Submodule 3rdparty/libpng updated: 1dcba4d6eb...eddf902320
Vendored
+142
@@ -0,0 +1,142 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug Library|x64">
|
||||
<Configuration>Debug Library</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release Library|x64">
|
||||
<Configuration>Release Library</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{D6973076-9317-4EF2-A0B8-B7A18AC0713E}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>libpng</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\3rdparty\zlib.props" />
|
||||
<Import Project="..\common_default.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="..\common_default_macros.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<CustomBuildBeforeTargets />
|
||||
<TargetName>$(ProjectName)16</TargetName>
|
||||
<OutDir>$(SolutionDir)lib/</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<CustomBuildBeforeTargets />
|
||||
<TargetName>$(ProjectName)16</TargetName>
|
||||
<OutDir>$(SolutionDir)lib/</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>$(WarningLevel)</WarningLevel>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<FloatingPointExceptions>false</FloatingPointExceptions>
|
||||
<TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
|
||||
<PrecompiledHeaderFile>pngpriv.h</PrecompiledHeaderFile>
|
||||
<BrowseInformation>true</BrowseInformation>
|
||||
<CompileAs>CompileAsC</CompileAs>
|
||||
<StringPooling>true</StringPooling>
|
||||
<DisableSpecificWarnings>$(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<AdditionalIncludeDirectories>$(ZLibSrcDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<TreatWarningAsError>$(TreatWarningAsError)</TreatWarningAsError>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>$(WarningLevel)</WarningLevel>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<FloatingPointExceptions>false</FloatingPointExceptions>
|
||||
<TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
|
||||
<PrecompiledHeaderFile>pngpriv.h</PrecompiledHeaderFile>
|
||||
<BrowseInformation>true</BrowseInformation>
|
||||
<CompileAs>CompileAsC</CompileAs>
|
||||
<StringPooling>true</StringPooling>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<DisableSpecificWarnings>$(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<AdditionalIncludeDirectories>$(ZLibSrcDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<TreatWarningAsError>$(TreatWarningAsError)</TreatWarningAsError>
|
||||
<Optimization>Full</Optimization>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
<Lib>
|
||||
<LinkTimeCodeGeneration>true</LinkTimeCodeGeneration>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include=".\libpng\png.c">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include=".\libpng\pngerror.c" />
|
||||
<ClCompile Include=".\libpng\pngget.c" />
|
||||
<ClCompile Include=".\libpng\pngmem.c" />
|
||||
<ClCompile Include=".\libpng\pngpread.c" />
|
||||
<ClCompile Include=".\libpng\pngread.c" />
|
||||
<ClCompile Include=".\libpng\pngrio.c" />
|
||||
<ClCompile Include=".\libpng\pngrtran.c" />
|
||||
<ClCompile Include=".\libpng\pngrutil.c" />
|
||||
<ClCompile Include=".\libpng\pngset.c" />
|
||||
<ClCompile Include=".\libpng\pngtrans.c" />
|
||||
<ClCompile Include=".\libpng\pngwio.c" />
|
||||
<ClCompile Include=".\libpng\pngwrite.c" />
|
||||
<ClCompile Include=".\libpng\pngwtran.c" />
|
||||
<ClCompile Include=".\libpng\pngwutil.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include=".\libpng\scripts\pngwin.rc">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'">true</ExcludedFromBuild>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
+1
Submodule 3rdparty/libusb added at 7cfa00e9d7
Vendored
+90
@@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectName>libusb-1.0 (static)</ProjectName>
|
||||
<ProjectGuid>{349EE8F9-7D25-4909-AAF5-FF3FADE72187}</ProjectGuid>
|
||||
<RootNamespace>libusb</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\common_default.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="..\common_default_macros.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<WholeProgramOptimization Condition="'$(Configuration)'=='Release'">true</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
|
||||
<OutDir>$(SolutionDir)lib/</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
|
||||
<TargetName>libusb-1.0</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>libusb\msvc;libusb\libusb;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WINVER=0x0501;_WIN32_WINNT=0x0501;_LIB;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
</ClCompile>
|
||||
<ClCompile Condition="'$(Configuration)'=='Debug'">
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<ClCompile Condition="'$(Configuration)'=='Release'">
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<OutputFile>$(OutDir)libusb-1.0.lib</OutputFile>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="libusb\libusb\core.c" />
|
||||
<ClCompile Include="libusb\libusb\descriptor.c" />
|
||||
<ClCompile Include="libusb\libusb\hotplug.c" />
|
||||
<ClCompile Include="libusb\libusb\io.c" />
|
||||
<ClCompile Include="libusb\libusb\os\poll_windows.c" />
|
||||
<ClCompile Include="libusb\libusb\strerror.c" />
|
||||
<ClCompile Include="libusb\libusb\sync.c" />
|
||||
<ClCompile Include="libusb\libusb\os\threads_windows.c" />
|
||||
<ClCompile Include="libusb\libusb\os\windows_nt_common.c" />
|
||||
<ClCompile Include="libusb\libusb\os\windows_usbdk.c" />
|
||||
<ClCompile Include="libusb\libusb\os\windows_winusb.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="libusb\msvc\config.h" />
|
||||
<ClInclude Include="libusb\libusb\hotplug.h" />
|
||||
<ClInclude Include="libusb\libusb\libusb.h" />
|
||||
<ClInclude Include="libusb\libusb\libusbi.h" />
|
||||
<ClInclude Include="libusb\libusb\os\poll_windows.h" />
|
||||
<ClInclude Include="libusb\libusb\os\threads_windows.h" />
|
||||
<ClInclude Include="libusb\libusb\version.h" />
|
||||
<ClInclude Include="libusb\libusb\version_nano.h" />
|
||||
<ClInclude Include="libusb\libusb\os\windows_common.h" />
|
||||
<ClInclude Include="libusb\libusb\os\windows_nt_common.h" />
|
||||
<ClInclude Include="libusb\libusb\os\windows_nt_shared_types.h" />
|
||||
<ClInclude Include="libusb\libusb\os\windows_usbdk.h" />
|
||||
<ClInclude Include="libusb\libusb\os\windows_winusb.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
Vendored
+65
@@ -0,0 +1,65 @@
|
||||
if(WITH_LLVM)
|
||||
if(BUILD_LLVM_SUBMODULE)
|
||||
message(STATUS "LLVM will be built from the submodule.")
|
||||
|
||||
set(LLVM_TARGETS_TO_BUILD "X86" CACHE INTERNAL "")
|
||||
option(LLVM_BUILD_RUNTIME OFF)
|
||||
option(LLVM_BUILD_TOOLS OFF)
|
||||
option(LLVM_INCLUDE_DOCS OFF)
|
||||
option(LLVM_INCLUDE_EXAMPLES OFF)
|
||||
option(LLVM_INCLUDE_TESTS OFF)
|
||||
option(LLVM_INCLUDE_TOOLS OFF)
|
||||
option(LLVM_INCLUDE_UTILS OFF)
|
||||
option(WITH_POLLY OFF)
|
||||
option(LLVM_ENABLE_CXX1Z TRUE)
|
||||
|
||||
set(CXX_FLAGS_OLD ${CMAKE_CXX_FLAGS})
|
||||
|
||||
if (MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS")
|
||||
endif()
|
||||
|
||||
# LLVM needs to be built out-of-tree
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/llvm ${CMAKE_CURRENT_BINARY_DIR}/llvm_build EXCLUDE_FROM_ALL)
|
||||
set(LLVM_DIR "${CMAKE_CURRENT_BINARY_DIR}/llvm_build/lib/cmake/llvm/")
|
||||
|
||||
set(CMAKE_CXX_FLAGS ${CXX_FLAGS_OLD})
|
||||
|
||||
# now tries to find LLVM again
|
||||
find_package(LLVM 9.0 CONFIG)
|
||||
if(NOT LLVM_FOUND)
|
||||
message(FATAL_ERROR "Couldn't build LLVM from the submodule. You might need to run `git submodule update --init`")
|
||||
endif()
|
||||
|
||||
else()
|
||||
message(STATUS "Using prebuilt LLVM")
|
||||
|
||||
if (LLVM_DIR AND NOT IS_ABSOLUTE "${LLVM_DIR}")
|
||||
# change relative LLVM_DIR to be relative to the source dir
|
||||
set(LLVM_DIR ${CMAKE_SOURCE_DIR}/${LLVM_DIR})
|
||||
endif()
|
||||
|
||||
find_package(LLVM 9.0 CONFIG)
|
||||
|
||||
if (NOT LLVM_FOUND)
|
||||
if (LLVM_VERSION AND LLVM_VERSION_MAJOR LESS 9)
|
||||
message(FATAL_ERROR "Found LLVM version ${LLVM_VERSION}. Required version 9.0. \
|
||||
Enable BUILD_LLVM_SUBMODULE option to build LLVM from included as a git submodule.")
|
||||
endif()
|
||||
|
||||
message(FATAL_ERROR "Can't find LLVM libraries from the CMAKE_PREFIX_PATH path or LLVM_DIR. \
|
||||
Enable BUILD_LLVM_SUBMODULE option to build LLVM from included as a git submodule.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(LLVM_LIBS LLVMMCJIT LLVMX86CodeGen LLVMX86AsmParser)
|
||||
|
||||
add_library(3rdparty_llvm INTERFACE)
|
||||
target_link_libraries(3rdparty_llvm INTERFACE ${LLVM_LIBS})
|
||||
target_include_directories(3rdparty_llvm INTERFACE ${LLVM_INCLUDE_DIRS})
|
||||
target_compile_definitions(3rdparty_llvm INTERFACE ${LLVM_DEFINITIONS} -DLLVM_AVAILABLE)
|
||||
|
||||
add_library(3rdparty::llvm ALIAS 3rdparty_llvm)
|
||||
else()
|
||||
add_library(3rdparty::llvm ALIAS 3rdparty_dummy_lib)
|
||||
endif()
|
||||
Vendored
+64
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{EB33566E-DA7F-4D28-9077-88C0B7C77E35}</ProjectGuid>
|
||||
<RootNamespace>pnglibconf</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\common_default.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="..\common_default_macros.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\3rdparty\zlib.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<CustomBuildBeforeTargets>Build</CustomBuildBeforeTargets>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>$(WarningLevel)</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
<CustomBuildStep>
|
||||
<Command>copy .\libpng\scripts\pnglibconf.h.prebuilt .\libpng\pnglibconf.h</Command>
|
||||
</CustomBuildStep>
|
||||
<CustomBuildStep>
|
||||
<Message>Generating pnglibconf.h</Message>
|
||||
</CustomBuildStep>
|
||||
<CustomBuildStep>
|
||||
<Outputs>.\libpng\pnglibconf.h</Outputs>
|
||||
</CustomBuildStep>
|
||||
<CustomBuildStep>
|
||||
<Inputs>.\libpng\scripts\pnglibconf.h.prebuilt</Inputs>
|
||||
</CustomBuildStep>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
Vendored
+1
-1
Submodule 3rdparty/pugixml updated: 257fbb4e1b...8bf806c035
Vendored
+49
@@ -0,0 +1,49 @@
|
||||
add_library(3rdparty_qt5 INTERFACE)
|
||||
|
||||
find_package(Qt5 5.10 CONFIG COMPONENTS Widgets Network Qml)
|
||||
if(WIN32)
|
||||
find_package(Qt5 5.10 COMPONENTS WinExtras REQUIRED)
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::WinExtras Qt5::Network Qt5::Qml)
|
||||
else()
|
||||
find_package(Qt5 5.10 COMPONENTS DBus Gui)
|
||||
if(Qt5DBus_FOUND)
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::DBus Qt5::Network Qt5::Qml)
|
||||
target_compile_definitions(3rdparty_qt5 INTERFACE -DHAVE_QTDBUS)
|
||||
else()
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::Network Qt5::Qml)
|
||||
endif()
|
||||
target_include_directories(3rdparty_qt5 INTERFACE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
if(NOT Qt5Widgets_FOUND)
|
||||
if(Qt5Widgets_VERSION VERSION_LESS 5.10.0)
|
||||
message("Minimum supported Qt5 version is 5.10.0! You have version ${Qt5Widgets_VERSION} installed, please upgrade!")
|
||||
if(CMAKE_SYSTEM MATCHES "Linux")
|
||||
message(FATAL_ERROR "Most distros do not provide an up-to-date version of Qt.
|
||||
If you're on Ubuntu or Linux Mint, there are PPAs you can use to install one of the latest qt5 versions.
|
||||
https://launchpad.net/~beineri/+archive/ubuntu/opt-qt-5.11.0-bionic
|
||||
https://launchpad.net/~beineri/+archive/ubuntu/opt-qt-5.11.0-xenial
|
||||
https://launchpad.net/~beineri/+archive/ubuntu/opt-qt-5.10.1-trusty
|
||||
just make sure to run
|
||||
source /opt/qt511/bin/qt511-env.sh
|
||||
respective
|
||||
source /opt/qt510/bin/qt510-env.sh
|
||||
before re-running cmake")
|
||||
elseif(WIN32)
|
||||
message(FATAL_ERROR "You can download the latest version of Qt5 here: https://www.qt.io/download-open-source/")
|
||||
else()
|
||||
message(FATAL_ERROR "Look online for instructions on installing an up-to-date Qt5 on ${CMAKE_SYSTEM}.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message("CMake was unable to find Qt5!")
|
||||
if(WIN32)
|
||||
message(FATAL_ERROR "Make sure the QTDIR env variable has been set properly. (for example C:\\Qt\\5.11.1\\msvc2017_64\\)")
|
||||
elseif(CMAKE_SYSTEM MATCHES "Linux")
|
||||
message(FATAL_ERROR "Make sure to install your distro's qt5 package!")
|
||||
else()
|
||||
message(FATAL_ERROR "You need to have Qt5 installed, look online for instructions on installing Qt5 on ${CMAKE_SYSTEM}.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_library(3rdparty::qt5 ALIAS 3rdparty_qt5)
|
||||
Vendored
+5531
-4682
File diff suppressed because it is too large
Load Diff
Vendored
+3897
-2316
File diff suppressed because it is too large
Load Diff
+1
Submodule 3rdparty/xxHash added at 7cc9639699
Vendored
+59
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{939FE206-1182-ABC3-1234-FEAB88E98404}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\common_default.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="..\common_default_macros.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\rpcs3_default.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="..\rpcs3_debug.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="..\rpcs3_release.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="xxHash/xxhash.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="LICENSE" />
|
||||
<Text Include="CMakeLists.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="xxHash/xxhash.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
+1
Submodule 3rdparty/yaml-cpp added at eca9cfd648
+2
-2
@@ -13,13 +13,13 @@
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{FDC361C5-7734-493B-8CFB-037308B35122}</ProjectGuid>
|
||||
<RootNamespace>yamlcpp</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\common_default.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="..\common_default_macros.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
Vendored
+1
-1
@@ -2,7 +2,7 @@
|
||||
<!--
|
||||
* zlib.props - location of zlib source
|
||||
*
|
||||
* libpng version 1.6.20 - December 3, 2015
|
||||
* libpng version 1.6.34 - September 29, 2017
|
||||
*
|
||||
* Copyright (c) 1998-2011 Glenn Randers-Pehrson
|
||||
*
|
||||
Vendored
+105
@@ -0,0 +1,105 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="$(ZLibSrcDir)\adler32.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\compress.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\crc32.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\deflate.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\infback.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\inffast.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\inflate.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\inftrees.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\trees.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\uncompr.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\zutil.c" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{60F89955-91C6-3A36-8000-13C592FEC2DF}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>zlib</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\3rdparty\zlib.props" />
|
||||
<Import Project="..\common_default.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="..\common_default_macros.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)lib\$(Configuration)-$(Platform)\</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)lib\$(Configuration)-$(Platform)\</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<WarningLevel>$(WarningLevel)</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<BrowseInformation>true</BrowseInformation>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<DisableSpecificWarnings>$(DisableSpecificWarnings);4127;4131;4242;4244</DisableSpecificWarnings>
|
||||
<TreatWarningAsError>$(TreatWarningAsError)</TreatWarningAsError>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>$(WarningLevel)</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>Full</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<BrowseInformation>true</BrowseInformation>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<DisableSpecificWarnings>$(DisableSpecificWarnings);4127;4131;4242;4244</DisableSpecificWarnings>
|
||||
<TreatWarningAsError>$(TreatWarningAsError)</TreatWarningAsError>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
<Lib>
|
||||
<LinkTimeCodeGeneration>true</LinkTimeCodeGeneration>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
# Building
|
||||
|
||||
Only Windows and Linux are officially supported for building. However, various other platforms are capable of building RPCS3.
|
||||
Other instructions may be found [here](https://wiki.rpcs3.net/index.php?title=Building).
|
||||
|
||||
## Setup your environment
|
||||
|
||||
### Windows 7 or later
|
||||
|
||||
* [CMake 3.8.2+](https://www.cmake.org/download/) (add to PATH)
|
||||
* [Python 3.3+](https://www.python.org/downloads/) (add to PATH)
|
||||
* [Qt 5.10+](https://www.qt.io/download-qt-installer) (Avoid 5.11.1, due to a bug)
|
||||
* [Visual Studio 2017](https://visualstudio.microsoft.com/vs/older-downloads/)
|
||||
* [Vulkan SDK 1.1.97.0+](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html))
|
||||
|
||||
**Either add the** `QTDIR` **environment variable, e.g.** `<QtInstallFolder>\5.10.0\msvc2017_64\` **, or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools-19123)**
|
||||
|
||||
### Linux
|
||||
|
||||
These are the essentials tools to build RPCS3 on Linux. Some of them can be installed through your favorite package manager.
|
||||
|
||||
* Clang 5.0+ or GCC 8.1+
|
||||
* [CMake 3.8.2+](https://www.cmake.org/download/)
|
||||
* [Qt 5.10+](https://www.qt.io/download-qt-installer) (Avoid 5.11.1, due to a bug)
|
||||
* [Vulkan SDK 1.1.97.0+](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html))
|
||||
|
||||
**If you have an NVIDIA GPU, you may need to install the libglvnd package.**
|
||||
|
||||
#### Arch Linux
|
||||
|
||||
sudo pacman -S glew openal cmake vulkan-validation-layers qt5-base qt5-declarative
|
||||
|
||||
#### Debian & Ubuntu
|
||||
|
||||
sudo apt-get install cmake build-essential libasound2-dev libpulse-dev libopenal-dev libglew-dev zlib1g-dev libedit-dev libvulkan-dev libudev-dev git qt5-default libevdev-dev qtdeclarative5-dev qtbase5-private-dev
|
||||
|
||||
##### GCC 8.x installation
|
||||
|
||||
If the `gcc-8` package is not available on your system, use the following command
|
||||
```
|
||||
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
|
||||
```
|
||||
Then
|
||||
```
|
||||
sudo apt-get update
|
||||
sudo apt-get install gcc-8 g++-8
|
||||
```
|
||||
|
||||
You can either use `update-alternatives` to setup `gcc-8`/`g++-8` as your default compilers or prefix any `cmake` command by `CXX=g++-8 CC=gcc-8 ` to use it.
|
||||
|
||||
##### Vulkan SDK
|
||||
|
||||
For Ubuntu systems, it is strongly recommended to use the PPA from [LunarG](https://packages.lunarg.com/) which will provide a compatible Vulkan SDK to compile RPCS3. If your Vulkan SDK is older, it can lead to compilation errors.
|
||||
|
||||
Ubuntu 18.04 (Bionic Beaver)
|
||||
```
|
||||
wget -qO - http://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add -
|
||||
sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-1.1.106-bionic.list http://packages.lunarg.com/vulkan/1.1.106/lunarg-vulkan-1.1.106-bionic.list
|
||||
sudo apt update
|
||||
sudo apt install vulkan-sdk
|
||||
```
|
||||
|
||||
Ubuntu 16.04 (Xenial Xerus)
|
||||
```
|
||||
wget -qO - http://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add -
|
||||
sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-1.1.106-xenial.list http://packages.lunarg.com/vulkan/1.1.106/lunarg-vulkan-1.1.106-xenial.list
|
||||
sudo apt update
|
||||
sudo apt install vulkan-sdk
|
||||
```
|
||||
|
||||
#### Fedora
|
||||
|
||||
sudo dnf install alsa-lib-devel cmake glew glew-devel libatomic libevdev-devel libudev-devel openal-devel qt5-devel vulkan-devel
|
||||
|
||||
#### OpenSUSE
|
||||
|
||||
sudo zypper install git cmake libasound2 libpulse-devel openal-soft-devel glew-devel zlib-devel libedit-devel vulkan-devel libudev-devel libqt5-qtbase-devel libevdev-devel
|
||||
|
||||
## Setup the project
|
||||
|
||||
Clone and initialize the repository
|
||||
|
||||
```
|
||||
git clone https://github.com/RPCS3/rpcs3.git
|
||||
cd rpcs3
|
||||
git submodule update --init
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
#### Configuring the Qt plugin (if used)
|
||||
|
||||
1) Go to the Qt5 menu and edit Qt5 options.
|
||||
2) Add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\5.10.0\msvc2017_64`.
|
||||
3) While selecting the rpcs3qt project, go to Qt5->Project Setting and select the version you added.
|
||||
|
||||
#### Building the projects
|
||||
|
||||
Open `rpcs3.sln`. The recommended build configuration is `Release - LLVM` for all purposes.
|
||||
|
||||
You may want to download precompiled [LLVM libs](https://github.com/RPCS3/llvm/releases/download/continuous-master/llvmlibs.7z) and extract to root rpcs3 folder (which contains `rpcs3.sln`), as well as download and extract [additional libs](https://drive.google.com/uc?export=download&id=1A2eOMmCO714i0U7J0qI4aEMKnuWl8l_R) to `lib\%CONFIGURATION%-x64\` to speed up compilation time (unoptimised/debug libs are currently not available precompiled).
|
||||
|
||||
If you're not using precompiled libs, build the projects in *__BUILD_BEFORE* folder: right-click on every project > *Build*.
|
||||
|
||||
`Build > Build Solution`
|
||||
|
||||
### Linux
|
||||
|
||||
While still in the project root:
|
||||
|
||||
1) `cd .. && mkdir rpcs3_build && cd rpcs3_build`
|
||||
2) `cmake ../rpcs3/ && make` or `CXX=g++-8 CC=gcc-8 cmake ../rpcs3/ && make` to force these compilers
|
||||
3) Run RPCS3 with `./bin/rpcs3`
|
||||
|
||||
When using GDB, configure it to ignore SIGSEGV signal (`handle SIGSEGV nostop noprint`).
|
||||
If desired, use the various build options in [CMakeLists](https://github.com/RPCS3/rpcs3/blob/master/CMakeLists.txt).
|
||||
+59
-29
@@ -1,19 +1,46 @@
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
cmake_minimum_required(VERSION 3.8.2)
|
||||
|
||||
project(rpcs3)
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8)
|
||||
message(FATAL_ERROR "RPCS3 requires at least gcc-8.")
|
||||
endif()
|
||||
elseif(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
|
||||
message(FATAL_ERROR "RPCS3 requires at least clang-5.0.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(WITH_GDB "WITH_GDB" OFF)
|
||||
option(WITHOUT_LLVM "WITHOUT_LLVM" OFF)
|
||||
option(USE_NATIVE_INSTRUCTIONS "USE_NATIVE_INSTRUCTIONS makes rpcs3 compile with -march=native, which is useful for local builds, but not good for packages." ON)
|
||||
option(WITH_LLVM "Enable usage of LLVM library" ON)
|
||||
option(BUILD_LLVM_SUBMODULE "Build LLVM from git submodule" ON)
|
||||
option(USE_ALSA "ALSA audio backend" ON)
|
||||
option(USE_PULSE "PulseAudio audio backend" ON)
|
||||
option(USE_LIBEVDEV "libevdev-based joystick support" ON)
|
||||
option(USE_DISCORD_RPC "Discord rich presence integration" ON)
|
||||
option(USE_SYSTEM_ZLIB "Prefer system ZLIB instead of the builtin one" ON)
|
||||
option(USE_VULKAN "Vulkan render backend" ON)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/rpcs3/cmake_modules")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
if (WITH_GDB)
|
||||
add_definitions(-DWITH_GDB_DEBUGGER)
|
||||
endif()
|
||||
|
||||
set(ASMJIT_STATIC TRUE)
|
||||
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
message(STATUS "No build type selected, default to Release")
|
||||
set(CMAKE_BUILD_TYPE "Release")
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
add_definitions(-DNDEBUG)
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
message( FATAL_ERROR "RPCS3 can only be compiled on 64-bit platforms." )
|
||||
endif()
|
||||
@@ -24,36 +51,39 @@ if (CCACHE_FOUND)
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
|
||||
endif()
|
||||
|
||||
add_definitions(-DCMAKE_BUILD)
|
||||
|
||||
# We use libpng's static library and don't need to build the shared library and run the tests
|
||||
set(PNG_SHARED OFF CACHE BOOL "Build shared lib." FORCE)
|
||||
set(PNG_TESTS OFF CACHE BOOL "Build tests." FORCE)
|
||||
|
||||
# Select the version of libpng to use, default is builtin
|
||||
if (NOT USE_SYSTEM_LIBPNG)
|
||||
add_subdirectory( 3rdparty/libpng )
|
||||
if(WIN32)
|
||||
add_definitions(-DUNICODE)
|
||||
add_definitions(-D_WIN32_WINNT=0x0602)
|
||||
endif()
|
||||
|
||||
option(VULKAN_PREBUILT "" OFF)
|
||||
if(APPLE)
|
||||
include_directories(/opt/local/include)
|
||||
link_directories(/opt/local/lib)
|
||||
endif()
|
||||
|
||||
# Warnings are silenced for 3rdparty code
|
||||
set(CMAKE_CXX_FLAGS -w)
|
||||
set(CMAKE_C_FLAGS -w)
|
||||
set(LLVM_ENABLE_WARNINGS OFF CACHE BOOL "")
|
||||
|
||||
if(MSVC)
|
||||
add_compile_options(/wd4530) # C++ exception handler used, but unwind semantics are not enabled
|
||||
endif()
|
||||
|
||||
add_subdirectory(Vulkan EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(asmjitsrc EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(3rdparty)
|
||||
|
||||
unset(CMAKE_CXX_FLAGS)
|
||||
unset(CMAKE_C_FLAGS)
|
||||
|
||||
if (NOT WIN32)
|
||||
add_compile_options(-pthread)
|
||||
endif()
|
||||
|
||||
# TODO: do real installation, including copying directory structure
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PROJECT_BINARY_DIR}/bin")
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PROJECT_BINARY_DIR}/bin")
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${PROJECT_BINARY_DIR}/bin")
|
||||
|
||||
add_subdirectory( Vulkan )
|
||||
add_subdirectory( rpcs3 )
|
||||
|
||||
include_directories(3rdparty/hidapi/hidapi)
|
||||
if(APPLE)
|
||||
add_subdirectory(3rdparty/hidapi/mac)
|
||||
#list(APPEND LIBS hidapi)
|
||||
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
add_subdirectory(3rdparty/hidapi/linux)
|
||||
elseif(MSVC)
|
||||
add_subdirectory(3rdparty/hidapi/windows)
|
||||
else()
|
||||
add_subdirectory(3rdparty/hidapi/libusb)
|
||||
#list(APPEND LIBS hidapi-libusb)
|
||||
endif()
|
||||
add_subdirectory(rpcs3)
|
||||
|
||||
@@ -4,103 +4,36 @@ RPCS3
|
||||
[](https://travis-ci.org/RPCS3/rpcs3)
|
||||
[](https://ci.appveyor.com/project/rpcs3/rpcs3/branch/master)
|
||||
|
||||
The world's first open-source PlayStation 3 emulator/debugger written in C++ for Windows and Linux.
|
||||
The world's first free and open-source PlayStation 3 emulator/debugger, written in C++ for Windows and Linux.
|
||||
|
||||
You can find some basic information in our [**website**](https://rpcs3.net/).
|
||||
For discussion about this emulator and PS3 emulation please visit our [**forums**](https://forums.rpcs3.net) and our [**Discord server**](https://discord.me/RPCS3).
|
||||
You can find some basic information on our [**website**](https://rpcs3.net/). Game info is being populated on the [**Wiki**](https://wiki.rpcs3.net/).
|
||||
For discussion about this emulator, PS3 emulation, and game compatibility reports, please visit our [**forums**](https://forums.rpcs3.net) and our [**Discord server**](https://discord.me/RPCS3).
|
||||
|
||||
[**Support Lead Developers Nekotekina and kd-11 on Patreon**](https://www.patreon.com/Nekotekina)
|
||||
|
||||
## Contributing
|
||||
|
||||
## Development
|
||||
If you want to help the project but do not code, the best way to help out is to test games and make bug reports. See:
|
||||
* [Quickstart](https://rpcs3.net/quickstart)
|
||||
|
||||
If you want to contribute please take a look at the [Coding Style](https://github.com/RPCS3/rpcs3/wiki/Coding-Style), [Roadmap](https://github.com/RPCS3/rpcs3/wiki/Roadmap) and [Developer Information](https://github.com/RPCS3/rpcs3/wiki/Developer-Information) pages. You should as well contact any of the developers in the forums or in Discord in order to know more about the current situation of the emulator.
|
||||
If you want to contribute as a developer, please take a look at the following pages:
|
||||
|
||||
* [Coding Style](https://github.com/RPCS3/rpcs3/wiki/Coding-Style)
|
||||
* [Developer Information](https://github.com/RPCS3/rpcs3/wiki/Developer-Information)
|
||||
* [Roadmap](https://rpcs3.net/roadmap)
|
||||
|
||||
## Dependencies
|
||||
You should also contact any of the developers in the forums or in the Discord server to learn more about the current state of the emulator.
|
||||
|
||||
### Windows
|
||||
* [Visual Studio 2015](https://www.visualstudio.com/vs/older-downloads/)
|
||||
* [Visual C++ Redistributable Packages for Visual Studio 2015](http://www.microsoft.com/en-us/download/details.aspx?id=48145)
|
||||
* [Cmake 3.1.0+](https://www.cmake.org/download/) (required; add to PATH)
|
||||
* [Python 3.3+](https://www.python.org/downloads/) (required; add to PATH)
|
||||
* [Qt 5.8+](https://www.qt.io/download-open-source/) (required; add QTDIR `<QtInstallFolder>\5.8\msvc2015_64\` environment variable if you do not want to use the Visual Studio Qt Plugin)
|
||||
* [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2015) (optional; see above)
|
||||
## Building
|
||||
|
||||
### Linux
|
||||
* [Qt 5.7+](https://www.qt.io/download-open-source/)
|
||||
* GCC 5.1+ or Clang 3.5.0+ ([not GCC 6.1](https://github.com/RPCS3/rpcs3/issues/1691))
|
||||
* Debian & Ubuntu: `sudo apt-get install cmake build-essential libasound2-dev libpulse-dev libopenal-dev libglew-dev zlib1g-dev libedit-dev libvulkan-dev libudev-dev git qt5-default`
|
||||
* Arch: `sudo pacman -S glew openal cmake llvm qt5-base`
|
||||
* Fedora: `sudo dnf install cmake glew glew-devel libatomic libudev-devel openal-devel qt5-devel vulkan-devel`
|
||||
* OpenSUSE: `sudo zypper install git cmake libasound2 libpulse-devel openal-soft-devel glew-devel zlib-devel libedit-devel vulkan-devel libudev-devel libqt5-qtbase-devel libevdev-devel`
|
||||
**If you have a NVIDIA GPU, you may need to install the libglvnd package.**
|
||||
See [BUILDING.md](BUILDING.md) for more information about how to setup an environment to build RPCS3.
|
||||
|
||||
### MacOS
|
||||
MacOS is not supported at this moment because it doesn't meet system requirements (OpenGL 4.3)
|
||||
* Xcode 6+ (tested with Xcode 6.4)
|
||||
* Install with Homebrew: `brew install glew llvm qt cmake`
|
||||
## Running
|
||||
|
||||
Check our friendly [quickstart](https://rpcs3.net/quickstart) guide to make sure your computer meets the minimum system requirements to run RPCS3.
|
||||
|
||||
## Building on Windows:
|
||||
To initialize the repository don't forget to execute `git submodule update --init` to pull the submodules.
|
||||
|
||||
### Configuring Qt
|
||||
|
||||
*If you're using Visual Studio 2017 without Qt plugin support (or simply dont want to use it):*
|
||||
1) Add `QTDIR` environment variable and set it to `<QtInstallFolder>\5.8\msvc2015_64\` </br>
|
||||
|
||||
|
||||
Open `rpcs3.sln`
|
||||
|
||||
|
||||
*If you wish to use the Visual Studio plugin for Qt:* </br>
|
||||
1) Go to the Qt5 menu and edit Qt5 options. Add the path to your Qt installation with compiler e.g. `C:\Qt\5.8\msvc2015_64`. </br>
|
||||
2) While selecting the rpcs3qt project, go to Qt5->Project Setting and select the version you added.
|
||||
|
||||
### Building the projects
|
||||
You may want to download precompiled [LLVM lib](https://drive.google.com/uc?export=download&id=0B8A6NaxhQAGRY2k3Q2Yya05lcm8) and extract to root rpcs3 folder (which contains `rpcs3.sln`), as well as download and extract [additional libs](https://drive.google.com/uc?export=download&id=0B8A6NaxhQAGRaHYxV3VOS2d0a0U) to `lib\%CONFIGURATION%-x64\` to speed up compilation time (unoptimised/debug libs are currently not available precompiled).
|
||||
|
||||
If you're not using precompiled libs, build the projects in *__BUILD_BEFORE* folder: right-click on every project > *Build*. </br></br>
|
||||
|
||||
`Build > Build Solution`</br>
|
||||
|
||||
|
||||
|
||||
## Building on Linux & Mac OS:
|
||||
|
||||
1) `git clone https://github.com/RPCS3/rpcs3.git` </br>
|
||||
2) `cd rpcs3/` </br>
|
||||
3) `git submodule update --init` </br>
|
||||
4) `cd ../ && mkdir rpcs3_build && cd rpcs3_build`
|
||||
4) `cmake ../rpcs3/ && make GitVersion && make` </br>
|
||||
5) Run RPCS3 with `./bin/rpcs3` </br>
|
||||
|
||||
If you are on MacOS and want to build with brew llvm and qt don't forget to add the following environment variables
|
||||
|
||||
* `LLVM_DIR=/usr/local/opt/llvm/` (or wherever llvm was installed).
|
||||
* `Qt5_DIR=/usr/local/opt/qt/lib/cmake/Qt5` (or wherever qt was installed).
|
||||
|
||||
When using GDB, configure it to ignore SIGSEGV signal (`handle SIGSEGV nostop noprint`).
|
||||
|
||||
|
||||
## CMake Build Options (Linux & Mac OS)
|
||||
|
||||
- ```-DUSE_SYSTEM_LIBPNG=ON/OFF``` (default = *OFF*)
|
||||
Build against the shared libpng instead of using the builtin one. libpng 1.6+ highly recommended. Try this option if you get version conflict errors or only see black game icons.
|
||||
|
||||
- ```-DUSE_SYSTEM_FFMPEG=ON/OFF``` (default = *OFF*)
|
||||
Build against the shared ffmpeg libraries instead of using the builtin patched version. Try this if the builtin version breaks the OpenGL renderer for you.
|
||||
|
||||
- ```-DWITHOUT_LLVM=ON/OFF``` (default = *OFF*)
|
||||
This forces RPCS3 to build without LLVM, not recommended.
|
||||
|
||||
- ```-DWITH_GDB=ON/OFF``` (default = *OFF*)
|
||||
This Builds RPCS3 with support for debugging PS3 games using gdb.
|
||||
|
||||
- ```-DUSE_VULKAN=ON/OFF``` (default = *ON*)
|
||||
This builds RPCS3 with Vulkan support.
|
||||
Don't forget to have your graphics driver up to date and to install the [Visual C++ Redistributable Packages for Visual Studio 2017](https://go.microsoft.com/fwlink/?LinkId=746572) if you are a Windows user.
|
||||
|
||||
## License
|
||||
|
||||
Most files are licensed under the terms of GNU GPLv2 License, see LICENSE file for details. Some files may be licensed differently, check appropriate file headers for details.
|
||||
Most files are licensed under the terms of GNU GPLv2 License; see LICENSE file for details. Some files may be licensed differently; check appropriate file headers for details.
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "Atomic.h"
|
||||
#include "util/atomic.hpp"
|
||||
#include <memory>
|
||||
#include <cstddef>
|
||||
|
||||
// Unfinished. Only std::default_delete will work as expected.
|
||||
template<typename T, typename D>
|
||||
|
||||
+29
-48
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include <cstring>
|
||||
|
||||
// 128-bit vector type and also se_storage<> storage type
|
||||
union alignas(16) v128
|
||||
@@ -307,7 +308,7 @@ struct offset32_array<v128::masked_array_t<T, N, M>>
|
||||
template <typename Arg>
|
||||
static inline u32 index32(const Arg& arg)
|
||||
{
|
||||
return SIZE_32(T) * (static_cast<u32>(arg) ^ static_cast<u32>(M));
|
||||
return u32{sizeof(T)} * (static_cast<u32>(arg) ^ static_cast<u32>(M));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -334,7 +335,10 @@ inline v128 operator~(const v128& other)
|
||||
template <typename T, std::size_t Align, std::size_t Size>
|
||||
struct se_storage
|
||||
{
|
||||
using type = std::aligned_storage_t<Size, Align>;
|
||||
struct type
|
||||
{
|
||||
alignas(Align) std::byte data[Size];
|
||||
};
|
||||
|
||||
// Unoptimized generic byteswap for unaligned data
|
||||
static void reverse(u8* dst, const u8* src)
|
||||
@@ -358,16 +362,6 @@ struct se_storage
|
||||
reverse(reinterpret_cast<u8*>(&result), reinterpret_cast<const u8*>(&src));
|
||||
return result;
|
||||
}
|
||||
|
||||
static type copy(const type& src)
|
||||
{
|
||||
type result;
|
||||
for (std::size_t i = 0; i < Size; i++)
|
||||
{
|
||||
reinterpret_cast<u8*>(&result)[i] = reinterpret_cast<const u8*>(&src)[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@@ -386,18 +380,12 @@ struct se_storage<T, 2, 2>
|
||||
|
||||
static inline u16 to(const T& src)
|
||||
{
|
||||
return swap(reinterpret_cast<const u16&>(src));
|
||||
return swap(std::bit_cast<u16>(src));
|
||||
}
|
||||
|
||||
static inline T from(u16 src)
|
||||
{
|
||||
const u16 result = swap(src);
|
||||
return reinterpret_cast<const T&>(result);
|
||||
}
|
||||
|
||||
static inline T copy(const T& src)
|
||||
{
|
||||
return src;
|
||||
return std::bit_cast<T, u16>(swap(src));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -417,18 +405,12 @@ struct se_storage<T, 4, 4>
|
||||
|
||||
static inline u32 to(const T& src)
|
||||
{
|
||||
return swap(reinterpret_cast<const u32&>(src));
|
||||
return swap(std::bit_cast<u32>(src));
|
||||
}
|
||||
|
||||
static inline T from(u32 src)
|
||||
{
|
||||
const u32 result = swap(src);
|
||||
return reinterpret_cast<const T&>(result);
|
||||
}
|
||||
|
||||
static inline T copy(const T& src)
|
||||
{
|
||||
return src;
|
||||
return std::bit_cast<T, u32>(swap(src));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -448,18 +430,12 @@ struct se_storage<T, 8, 8>
|
||||
|
||||
static inline u64 to(const T& src)
|
||||
{
|
||||
return swap(reinterpret_cast<const u64&>(src));
|
||||
return swap(std::bit_cast<u64>(src));
|
||||
}
|
||||
|
||||
static inline T from(u64 src)
|
||||
{
|
||||
const u64 result = swap(src);
|
||||
return reinterpret_cast<const T&>(result);
|
||||
}
|
||||
|
||||
static inline T copy(const T& src)
|
||||
{
|
||||
return src;
|
||||
return std::bit_cast<T, u64>(swap(src));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -475,18 +451,12 @@ struct se_storage<T, 16, 16>
|
||||
|
||||
static inline v128 to(const T& src)
|
||||
{
|
||||
return swap(reinterpret_cast<const v128&>(src));
|
||||
return swap(std::bit_cast<v128>(src));
|
||||
}
|
||||
|
||||
static inline T from(const v128& src)
|
||||
{
|
||||
const v128 result = swap(src);
|
||||
return reinterpret_cast<const T&>(result);
|
||||
}
|
||||
|
||||
static inline T copy(const T& src)
|
||||
{
|
||||
return src;
|
||||
return std::bit_cast<T, v128>(swap(src));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -520,6 +490,11 @@ public:
|
||||
return storage::from(m_data);
|
||||
}
|
||||
|
||||
stype& raw()
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
se_t& operator=(const se_t&) = default;
|
||||
|
||||
se_t& operator=(type value)
|
||||
@@ -554,27 +529,33 @@ public:
|
||||
se_t() = default;
|
||||
|
||||
se_t(type value)
|
||||
: m_data(reinterpret_cast<const stype&>(value))
|
||||
: m_data(std::bit_cast<stype>(value))
|
||||
{
|
||||
}
|
||||
|
||||
type value() const
|
||||
{
|
||||
return storage::copy(reinterpret_cast<const type&>(m_data));
|
||||
return std::bit_cast<type>(m_data);
|
||||
}
|
||||
|
||||
stype& raw()
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
se_t& operator=(const se_t& value) = default;
|
||||
|
||||
se_t& operator=(type value)
|
||||
{
|
||||
return m_data = reinterpret_cast<const stype&>(value), *this;
|
||||
m_data = std::bit_cast<stype>(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
using simple_type = simple_t<T>;
|
||||
|
||||
operator type() const
|
||||
{
|
||||
return storage::copy(reinterpret_cast<const type&>(m_data));
|
||||
return value();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include <limits>
|
||||
|
||||
template<typename T, uint N>
|
||||
struct bf_base
|
||||
@@ -15,11 +16,11 @@ struct bf_base
|
||||
// Field bitsize
|
||||
static constexpr uint bitsize = N;
|
||||
|
||||
// Value mask
|
||||
static constexpr utype vmask = static_cast<utype>(~utype{} >> (bitmax - bitsize));
|
||||
|
||||
// All ones mask
|
||||
static constexpr utype mask1 = static_cast<utype>(~utype{});
|
||||
static constexpr utype mask1 = std::numeric_limits<utype>::max();
|
||||
|
||||
// Value mask
|
||||
static constexpr utype vmask = mask1 >> (bitmax - bitsize);
|
||||
|
||||
protected:
|
||||
type m_data;
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
#pragma once
|
||||
|
||||
#include <Utilities/types.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "windows.h"
|
||||
#include "tlhelp32.h"
|
||||
#else
|
||||
#include "stdlib.h"
|
||||
#include "sys/times.h"
|
||||
#include "sys/types.h"
|
||||
#include "unistd.h"
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include <mach/mach_init.h>
|
||||
# include <mach/task.h>
|
||||
# include <mach/vm_map.h>
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
# include <dirent.h>
|
||||
#endif
|
||||
|
||||
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
# include <sys/sysctl.h>
|
||||
# if defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
# include <sys/user.h>
|
||||
# endif
|
||||
|
||||
# if defined(__NetBSD__)
|
||||
# undef KERN_PROC
|
||||
# define KERN_PROC KERN_PROC2
|
||||
# define kinfo_proc kinfo_proc2
|
||||
# endif
|
||||
|
||||
# if defined(__DragonFly__)
|
||||
# define KP_NLWP(kp) (kp.kp_nthreads)
|
||||
# elif defined(__FreeBSD__)
|
||||
# define KP_NLWP(kp) (kp.ki_numthreads)
|
||||
# elif defined(__NetBSD__)
|
||||
# define KP_NLWP(kp) (kp.p_nlwps)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
class CPUStats
|
||||
{
|
||||
#ifdef _WIN32
|
||||
HANDLE m_self;
|
||||
using time_type = ULARGE_INTEGER;
|
||||
#else
|
||||
using time_type = clock_t;
|
||||
#endif
|
||||
|
||||
private:
|
||||
s32 m_num_processors;
|
||||
time_type m_last_cpu, m_sys_cpu, m_usr_cpu;
|
||||
|
||||
public:
|
||||
CPUStats()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SYSTEM_INFO sysInfo;
|
||||
FILETIME ftime, fsys, fuser;
|
||||
|
||||
GetSystemInfo(&sysInfo);
|
||||
m_num_processors = sysInfo.dwNumberOfProcessors;
|
||||
|
||||
GetSystemTimeAsFileTime(&ftime);
|
||||
memcpy(&m_last_cpu, &ftime, sizeof(FILETIME));
|
||||
|
||||
m_self = GetCurrentProcess();
|
||||
GetProcessTimes(m_self, &ftime, &ftime, &fsys, &fuser);
|
||||
memcpy(&m_sys_cpu, &fsys, sizeof(FILETIME));
|
||||
memcpy(&m_usr_cpu, &fuser, sizeof(FILETIME));
|
||||
#else
|
||||
struct tms timeSample;
|
||||
|
||||
m_last_cpu = times(&timeSample);
|
||||
m_sys_cpu = timeSample.tms_stime;
|
||||
m_usr_cpu = timeSample.tms_utime;
|
||||
m_num_processors = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
#endif
|
||||
}
|
||||
|
||||
double get_usage()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
FILETIME ftime, fsys, fusr;
|
||||
ULARGE_INTEGER now, sys, usr;
|
||||
|
||||
GetSystemTimeAsFileTime(&ftime);
|
||||
memcpy(&now, &ftime, sizeof(FILETIME));
|
||||
|
||||
GetProcessTimes(m_self, &ftime, &ftime, &fsys, &fusr);
|
||||
memcpy(&sys, &fsys, sizeof(FILETIME));
|
||||
memcpy(&usr, &fusr, sizeof(FILETIME));
|
||||
double percent = double(sys.QuadPart - m_sys_cpu.QuadPart) + (usr.QuadPart - m_usr_cpu.QuadPart);
|
||||
percent /= (now.QuadPart - m_last_cpu.QuadPart);
|
||||
percent /= m_num_processors;
|
||||
|
||||
m_last_cpu = now;
|
||||
m_usr_cpu = usr;
|
||||
m_sys_cpu = sys;
|
||||
|
||||
return std::clamp(percent * 100, 0.0, 100.0);
|
||||
#else
|
||||
struct tms timeSample;
|
||||
clock_t now;
|
||||
double percent;
|
||||
|
||||
now = times(&timeSample);
|
||||
if (now <= m_last_cpu || timeSample.tms_stime < m_sys_cpu || timeSample.tms_utime < m_usr_cpu)
|
||||
{
|
||||
// Overflow detection. Just skip this value.
|
||||
percent = -1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
percent = (timeSample.tms_stime - m_sys_cpu) + (timeSample.tms_utime - m_usr_cpu);
|
||||
percent /= (now - m_last_cpu);
|
||||
percent /= m_num_processors;
|
||||
percent *= 100;
|
||||
}
|
||||
m_last_cpu = now;
|
||||
m_sys_cpu = timeSample.tms_stime;
|
||||
m_usr_cpu = timeSample.tms_utime;
|
||||
|
||||
return percent;
|
||||
#endif
|
||||
}
|
||||
|
||||
static u32 get_thread_count()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// first determine the id of the current process
|
||||
DWORD const id = GetCurrentProcessId();
|
||||
|
||||
// then get a process list snapshot.
|
||||
HANDLE const snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
|
||||
|
||||
// initialize the process entry structure.
|
||||
PROCESSENTRY32 entry = {0};
|
||||
entry.dwSize = sizeof(entry);
|
||||
|
||||
// get the first process info.
|
||||
BOOL ret = true;
|
||||
ret = Process32First(snapshot, &entry);
|
||||
while (ret && entry.th32ProcessID != id)
|
||||
{
|
||||
ret = Process32Next(snapshot, &entry);
|
||||
}
|
||||
CloseHandle(snapshot);
|
||||
return ret ? entry.cntThreads : 0;
|
||||
#elif defined(__APPLE__)
|
||||
const task_t task = mach_task_self();
|
||||
mach_msg_type_number_t thread_count;
|
||||
thread_act_array_t thread_list;
|
||||
if (task_threads(task, &thread_list, &thread_count) != KERN_SUCCESS)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
vm_deallocate(task, reinterpret_cast<vm_address_t>(thread_list),
|
||||
sizeof(thread_t) * thread_count);
|
||||
return static_cast<u32>(thread_count);
|
||||
#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
|
||||
int mib[] = {
|
||||
CTL_KERN,
|
||||
KERN_PROC,
|
||||
KERN_PROC_PID,
|
||||
getpid(),
|
||||
#if defined(__NetBSD__)
|
||||
sizeof(struct kinfo_proc),
|
||||
1,
|
||||
#endif
|
||||
};
|
||||
u_int miblen = std::size(mib);
|
||||
struct kinfo_proc info;
|
||||
size_t size = sizeof(info);
|
||||
if (sysctl(mib, miblen, &info, &size, NULL, 0))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return KP_NLWP(info);
|
||||
#elif defined(__OpenBSD__)
|
||||
int mib[] = {
|
||||
CTL_KERN,
|
||||
KERN_PROC,
|
||||
KERN_PROC_PID | KERN_PROC_SHOW_THREADS,
|
||||
getpid(),
|
||||
sizeof(struct kinfo_proc),
|
||||
0,
|
||||
};
|
||||
u_int miblen = std::size(mib);
|
||||
|
||||
// get number of structs
|
||||
size_t size;
|
||||
if (sysctl(mib, miblen, NULL, &size, NULL, 0))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
mib[5] = size / mib[4];
|
||||
|
||||
// populate array of structs
|
||||
struct kinfo_proc info[mib[5]];
|
||||
if (sysctl(mib, miblen, &info, &size, NULL, 0))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// exclude empty members
|
||||
u32 thread_count{0};
|
||||
for (int i = 0; i < size / mib[4]; i++)
|
||||
{
|
||||
if (info[i].p_tid != -1)
|
||||
++thread_count;
|
||||
}
|
||||
return thread_count;
|
||||
#elif defined(__linux__)
|
||||
u32 thread_count{0};
|
||||
|
||||
DIR* proc_dir = opendir("/proc/self/task");
|
||||
if (proc_dir)
|
||||
{
|
||||
// proc available, iterate through tasks and count them
|
||||
struct dirent* entry;
|
||||
while ((entry = readdir(proc_dir)) != NULL)
|
||||
{
|
||||
if (entry->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
++thread_count;
|
||||
}
|
||||
|
||||
closedir(proc_dir);
|
||||
}
|
||||
return thread_count;
|
||||
#else
|
||||
// unimplemented
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
@@ -7,8 +7,6 @@
|
||||
|
||||
namespace cfg
|
||||
{
|
||||
logs::channel cfg("CFG");
|
||||
|
||||
_base::_base(type _type)
|
||||
: m_type(_type)
|
||||
{
|
||||
@@ -67,19 +65,19 @@ bool cfg::try_to_int64(s64* out, const std::string& value, s64 min, s64 max)
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (out) cfg.error("cfg::try_to_int('%s'): exception: %s", value, e.what());
|
||||
if (out) LOG_ERROR(GENERAL, "cfg::try_to_int('%s'): exception: %s", value, e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pos != value.size())
|
||||
{
|
||||
if (out) cfg.error("cfg::try_to_int('%s'): unexpected characters (pos=%zu)", value, pos);
|
||||
if (out) LOG_ERROR(GENERAL, "cfg::try_to_int('%s'): unexpected characters (pos=%zu)", value, pos);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result < min || result > max)
|
||||
{
|
||||
if (out) cfg.error("cfg::try_to_int('%s'): out of bounds (%lld..%lld)", value, min, max);
|
||||
if (out) LOG_ERROR(GENERAL, "cfg::try_to_int('%s'): out of bounds (%lld..%lld)", value, min, max);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -119,13 +117,13 @@ bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) f
|
||||
|
||||
if (pos != value.size())
|
||||
{
|
||||
if (out) cfg.error("cfg::try_to_enum_value('%s'): unexpected characters (pos=%zu)", value, pos);
|
||||
if (out) LOG_ERROR(GENERAL, "cfg::try_to_enum_value('%s'): unexpected characters (pos=%zu)", value, pos);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (val > max)
|
||||
{
|
||||
if (out) cfg.error("cfg::try_to_enum_value('%s'): out of bounds(0..%u)", value, max);
|
||||
if (out) LOG_ERROR(GENERAL, "cfg::try_to_enum_value('%s'): out of bounds(0..%u)", value, max);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -134,7 +132,7 @@ bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) f
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (out) cfg.error("cfg::try_to_enum_value('%s'): invalid enum value: %s", value, e.what());
|
||||
if (out) LOG_ERROR(GENERAL, "cfg::try_to_enum_value('%s'): invalid enum value: %s", value, e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -300,7 +298,7 @@ bool cfg::node::from_string(const std::string& value) try
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
cfg.fatal("%s thrown: %s", typeid(e).name(), e.what());
|
||||
LOG_FATAL(GENERAL, "%s thrown: %s", typeid(e).name(), e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+24
-4
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "Utilities/types.h"
|
||||
#include "Utilities/StrFmt.h"
|
||||
@@ -49,9 +49,10 @@ namespace cfg
|
||||
_base(type _type, class node* owner, const std::string& name);
|
||||
|
||||
public:
|
||||
// Disallow copy/move constructors and assignments
|
||||
_base(const _base&) = delete;
|
||||
|
||||
_base& operator=(const _base&) = delete;
|
||||
|
||||
// Get type
|
||||
type get_type() const { return m_type; }
|
||||
|
||||
@@ -132,6 +133,11 @@ namespace cfg
|
||||
return m_value;
|
||||
}
|
||||
|
||||
const bool& get() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void from_default() override;
|
||||
|
||||
std::string to_string() const override
|
||||
@@ -178,6 +184,11 @@ namespace cfg
|
||||
return m_value;
|
||||
}
|
||||
|
||||
const T& get() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void from_default() override
|
||||
{
|
||||
m_value = def;
|
||||
@@ -224,6 +235,10 @@ namespace cfg
|
||||
public:
|
||||
int_type def;
|
||||
|
||||
// Expose range
|
||||
static const s64 max = Max;
|
||||
static const s64 min = Min;
|
||||
|
||||
_int(node* owner, const std::string& name, int_type def = std::min<int_type>(Max, std::max<int_type>(Min, 0)))
|
||||
: _base(type::_int, owner, name)
|
||||
, m_value(def)
|
||||
@@ -236,6 +251,11 @@ namespace cfg
|
||||
return m_value;
|
||||
}
|
||||
|
||||
const int_type& get() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void from_default() override
|
||||
{
|
||||
m_value = def;
|
||||
@@ -331,7 +351,7 @@ namespace cfg
|
||||
{
|
||||
}
|
||||
|
||||
std::set<std::string> get_set() const
|
||||
const std::set<std::string>& get_set() const
|
||||
{
|
||||
return m_set;
|
||||
}
|
||||
@@ -366,7 +386,7 @@ namespace cfg
|
||||
{
|
||||
}
|
||||
|
||||
std::map<std::string, logs::level> get_map() const
|
||||
const std::map<std::string, logs::level>& get_map() const
|
||||
{
|
||||
return m_map;
|
||||
}
|
||||
|
||||
+389
-121
@@ -9,6 +9,7 @@
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <typeinfo>
|
||||
#include <map>
|
||||
|
||||
using namespace std::literals::string_literals;
|
||||
|
||||
@@ -124,6 +125,7 @@ static fs::error to_error(DWORD e)
|
||||
#include <sys/types.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/uio.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
@@ -134,10 +136,12 @@ static fs::error to_error(DWORD e)
|
||||
#if defined(__APPLE__)
|
||||
#include <copyfile.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#elif defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
#include <sys/socket.h> // sendfile
|
||||
#elif defined(__linux__) || defined(__sun)
|
||||
#include <sys/sendfile.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/fs.h>
|
||||
#else
|
||||
#include <fstream>
|
||||
#endif
|
||||
|
||||
static fs::error to_error(int e)
|
||||
@@ -191,6 +195,43 @@ namespace fs
|
||||
// Do notning
|
||||
}
|
||||
|
||||
fs::native_handle fs::file_base::get_handle()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
u64 file_base::write_gather(const iovec_clone* buffers, u64 buf_count)
|
||||
{
|
||||
u64 total = 0;
|
||||
|
||||
for (u64 i = 0; i < buf_count; i++)
|
||||
{
|
||||
if (!buffers[i].iov_base || buffers[i].iov_len + total < total)
|
||||
{
|
||||
g_tls_error = error::inval;
|
||||
return -1;
|
||||
}
|
||||
|
||||
total += buffers[i].iov_len;
|
||||
}
|
||||
|
||||
const auto buf = std::make_unique<uchar[]>(total);
|
||||
|
||||
u64 copied = 0;
|
||||
|
||||
for (u64 i = 0; i < buf_count; i++)
|
||||
{
|
||||
std::memcpy(buf.get() + copied, buffers[i].iov_base, buffers[i].iov_len);
|
||||
copied += buffers[i].iov_len;
|
||||
}
|
||||
|
||||
return this->write(buf.get(), total);
|
||||
}
|
||||
|
||||
dir_base::~dir_base()
|
||||
{
|
||||
}
|
||||
@@ -216,7 +257,7 @@ std::shared_ptr<fs::device_base> fs::device_manager::get_device(const std::strin
|
||||
|
||||
std::shared_ptr<fs::device_base> fs::device_manager::set_device(const std::string& name, const std::shared_ptr<device_base>& device)
|
||||
{
|
||||
writer_lock lock(m_mutex);
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
return m_map[name] = device;
|
||||
}
|
||||
@@ -283,7 +324,7 @@ bool fs::stat(const std::string& path, stat_t& info)
|
||||
info.size = (u64)attrs.nFileSizeLow | ((u64)attrs.nFileSizeHigh << 32);
|
||||
info.atime = to_time(attrs.ftLastAccessTime);
|
||||
info.mtime = to_time(attrs.ftLastWriteTime);
|
||||
info.ctime = to_time(attrs.ftCreationTime);
|
||||
info.ctime = info.mtime;
|
||||
#else
|
||||
struct ::stat file_info;
|
||||
if (::stat(path.c_str(), &file_info) != 0)
|
||||
@@ -297,9 +338,12 @@ bool fs::stat(const std::string& path, stat_t& info)
|
||||
info.size = file_info.st_size;
|
||||
info.atime = file_info.st_atime;
|
||||
info.mtime = file_info.st_mtime;
|
||||
info.ctime = file_info.st_ctime;
|
||||
info.ctime = info.mtime;
|
||||
#endif
|
||||
|
||||
if (info.atime < info.mtime)
|
||||
info.atime = info.mtime;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -495,18 +539,30 @@ bool fs::create_dir(const std::string& path)
|
||||
|
||||
bool fs::create_path(const std::string& path)
|
||||
{
|
||||
const auto& parent = get_parent_dir(path);
|
||||
const std::string parent = get_parent_dir(path);
|
||||
|
||||
if (!parent.empty() && !is_dir(parent) && !create_path(parent))
|
||||
if (!parent.empty() && !create_path(parent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return create_dir(path);
|
||||
if (!create_dir(path) && g_tls_error != error::exist)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fs::remove_dir(const std::string& path)
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
// Don't allow removing empty path (TODO)
|
||||
g_tls_error = fs::error::noent;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto device = get_virtual_device(path))
|
||||
{
|
||||
return device->remove_dir(path);
|
||||
@@ -533,6 +589,13 @@ bool fs::remove_dir(const std::string& path)
|
||||
|
||||
bool fs::rename(const std::string& from, const std::string& to, bool overwrite)
|
||||
{
|
||||
if (from.empty() || to.empty())
|
||||
{
|
||||
// Don't allow opening empty path (TODO)
|
||||
g_tls_error = fs::error::noent;
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto device = get_virtual_device(from);
|
||||
|
||||
if (device != get_virtual_device(to))
|
||||
@@ -577,6 +640,21 @@ bool fs::rename(const std::string& from, const std::string& to, bool overwrite)
|
||||
|
||||
return true;
|
||||
#else
|
||||
|
||||
#ifdef __linux__
|
||||
if (syscall(SYS_renameat2, AT_FDCWD, from.c_str(), AT_FDCWD, to.c_str(), overwrite ? 0 : 1 /* RENAME_NOREPLACE */) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the filesystem doesn't support RENAME_NOREPLACE, it returns EINVAL. Retry with fallback method in that case.
|
||||
if (errno != EINVAL || overwrite)
|
||||
{
|
||||
g_tls_error = to_error(errno);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!overwrite && exists(to))
|
||||
{
|
||||
g_tls_error = fs::error::exist;
|
||||
@@ -610,7 +688,7 @@ bool fs::copy_file(const std::string& from, const std::string& to, bool overwrit
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
#elif defined(__APPLE__) || defined(__linux__) || defined(__sun)
|
||||
/* Source: http://stackoverflow.com/questions/2180079/how-can-i-copy-a-file-on-unix-using-c */
|
||||
|
||||
const int input = ::open(from.c_str(), O_RDONLY);
|
||||
@@ -634,17 +712,13 @@ bool fs::copy_file(const std::string& from, const std::string& to, bool overwrit
|
||||
#if defined(__APPLE__)
|
||||
// fcopyfile works on OS X 10.5+
|
||||
if (::fcopyfile(input, output, 0, COPYFILE_ALL))
|
||||
#elif defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
if (::sendfile(input, output, 0, 0, NULL, NULL, 0))
|
||||
#elif defined(__linux__) || defined(__sun)
|
||||
// sendfile will work with non-socket output (i.e. regular file) on Linux 2.6.33+
|
||||
off_t bytes_copied = 0;
|
||||
struct ::stat fileinfo = { 0 };
|
||||
if (::fstat(input, &fileinfo) || ::sendfile(output, input, &bytes_copied, fileinfo.st_size))
|
||||
#else // NetBSD, OpenBSD, etc.
|
||||
fmt::throw_exception("fs::copy_file() isn't implemented for this platform.\nFrom: %s\nTo: %s", from, to);
|
||||
errno = 0;
|
||||
if (true)
|
||||
#else
|
||||
#error "Native file copy implementation is missing"
|
||||
#endif
|
||||
{
|
||||
const int err = errno;
|
||||
@@ -657,6 +731,31 @@ bool fs::copy_file(const std::string& from, const std::string& to, bool overwrit
|
||||
|
||||
::close(input);
|
||||
::close(output);
|
||||
return true;
|
||||
#else // fallback
|
||||
{
|
||||
std::ifstream out{to, std::ios::binary};
|
||||
if (out.good() && !overwrite)
|
||||
{
|
||||
g_tls_error = to_error(EEXIST);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::ifstream in{from, std::ios::binary};
|
||||
std::ofstream out{to, std::ios::binary};
|
||||
|
||||
if (!in.good() || !out.good())
|
||||
{
|
||||
g_tls_error = to_error(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::istreambuf_iterator<char> bin(in);
|
||||
std::istreambuf_iterator<char> ein;
|
||||
std::ostreambuf_iterator<char> bout(out);
|
||||
std::copy(bin, ein, bout);
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
@@ -780,6 +879,13 @@ void fs::file::xfail() const
|
||||
|
||||
fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
// Don't allow opening empty path (TODO)
|
||||
g_tls_error = fs::error::noent;
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto device = get_virtual_device(path))
|
||||
{
|
||||
if (auto&& _file = device->open(path, mode))
|
||||
@@ -793,29 +899,34 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD access = 0;
|
||||
if (test(mode & fs::read)) access |= GENERIC_READ;
|
||||
if (test(mode & fs::write)) access |= test(mode & fs::append) ? FILE_APPEND_DATA : GENERIC_WRITE;
|
||||
if (mode & fs::read) access |= GENERIC_READ;
|
||||
if (mode & fs::write) access |= DELETE | (mode & fs::append ? FILE_APPEND_DATA : GENERIC_WRITE);
|
||||
|
||||
DWORD disp = 0;
|
||||
if (test(mode & fs::create))
|
||||
if (mode & fs::create)
|
||||
{
|
||||
disp =
|
||||
test(mode & fs::excl) ? CREATE_NEW :
|
||||
test(mode & fs::trunc) ? CREATE_ALWAYS : OPEN_ALWAYS;
|
||||
mode & fs::excl ? CREATE_NEW :
|
||||
mode & fs::trunc ? CREATE_ALWAYS : OPEN_ALWAYS;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (test(mode & fs::excl))
|
||||
if (mode & fs::excl)
|
||||
{
|
||||
g_tls_error = error::inval;
|
||||
return;
|
||||
}
|
||||
|
||||
disp = test(mode & fs::trunc) ? TRUNCATE_EXISTING : OPEN_EXISTING;
|
||||
disp = mode & fs::trunc ? TRUNCATE_EXISTING : OPEN_EXISTING;
|
||||
}
|
||||
|
||||
DWORD share = FILE_SHARE_READ;
|
||||
if (!test(mode & fs::unshare))
|
||||
DWORD share = 0;
|
||||
if (!(mode & fs::unread) || !(mode & fs::write))
|
||||
{
|
||||
share |= FILE_SHARE_READ;
|
||||
}
|
||||
|
||||
if (!(mode & (fs::lock + fs::unread)) || !(mode & fs::write))
|
||||
{
|
||||
share |= FILE_SHARE_WRITE | FILE_SHARE_DELETE;
|
||||
}
|
||||
@@ -828,7 +939,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
return;
|
||||
}
|
||||
|
||||
class windows_file final : public file_base, public get_native_handle
|
||||
class windows_file final : public file_base
|
||||
{
|
||||
const HANDLE m_handle;
|
||||
|
||||
@@ -854,7 +965,10 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
info.size = this->size();
|
||||
info.atime = to_time(basic_info.LastAccessTime);
|
||||
info.mtime = to_time(basic_info.ChangeTime);
|
||||
info.ctime = to_time(basic_info.CreationTime);
|
||||
info.ctime = info.mtime;
|
||||
|
||||
if (info.atime < info.mtime)
|
||||
info.atime = info.mtime;
|
||||
|
||||
return info;
|
||||
}
|
||||
@@ -928,7 +1042,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
return size.QuadPart;
|
||||
}
|
||||
|
||||
native_handle get() override
|
||||
native_handle get_handle() override
|
||||
{
|
||||
return m_handle;
|
||||
}
|
||||
@@ -938,16 +1052,23 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
#else
|
||||
int flags = 0;
|
||||
|
||||
if (test(mode & fs::read) && test(mode & fs::write)) flags |= O_RDWR;
|
||||
else if (test(mode & fs::read)) flags |= O_RDONLY;
|
||||
else if (test(mode & fs::write)) flags |= O_WRONLY;
|
||||
if (mode & fs::read && mode & fs::write) flags |= O_RDWR;
|
||||
else if (mode & fs::read) flags |= O_RDONLY;
|
||||
else if (mode & fs::write) flags |= O_WRONLY;
|
||||
|
||||
if (test(mode & fs::append)) flags |= O_APPEND;
|
||||
if (test(mode & fs::create)) flags |= O_CREAT;
|
||||
if (test(mode & fs::trunc)) flags |= O_TRUNC;
|
||||
if (test(mode & fs::excl)) flags |= O_EXCL;
|
||||
if (mode & fs::append) flags |= O_APPEND;
|
||||
if (mode & fs::create) flags |= O_CREAT;
|
||||
if (mode & fs::trunc && !(mode & (fs::lock + fs::unread))) flags |= O_TRUNC;
|
||||
if (mode & fs::excl) flags |= O_EXCL;
|
||||
|
||||
const int fd = ::open(path.c_str(), flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
int perm = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
|
||||
if (mode & fs::write && mode & fs::unread)
|
||||
{
|
||||
perm = 0;
|
||||
}
|
||||
|
||||
const int fd = ::open(path.c_str(), flags, perm);
|
||||
|
||||
if (fd == -1)
|
||||
{
|
||||
@@ -955,14 +1076,20 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
return;
|
||||
}
|
||||
|
||||
if (test(mode & fs::unshare) && ::flock(fd, LOCK_EX | LOCK_NB) != 0)
|
||||
if (mode & fs::write && mode & (fs::lock + fs::unread) && ::flock(fd, LOCK_EX | LOCK_NB) != 0)
|
||||
{
|
||||
g_tls_error = errno == EWOULDBLOCK ? fs::error::acces : to_error(errno);
|
||||
::close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
class unix_file final : public file_base, public get_native_handle
|
||||
if (mode & fs::trunc && mode & (fs::lock + fs::unread))
|
||||
{
|
||||
// Postpone truncation in order to avoid using O_TRUNC on a locked file
|
||||
::ftruncate(fd, 0);
|
||||
}
|
||||
|
||||
class unix_file final : public file_base
|
||||
{
|
||||
const int m_fd;
|
||||
|
||||
@@ -988,7 +1115,10 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
info.size = file_info.st_size;
|
||||
info.atime = file_info.st_atime;
|
||||
info.mtime = file_info.st_mtime;
|
||||
info.ctime = file_info.st_ctime;
|
||||
info.ctime = info.mtime;
|
||||
|
||||
if (info.atime < info.mtime)
|
||||
info.atime = info.mtime;
|
||||
|
||||
return info;
|
||||
}
|
||||
@@ -1052,10 +1182,21 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
return file_info.st_size;
|
||||
}
|
||||
|
||||
native_handle get() override
|
||||
native_handle get_handle() override
|
||||
{
|
||||
return m_fd;
|
||||
}
|
||||
|
||||
u64 write_gather(const iovec_clone* buffers, u64 buf_count) override
|
||||
{
|
||||
static_assert(sizeof(iovec) == sizeof(iovec_clone), "Weird iovec size");
|
||||
static_assert(offsetof(iovec, iov_len) == offsetof(iovec_clone, iov_len), "Weird iovec::iov_len offset");
|
||||
|
||||
const auto result = ::writev(m_fd, (const iovec*)buffers, buf_count);
|
||||
verify("file::write_gather" HERE), result != -1;
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
m_file = std::make_unique<unix_file>(fd);
|
||||
@@ -1133,9 +1274,9 @@ fs::file::file(const void* ptr, std::size_t size)
|
||||
|
||||
fs::native_handle fs::file::get_handle() const
|
||||
{
|
||||
if (auto getter = dynamic_cast<get_native_handle*>(m_file.get()))
|
||||
if (m_file)
|
||||
{
|
||||
return getter->get();
|
||||
return m_file->get_handle();
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -1145,6 +1286,15 @@ fs::native_handle fs::file::get_handle() const
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
bool fs::file::set_delete(bool autodelete) const
|
||||
{
|
||||
FILE_DISPOSITION_INFO disp;
|
||||
disp.DeleteFileW = autodelete;
|
||||
return SetFileInformationByHandle(get_handle(), FileDispositionInfo, &disp, sizeof(disp)) != 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void fs::dir::xnull() const
|
||||
{
|
||||
fmt::throw_exception<std::logic_error>("fs::dir is null");
|
||||
@@ -1152,6 +1302,13 @@ void fs::dir::xnull() const
|
||||
|
||||
bool fs::dir::open(const std::string& path)
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
// Don't allow opening empty path (TODO)
|
||||
g_tls_error = fs::error::noent;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto device = get_virtual_device(path))
|
||||
{
|
||||
if (auto&& _dir = device->open_dir(path))
|
||||
@@ -1188,7 +1345,10 @@ bool fs::dir::open(const std::string& path)
|
||||
info.size = ((u64)found.nFileSizeHigh << 32) | (u64)found.nFileSizeLow;
|
||||
info.atime = to_time(found.ftLastAccessTime);
|
||||
info.mtime = to_time(found.ftLastWriteTime);
|
||||
info.ctime = to_time(found.ftCreationTime);
|
||||
info.ctime = info.mtime;
|
||||
|
||||
if (info.atime < info.mtime)
|
||||
info.atime = info.mtime;
|
||||
|
||||
m_entries.emplace_back(std::move(info));
|
||||
}
|
||||
@@ -1271,7 +1431,10 @@ bool fs::dir::open(const std::string& path)
|
||||
info.size = file_info.st_size;
|
||||
info.atime = file_info.st_atime;
|
||||
info.mtime = file_info.st_mtime;
|
||||
info.ctime = file_info.st_ctime;
|
||||
info.ctime = info.mtime;
|
||||
|
||||
if (info.atime < info.mtime)
|
||||
info.atime = info.mtime;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1296,8 +1459,10 @@ const std::string& fs::get_config_dir()
|
||||
std::string dir;
|
||||
|
||||
#ifdef _WIN32
|
||||
wchar_t buf[2048];
|
||||
if (GetModuleFileName(NULL, buf, ::size32(buf)) - 1 >= ::size32(buf) - 1)
|
||||
wchar_t buf[32768];
|
||||
constexpr DWORD size = static_cast<DWORD>(std::size(buf));
|
||||
if (GetEnvironmentVariable(L"RPCS3_CONFIG_DIR", buf, size) - 1 >= size - 1 &&
|
||||
GetModuleFileName(NULL, buf, size) - 1 >= size - 1)
|
||||
{
|
||||
MessageBoxA(0, fmt::format("GetModuleFileName() failed: error %u.", GetLastError()).c_str(), "fs::get_config_dir()", MB_ICONERROR);
|
||||
return dir; // empty
|
||||
@@ -1309,16 +1474,22 @@ const std::string& fs::get_config_dir()
|
||||
|
||||
dir.resize(dir.rfind('/') + 1);
|
||||
#else
|
||||
if (const char* home = ::getenv("XDG_CONFIG_HOME"))
|
||||
dir = home;
|
||||
|
||||
#ifdef __APPLE__
|
||||
if (const char* home = ::getenv("HOME"))
|
||||
dir = home + "/Library/Application Support"s;
|
||||
#else
|
||||
if (const char* conf = ::getenv("XDG_CONFIG_HOME"))
|
||||
dir = conf;
|
||||
else if (const char* home = ::getenv("HOME"))
|
||||
dir = home + "/.config"s;
|
||||
#endif
|
||||
else // Just in case
|
||||
dir = "./config";
|
||||
|
||||
dir += "/rpcs3/";
|
||||
|
||||
if (!is_dir(dir) && !create_path(dir))
|
||||
if (!create_path(dir))
|
||||
{
|
||||
std::printf("Failed to create configuration directory '%s' (%d).\n", dir.c_str(), errno);
|
||||
}
|
||||
@@ -1330,102 +1501,83 @@ const std::string& fs::get_config_dir()
|
||||
return s_dir;
|
||||
}
|
||||
|
||||
std::string fs::get_data_dir(const std::string& prefix, const std::string& location, const std::string& suffix)
|
||||
const std::string& fs::get_cache_dir()
|
||||
{
|
||||
static const std::string s_dir = []
|
||||
{
|
||||
const std::string& dir = get_config_dir() + "data/";
|
||||
std::string dir;
|
||||
|
||||
if (!is_dir(dir) && !create_path(dir))
|
||||
#ifdef _WIN32
|
||||
dir = get_config_dir();
|
||||
#else
|
||||
|
||||
#ifdef __APPLE__
|
||||
if (const char* home = ::getenv("HOME"))
|
||||
dir = home + "/Library/Caches"s;
|
||||
#else
|
||||
if (const char* cache = ::getenv("XDG_CACHE_HOME"))
|
||||
dir = cache;
|
||||
else if (const char* conf = ::getenv("XDG_CONFIG_HOME"))
|
||||
dir = conf;
|
||||
else if (const char* home = ::getenv("HOME"))
|
||||
dir = home + "/.cache"s;
|
||||
#endif
|
||||
else // Just in case
|
||||
dir = "./cache";
|
||||
|
||||
dir += "/rpcs3/";
|
||||
|
||||
if (!create_path(dir))
|
||||
{
|
||||
return get_config_dir();
|
||||
std::printf("Failed to create configuration directory '%s' (%d).\n", dir.c_str(), errno);
|
||||
}
|
||||
#endif
|
||||
|
||||
return dir;
|
||||
}();
|
||||
|
||||
std::vector<u8> buf;
|
||||
buf.reserve(location.size() + 1);
|
||||
return s_dir;
|
||||
}
|
||||
|
||||
// Normalize location
|
||||
for (char c : location)
|
||||
bool fs::remove_all(const std::string& path, bool remove_root)
|
||||
{
|
||||
if (const auto root_dir = dir(path))
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (c == '/' || c == '\\')
|
||||
#else
|
||||
if (c == '/')
|
||||
#endif
|
||||
for (const auto& entry : root_dir)
|
||||
{
|
||||
if (buf.empty() || buf.back() != '/')
|
||||
if (entry.name == "." || entry.name == "..")
|
||||
{
|
||||
buf.push_back('/');
|
||||
continue;
|
||||
}
|
||||
|
||||
continue;
|
||||
if (entry.is_directory == false)
|
||||
{
|
||||
if (!remove_file(path + '/' + entry.name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry.is_directory == true)
|
||||
{
|
||||
if (!remove_all(path + '/' + entry.name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buf.push_back(c);
|
||||
}
|
||||
|
||||
// Calculate hash
|
||||
u8 hash[20];
|
||||
sha1(buf.data(), buf.size(), hash);
|
||||
|
||||
// Concatenate
|
||||
std::string&& result = fmt::format("%s%s/%016llx%08x-%s/", s_dir, prefix, reinterpret_cast<be_t<u64>&>(hash[0]), reinterpret_cast<be_t<u32>&>(hash[8]), suffix);
|
||||
|
||||
if (!is_dir(result))
|
||||
else
|
||||
{
|
||||
// Create dir if necessary
|
||||
if (create_path(result))
|
||||
{
|
||||
// Acknowledge original location
|
||||
file(result + ".location", rewrite).write(buf);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string fs::get_data_dir(const std::string& prefix, const std::string& path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const auto& delim = "/\\";
|
||||
#else
|
||||
const auto& delim = "/";
|
||||
#endif
|
||||
|
||||
// Extract file name and location
|
||||
const std::string& location = fs::get_parent_dir(path);
|
||||
const std::size_t name_pos = path.find_first_not_of(delim, location.size());
|
||||
|
||||
return fs::get_data_dir(prefix, location, name_pos == -1 ? std::string{} : path.substr(name_pos));
|
||||
}
|
||||
|
||||
void fs::remove_all(const std::string& path, bool remove_root)
|
||||
{
|
||||
for (const auto& entry : dir(path))
|
||||
{
|
||||
if (entry.name == "." || entry.name == "..")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry.is_directory == false)
|
||||
{
|
||||
remove_file(path + '/' + entry.name);
|
||||
}
|
||||
|
||||
if (entry.is_directory == true)
|
||||
{
|
||||
remove_all(path + '/' + entry.name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (remove_root)
|
||||
{
|
||||
remove_dir(path);
|
||||
return remove_dir(path);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u64 fs::get_dir_size(const std::string& path)
|
||||
@@ -1453,6 +1605,122 @@ u64 fs::get_dir_size(const std::string& path)
|
||||
return result;
|
||||
}
|
||||
|
||||
fs::file fs::make_gather(std::vector<fs::file> files)
|
||||
{
|
||||
struct gather_stream : file_base
|
||||
{
|
||||
u64 pos = 0;
|
||||
u64 end = 0;
|
||||
std::vector<file> files;
|
||||
std::map<u64, u64> ends; // Fragment End Offset -> Index
|
||||
|
||||
gather_stream(std::vector<fs::file> arg)
|
||||
: files(std::move(arg))
|
||||
{
|
||||
// Preprocess files
|
||||
for (auto&& f : files)
|
||||
{
|
||||
end += f.size();
|
||||
ends.emplace(end, ends.size());
|
||||
}
|
||||
}
|
||||
|
||||
~gather_stream() override
|
||||
{
|
||||
}
|
||||
|
||||
fs::stat_t stat() override
|
||||
{
|
||||
fs::stat_t result{};
|
||||
|
||||
if (!files.empty())
|
||||
{
|
||||
result = files[0].stat();
|
||||
}
|
||||
|
||||
result.is_directory = false;
|
||||
result.is_writable = false;
|
||||
result.size = end;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool trunc(u64 length) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 read(void* buffer, u64 size) override
|
||||
{
|
||||
if (pos < end)
|
||||
{
|
||||
// Current pos
|
||||
const u64 start = pos;
|
||||
|
||||
// Get readable size
|
||||
if (const u64 max = std::min<u64>(size, end - pos))
|
||||
{
|
||||
u8* buf_out = static_cast<u8*>(buffer);
|
||||
u64 buf_max = max;
|
||||
|
||||
for (auto it = ends.upper_bound(pos); it != ends.end(); ++it)
|
||||
{
|
||||
// Set position for the fragment
|
||||
files[it->second].seek(pos - it->first, fs::seek_end);
|
||||
|
||||
const u64 count = std::min<u64>(it->first - pos, buf_max);
|
||||
const u64 read = files[it->second].read(buf_out, count);
|
||||
|
||||
buf_out += count;
|
||||
buf_max -= count;
|
||||
pos += read;
|
||||
|
||||
if (read < count || buf_max == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return pos - start;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 write(const void* buffer, u64 size) override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 seek(s64 offset, seek_mode whence) override
|
||||
{
|
||||
const s64 new_pos =
|
||||
whence == fs::seek_set ? offset :
|
||||
whence == fs::seek_cur ? offset + pos :
|
||||
whence == fs::seek_end ? offset + end :
|
||||
(fmt::raw_error("fs::gather_stream::seek(): invalid whence"), 0);
|
||||
|
||||
if (new_pos < 0)
|
||||
{
|
||||
fs::g_tls_error = fs::error::inval;
|
||||
return -1;
|
||||
}
|
||||
|
||||
pos = new_pos;
|
||||
return pos;
|
||||
}
|
||||
|
||||
u64 size() override
|
||||
{
|
||||
return end;
|
||||
}
|
||||
};
|
||||
|
||||
fs::file result;
|
||||
result.reset(std::make_unique<gather_stream>(std::move(files)));
|
||||
return result;
|
||||
}
|
||||
|
||||
template<>
|
||||
void fmt_class_string<fs::seek_mode>::format(std::string& out, u64 arg)
|
||||
{
|
||||
|
||||
+30
-14
@@ -25,7 +25,8 @@ namespace fs
|
||||
create,
|
||||
trunc,
|
||||
excl,
|
||||
unshare,
|
||||
lock,
|
||||
unread,
|
||||
|
||||
__bitset_enum_max
|
||||
};
|
||||
@@ -36,7 +37,8 @@ namespace fs
|
||||
constexpr auto create = +open_mode::create; // Create file if it doesn't exist
|
||||
constexpr auto trunc = +open_mode::trunc; // Clear opened file if it's not empty
|
||||
constexpr auto excl = +open_mode::excl; // Failure if the file already exists (used with `create`)
|
||||
constexpr auto unshare = +open_mode::unshare; // Prevent opening the file twice
|
||||
constexpr auto lock = +open_mode::lock; // Prevent opening the file more than once
|
||||
constexpr auto unread = +open_mode::unread; // Aggressively prevent reading the opened file (do not use)
|
||||
|
||||
constexpr auto rewrite = open_mode::write + open_mode::create + open_mode::trunc;
|
||||
|
||||
@@ -63,10 +65,11 @@ namespace fs
|
||||
s64 ctime;
|
||||
};
|
||||
|
||||
// Native handle getter
|
||||
struct get_native_handle
|
||||
// Helper, layout is equal to iovec struct
|
||||
struct iovec_clone
|
||||
{
|
||||
virtual native_handle get() = 0;
|
||||
const void* iov_base;
|
||||
std::size_t iov_len;
|
||||
};
|
||||
|
||||
// File handle base
|
||||
@@ -81,6 +84,8 @@ namespace fs
|
||||
virtual u64 write(const void* buffer, u64 size) = 0;
|
||||
virtual u64 seek(s64 offset, seek_mode whence) = 0;
|
||||
virtual u64 size() = 0;
|
||||
virtual native_handle get_handle();
|
||||
virtual u64 write_gather(const iovec_clone* buffers, u64 buf_count);
|
||||
};
|
||||
|
||||
// Directory entry (TODO)
|
||||
@@ -185,7 +190,7 @@ namespace fs
|
||||
file() = default;
|
||||
|
||||
// Open file with specified mode
|
||||
explicit file(const std::string& path, bs_t<open_mode> mode = ::fs::read);
|
||||
explicit file(const std::string& path, bs_t<open_mode> mode = ::fs::read);
|
||||
|
||||
// Open memory for read
|
||||
explicit file(const void* ptr, std::size_t size);
|
||||
@@ -364,6 +369,18 @@ namespace fs
|
||||
|
||||
// Get native handle if available
|
||||
native_handle get_handle() const;
|
||||
|
||||
// Gathered write
|
||||
u64 write_gather(const iovec_clone* buffers, u64 buf_count) const
|
||||
{
|
||||
if (!m_file) xnull();
|
||||
return m_file->write_gather(buffers, buf_count);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Windows-specific function
|
||||
bool set_delete(bool autodelete = true) const;
|
||||
#endif
|
||||
};
|
||||
|
||||
class dir final
|
||||
@@ -400,7 +417,7 @@ namespace fs
|
||||
{
|
||||
m_dir = std::move(ptr);
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<dir_base> release()
|
||||
{
|
||||
return std::move(m_dir);
|
||||
@@ -482,14 +499,11 @@ namespace fs
|
||||
// Get configuration directory
|
||||
const std::string& get_config_dir();
|
||||
|
||||
// Get data/cache directory for specified prefix and suffix
|
||||
std::string get_data_dir(const std::string& prefix, const std::string& location, const std::string& suffix);
|
||||
|
||||
// Get data/cache directory for specified prefix and path (suffix will be filename)
|
||||
std::string get_data_dir(const std::string& prefix, const std::string& path);
|
||||
// Get common cache directory
|
||||
const std::string& get_cache_dir();
|
||||
|
||||
// Delete directory and all its contents recursively
|
||||
void remove_all(const std::string& path, bool remove_root = true);
|
||||
bool remove_all(const std::string& path, bool remove_root = true);
|
||||
|
||||
// Get size of all files recursively
|
||||
u64 get_dir_size(const std::string& path);
|
||||
@@ -617,10 +631,12 @@ namespace fs
|
||||
if (fs::file f{path, mode})
|
||||
{
|
||||
// Write args sequentially
|
||||
int seq[]{ (f.write(args), 0)... };
|
||||
(f.write(args), ...);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
file make_gather(std::vector<file>);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "GDBDebugServer.h"
|
||||
#include "Log.h"
|
||||
#include <algorithm>
|
||||
#include "Emu/Memory/Memory.h"
|
||||
#include "Emu/Memory/vm.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/CPU/CPUThread.h"
|
||||
@@ -13,13 +13,13 @@
|
||||
#include "Emu/Cell/SPUThread.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include"fcntl.h"
|
||||
#include "fcntl.h"
|
||||
#endif
|
||||
|
||||
extern void ppu_set_breakpoint(u32 addr);
|
||||
extern void ppu_remove_breakpoint(u32 addr);
|
||||
|
||||
logs::channel gdbDebugServer("gdbDebugServer");
|
||||
LOG_CHANNEL(gdbDebugServer);
|
||||
|
||||
int sock_init(void)
|
||||
{
|
||||
@@ -166,9 +166,10 @@ char GDBDebugServer::read_char()
|
||||
|
||||
u8 GDBDebugServer::read_hexbyte()
|
||||
{
|
||||
char buf[2];
|
||||
read(buf, 2);
|
||||
return static_cast<u8>(strtol(buf, nullptr, 16));
|
||||
std::string s = "";
|
||||
s += read_char();
|
||||
s += read_char();
|
||||
return hex_to_u8(s);
|
||||
}
|
||||
|
||||
void GDBDebugServer::try_read_cmd(gdb_cmd & out_cmd)
|
||||
@@ -176,7 +177,7 @@ void GDBDebugServer::try_read_cmd(gdb_cmd & out_cmd)
|
||||
char c = read_char();
|
||||
//interrupt
|
||||
if (UNLIKELY(c == 0x03)) {
|
||||
out_cmd.cmd = "\0x03";
|
||||
out_cmd.cmd = '\x03';
|
||||
out_cmd.data = "";
|
||||
out_cmd.checksum = 0;
|
||||
return;
|
||||
@@ -223,7 +224,7 @@ void GDBDebugServer::try_read_cmd(gdb_cmd & out_cmd)
|
||||
}
|
||||
out_cmd.checksum = read_hexbyte();
|
||||
if (out_cmd.checksum != checksum) {
|
||||
throw new wrong_checksum_exception("Wrong checksum for packet" HERE);
|
||||
throw wrong_checksum_exception("Wrong checksum for packet" HERE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,6 +338,7 @@ bool GDBDebugServer::select_thread(u64 id)
|
||||
selected_thread = ppu.ptr;
|
||||
return true;
|
||||
}
|
||||
gdbDebugServer.warning("Unable to select thread! Is the emulator running?");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -352,7 +354,7 @@ std::string GDBDebugServer::get_reg(std::shared_ptr<ppu_thread> thread, u32 rid)
|
||||
case 65:
|
||||
return std::string(16, 'x');
|
||||
case 66:
|
||||
return u32_to_padded_hex(thread->cr_pack());
|
||||
return u32_to_padded_hex(thread->cr.pack());
|
||||
case 67:
|
||||
return u64_to_padded_hex(thread->lr);
|
||||
case 68:
|
||||
@@ -365,8 +367,8 @@ std::string GDBDebugServer::get_reg(std::shared_ptr<ppu_thread> thread, u32 rid)
|
||||
return std::string(8, 'x');
|
||||
default:
|
||||
if (rid > 70) return "";
|
||||
return (rid > 31)
|
||||
? u64_to_padded_hex(reinterpret_cast<u64&>(thread->fpr[rid - 32])) //fpr
|
||||
return (rid > 31)
|
||||
? u64_to_padded_hex(std::bit_cast<u64>(thread->fpr[rid - 32])) //fpr
|
||||
: u64_to_padded_hex(thread->gpr[rid]); //gpr
|
||||
}
|
||||
}
|
||||
@@ -381,7 +383,7 @@ bool GDBDebugServer::set_reg(std::shared_ptr<ppu_thread> thread, u32 rid, std::s
|
||||
case 65:
|
||||
return true;
|
||||
case 66:
|
||||
thread->cr_unpack(hex_to_u32(value));
|
||||
thread->cr.unpack(hex_to_u32(value));
|
||||
return true;
|
||||
case 67:
|
||||
thread->lr = hex_to_u64(value);
|
||||
@@ -399,7 +401,7 @@ bool GDBDebugServer::set_reg(std::shared_ptr<ppu_thread> thread, u32 rid, std::s
|
||||
if (rid > 70) return false;
|
||||
if (rid > 31) {
|
||||
u64 val = hex_to_u64(value);
|
||||
thread->fpr[rid - 32] = reinterpret_cast<f64&>(val);
|
||||
thread->fpr[rid - 32] = std::bit_cast<f64>(val);
|
||||
} else {
|
||||
thread->gpr[rid] = hex_to_u64(value);
|
||||
}
|
||||
@@ -427,6 +429,25 @@ bool GDBDebugServer::send_reason()
|
||||
return send_cmd_ack("S05");
|
||||
}
|
||||
|
||||
void GDBDebugServer::wait_with_interrupts() {
|
||||
char c;
|
||||
while (!paused) {
|
||||
int result = recv(client_socket, &c, 1, 0);
|
||||
|
||||
if (result == SOCKET_ERROR) {
|
||||
if (check_errno_again()) {
|
||||
thread_ctrl::wait_for(50);
|
||||
continue;
|
||||
}
|
||||
|
||||
gdbDebugServer.error("Error during socket read");
|
||||
fmt::throw_exception("Error during socket read" HERE);
|
||||
} else if (c == 0x03) {
|
||||
paused = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_extended_mode(gdb_cmd & cmd)
|
||||
{
|
||||
return send_cmd_ack("OK");
|
||||
@@ -453,24 +474,25 @@ bool GDBDebugServer::cmd_thread_info(gdb_cmd & cmd)
|
||||
result += u64_to_padded_hex(static_cast<u64>(cpu.id));
|
||||
};
|
||||
idm::select<ppu_thread>(on_select);
|
||||
idm::select<ARMv7Thread>(on_select);
|
||||
idm::select<RawSPUThread>(on_select);
|
||||
idm::select<SPUThread>(on_select);
|
||||
//idm::select<RawSPUThread>(on_select);
|
||||
//idm::select<SPUThread>(on_select);
|
||||
|
||||
//todo: this may exceed max command length
|
||||
result = "m" + result + "l";
|
||||
|
||||
|
||||
return send_cmd_ack(result);;
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_current_thread(gdb_cmd & cmd)
|
||||
{
|
||||
return send_cmd_ack(selected_thread.expired() ? "" : u64_to_padded_hex(selected_thread.lock()->id));
|
||||
return send_cmd_ack(selected_thread.expired() ? "" : ("QC" + u64_to_padded_hex(selected_thread.lock()->id)));
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_read_register(gdb_cmd & cmd)
|
||||
{
|
||||
select_thread(general_ops_thread_id);
|
||||
if (!select_thread(general_ops_thread_id)) {
|
||||
return send_cmd_ack("E02");
|
||||
}
|
||||
auto th = selected_thread.lock();
|
||||
if (th->id_type() == 1) {
|
||||
auto ppu = std::static_pointer_cast<ppu_thread>(th);
|
||||
@@ -488,7 +510,9 @@ bool GDBDebugServer::cmd_read_register(gdb_cmd & cmd)
|
||||
|
||||
bool GDBDebugServer::cmd_write_register(gdb_cmd & cmd)
|
||||
{
|
||||
select_thread(general_ops_thread_id);
|
||||
if (!select_thread(general_ops_thread_id)) {
|
||||
return send_cmd_ack("E02");
|
||||
}
|
||||
auto th = selected_thread.lock();
|
||||
if (th->id_type() == 1) {
|
||||
auto ppu = std::static_pointer_cast<ppu_thread>(th);
|
||||
@@ -517,7 +541,7 @@ bool GDBDebugServer::cmd_read_memory(gdb_cmd & cmd)
|
||||
std::string result;
|
||||
result.reserve(len * 2);
|
||||
for (u32 i = 0; i < len; ++i) {
|
||||
if (vm::check_addr(addr + i)) {
|
||||
if (vm::check_addr(addr)) {
|
||||
result += to_hexbyte(vm::read8(addr + i));
|
||||
} else {
|
||||
break;
|
||||
@@ -543,7 +567,7 @@ bool GDBDebugServer::cmd_write_memory(gdb_cmd & cmd)
|
||||
u32 len = hex_to_u32(cmd.data.substr(s + 1, s2 - s - 1));
|
||||
const char* data_ptr = (cmd.data.c_str()) + s2 + 1;
|
||||
for (u32 i = 0; i < len; ++i) {
|
||||
if (vm::check_addr(addr + i)) {
|
||||
if (vm::check_addr(addr + i, 1, vm::page_writable)) {
|
||||
u8 val;
|
||||
int res = sscanf_s(data_ptr, "%02hhX", &val);
|
||||
if (!res) {
|
||||
@@ -634,30 +658,31 @@ bool GDBDebugServer::cmd_vcont(gdb_cmd & cmd)
|
||||
{
|
||||
//todo: handle multiple actions and thread ids
|
||||
this->from_breakpoint = false;
|
||||
if (cmd.data[1] == 'c') {
|
||||
if (cmd.data[1] == 'c' || cmd.data[1] == 's') {
|
||||
select_thread(continue_ops_thread_id);
|
||||
auto ppu = std::static_pointer_cast<ppu_thread>(selected_thread.lock());
|
||||
ppu->state -= cpu_flag::dbg_pause;
|
||||
if (Emu.IsPaused()) {
|
||||
Emu.Resume();
|
||||
paused = false;
|
||||
if (cmd.data[1] == 's') {
|
||||
ppu->state += cpu_flag::dbg_step;
|
||||
}
|
||||
thread_ctrl::wait();
|
||||
//we are in all-stop mode
|
||||
Emu.Pause();
|
||||
return send_reason();
|
||||
} else if (cmd.data[1] == 's') {
|
||||
select_thread(continue_ops_thread_id);
|
||||
auto ppu = std::static_pointer_cast<ppu_thread>(selected_thread.lock());
|
||||
ppu->state += cpu_flag::dbg_step;
|
||||
ppu->state -= cpu_flag::dbg_pause;
|
||||
//special case if app didn't start yet (only loaded)
|
||||
if (!Emu.IsPaused() && !Emu.IsRunning()) {
|
||||
Emu.Run();
|
||||
}
|
||||
if (Emu.IsPaused()) {
|
||||
Emu.Resume();
|
||||
} else {
|
||||
ppu->notify();
|
||||
}
|
||||
thread_ctrl::wait();
|
||||
wait_with_interrupts();
|
||||
//we are in all-stop mode
|
||||
Emu.Pause();
|
||||
select_thread(pausedBy);
|
||||
// we have to remove dbg_pause from thread that paused execution, otherwise
|
||||
// it will be paused forever (Emu.Resume only removes dbg_global_pause)
|
||||
ppu = std::static_pointer_cast<ppu_thread>(selected_thread.lock());
|
||||
ppu->state -= cpu_flag::dbg_pause;
|
||||
return send_reason();
|
||||
}
|
||||
return send_cmd_ack("");
|
||||
@@ -729,7 +754,9 @@ void GDBDebugServer::on_task()
|
||||
return;
|
||||
}
|
||||
//stop immediately
|
||||
Emu.Pause();
|
||||
if (Emu.IsRunning()) {
|
||||
Emu.Pause();
|
||||
}
|
||||
|
||||
try {
|
||||
char hostbuf[32];
|
||||
@@ -768,7 +795,7 @@ void GDBDebugServer::on_task()
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::runtime_error& e)
|
||||
catch (std::runtime_error& e)
|
||||
{
|
||||
if (client_socket) {
|
||||
closesocket(client_socket);
|
||||
@@ -802,7 +829,16 @@ void GDBDebugServer::on_stop()
|
||||
this->stop = true;
|
||||
//just in case we are waiting for breakpoint
|
||||
this->notify();
|
||||
named_thread::on_stop();
|
||||
old_thread::on_stop();
|
||||
}
|
||||
|
||||
void GDBDebugServer::pause_from(cpu_thread* t) {
|
||||
if (paused) {
|
||||
return;
|
||||
}
|
||||
paused = true;
|
||||
pausedBy = t->id;
|
||||
notify();
|
||||
}
|
||||
|
||||
u32 g_gdb_debugger_id = 0;
|
||||
|
||||
+11
-15
@@ -40,8 +40,8 @@ public:
|
||||
const u64 ALL_THREADS = 0xffffffffffffffff;
|
||||
const u64 ANY_THREAD = 0;
|
||||
|
||||
class GDBDebugServer : public named_thread {
|
||||
|
||||
class GDBDebugServer
|
||||
{
|
||||
socket_t server_socket;
|
||||
socket_t client_socket;
|
||||
std::weak_ptr<cpu_thread> selected_thread;
|
||||
@@ -50,7 +50,7 @@ class GDBDebugServer : public named_thread {
|
||||
|
||||
//initialize server socket and start listening
|
||||
void start_server();
|
||||
//read at most cnt bytes to buf, returns nubmer of bytes actually read
|
||||
//read at most cnt bytes to buf, returns number of bytes actually read
|
||||
int read(void* buf, int cnt);
|
||||
//reads one character
|
||||
char read_char();
|
||||
@@ -60,8 +60,8 @@ class GDBDebugServer : public named_thread {
|
||||
void try_read_cmd(gdb_cmd& out_cmd);
|
||||
//reads commands until receiveing one with valid checksum
|
||||
//in case of other exception (i.e. wrong first char of command)
|
||||
//it will log exception text and return false
|
||||
//in that case best for caller would be to stop reading, because
|
||||
//it will log exception text and return false
|
||||
//in that case best for caller would be to stop reading, because
|
||||
//chance of getting correct command is low
|
||||
bool read_cmd(gdb_cmd& out_cmd);
|
||||
//send cnt bytes from buf to client
|
||||
@@ -90,6 +90,8 @@ class GDBDebugServer : public named_thread {
|
||||
//send reason of stop, returns false if sending response failed
|
||||
bool send_reason();
|
||||
|
||||
void wait_with_interrupts();
|
||||
|
||||
//commands
|
||||
bool cmd_extended_mode(gdb_cmd& cmd);
|
||||
bool cmd_reason(gdb_cmd& cmd);
|
||||
@@ -110,20 +112,14 @@ class GDBDebugServer : public named_thread {
|
||||
bool cmd_set_breakpoint(gdb_cmd& cmd);
|
||||
bool cmd_remove_breakpoint(gdb_cmd& cmd);
|
||||
|
||||
protected:
|
||||
void on_task() override final;
|
||||
void on_exit() override final;
|
||||
|
||||
public:
|
||||
static const u32 id_base = 1;
|
||||
static const u32 id_step = 1;
|
||||
static const u32 id_count = 0x100000;
|
||||
|
||||
bool from_breakpoint = true;
|
||||
bool stop = false;
|
||||
bool paused = false;
|
||||
u64 pausedBy;
|
||||
|
||||
virtual std::string get_name() const;
|
||||
virtual void on_stop() override final;
|
||||
void operator()();
|
||||
void pause_from(cpu_thread* t);
|
||||
};
|
||||
|
||||
extern u32 g_gdb_debugger_id;
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
|
||||
#pragma push_macro("new")
|
||||
#undef new
|
||||
#include <gsl.h>
|
||||
#include <gsl/gsl>
|
||||
#pragma pop_macro("new")
|
||||
#undef Expects
|
||||
#undef Ensures
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
template<typename T1, typename T2> struct range_t
|
||||
{
|
||||
T1 _min; // first value
|
||||
|
||||
+387
-80
@@ -1,3 +1,232 @@
|
||||
#include "types.h"
|
||||
#include "JIT.h"
|
||||
#include "StrFmt.h"
|
||||
#include "File.h"
|
||||
#include "Log.h"
|
||||
#include "mutex.h"
|
||||
#include "sysinfo.h"
|
||||
#include "VirtualMemory.h"
|
||||
#include <immintrin.h>
|
||||
|
||||
// Memory manager mutex
|
||||
shared_mutex s_mutex2;
|
||||
|
||||
#ifdef __linux__
|
||||
#define CAN_OVERCOMMIT
|
||||
#endif
|
||||
|
||||
static u8* get_jit_memory()
|
||||
{
|
||||
// Reserve 2G memory (magic static)
|
||||
static void* const s_memory2 = []() -> void*
|
||||
{
|
||||
void* ptr = utils::memory_reserve(0x80000000);
|
||||
|
||||
#ifdef CAN_OVERCOMMIT
|
||||
utils::memory_commit(ptr, 0x80000000);
|
||||
utils::memory_protect(ptr, 0x40000000, utils::protection::wx);
|
||||
#endif
|
||||
return ptr;
|
||||
}();
|
||||
|
||||
return static_cast<u8*>(s_memory2);
|
||||
}
|
||||
|
||||
// Allocation counters (1G code, 1G data subranges)
|
||||
static atomic_t<u64> s_code_pos{0}, s_data_pos{0};
|
||||
|
||||
// Snapshot of code generated before main()
|
||||
static std::vector<u8> s_code_init, s_data_init;
|
||||
|
||||
template <atomic_t<u64>& Ctr, uint Off, utils::protection Prot>
|
||||
static u8* add_jit_memory(std::size_t size, uint align)
|
||||
{
|
||||
// Select subrange
|
||||
u8* pointer = get_jit_memory() + Off;
|
||||
|
||||
if (UNLIKELY(!size && !align))
|
||||
{
|
||||
// Return subrange info
|
||||
return pointer;
|
||||
}
|
||||
|
||||
#ifndef CAN_OVERCOMMIT
|
||||
std::lock_guard lock(s_mutex2);
|
||||
#endif
|
||||
|
||||
u64 olda, newa;
|
||||
|
||||
// Simple allocation by incrementing pointer to the next free data
|
||||
const u64 pos = Ctr.atomic_op([&](u64& ctr) -> u64
|
||||
{
|
||||
const u64 _pos = ::align(ctr, align);
|
||||
const u64 _new = ::align(_pos + size, align);
|
||||
|
||||
if (UNLIKELY(_new > 0x40000000))
|
||||
{
|
||||
// Sorry, we failed, and further attempts should fail too.
|
||||
ctr = 0x40000000;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check the necessity to commit more memory
|
||||
olda = ::align(ctr, 0x10000);
|
||||
newa = ::align(_new, 0x10000);
|
||||
|
||||
ctr = _new;
|
||||
return _pos;
|
||||
});
|
||||
|
||||
if (UNLIKELY(pos == -1))
|
||||
{
|
||||
LOG_WARNING(GENERAL, "JIT: Out of memory (size=0x%x, align=0x%x, off=0x%x)", size, align, Off);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (UNLIKELY(olda != newa))
|
||||
{
|
||||
#ifdef CAN_OVERCOMMIT
|
||||
// TODO: possibly madvise
|
||||
#else
|
||||
// Commit more memory
|
||||
utils::memory_commit(pointer + olda, newa - olda, Prot);
|
||||
#endif
|
||||
}
|
||||
|
||||
return pointer + pos;
|
||||
}
|
||||
|
||||
jit_runtime::jit_runtime()
|
||||
: HostRuntime()
|
||||
{
|
||||
}
|
||||
|
||||
jit_runtime::~jit_runtime()
|
||||
{
|
||||
}
|
||||
|
||||
asmjit::Error jit_runtime::_add(void** dst, asmjit::CodeHolder* code) noexcept
|
||||
{
|
||||
std::size_t codeSize = code->getCodeSize();
|
||||
if (UNLIKELY(!codeSize))
|
||||
{
|
||||
*dst = nullptr;
|
||||
return asmjit::kErrorNoCodeGenerated;
|
||||
}
|
||||
|
||||
void* p = jit_runtime::alloc(codeSize, 16);
|
||||
if (UNLIKELY(!p))
|
||||
{
|
||||
*dst = nullptr;
|
||||
return asmjit::kErrorNoVirtualMemory;
|
||||
}
|
||||
|
||||
std::size_t relocSize = code->relocate(p);
|
||||
if (UNLIKELY(!relocSize))
|
||||
{
|
||||
*dst = nullptr;
|
||||
return asmjit::kErrorInvalidState;
|
||||
}
|
||||
|
||||
flush(p, relocSize);
|
||||
*dst = p;
|
||||
|
||||
return asmjit::kErrorOk;
|
||||
}
|
||||
|
||||
asmjit::Error jit_runtime::_release(void* ptr) noexcept
|
||||
{
|
||||
return asmjit::kErrorOk;
|
||||
}
|
||||
|
||||
u8* jit_runtime::alloc(std::size_t size, uint align, bool exec) noexcept
|
||||
{
|
||||
if (exec)
|
||||
{
|
||||
return add_jit_memory<s_code_pos, 0x0, utils::protection::wx>(size, align);
|
||||
}
|
||||
else
|
||||
{
|
||||
return add_jit_memory<s_data_pos, 0x40000000, utils::protection::rw>(size, align);
|
||||
}
|
||||
}
|
||||
|
||||
void jit_runtime::initialize()
|
||||
{
|
||||
if (!s_code_init.empty() || !s_data_init.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Create code/data snapshot
|
||||
s_code_init.resize(s_code_pos);
|
||||
std::memcpy(s_code_init.data(), alloc(0, 0, true), s_code_pos);
|
||||
s_data_init.resize(s_data_pos);
|
||||
std::memcpy(s_data_init.data(), alloc(0, 0, false), s_data_pos);
|
||||
}
|
||||
|
||||
void jit_runtime::finalize() noexcept
|
||||
{
|
||||
// Reset JIT memory
|
||||
#ifdef CAN_OVERCOMMIT
|
||||
utils::memory_reset(get_jit_memory(), 0x80000000);
|
||||
utils::memory_protect(get_jit_memory(), 0x40000000, utils::protection::wx);
|
||||
#else
|
||||
utils::memory_decommit(get_jit_memory(), 0x80000000);
|
||||
#endif
|
||||
|
||||
s_code_pos = 0;
|
||||
s_data_pos = 0;
|
||||
|
||||
// Restore code/data snapshot
|
||||
std::memcpy(alloc(s_code_init.size(), 1, true), s_code_init.data(), s_code_init.size());
|
||||
std::memcpy(alloc(s_data_init.size(), 1, false), s_data_init.data(), s_data_init.size());
|
||||
}
|
||||
|
||||
asmjit::JitRuntime& asmjit::get_global_runtime()
|
||||
{
|
||||
// Magic static
|
||||
static asmjit::JitRuntime g_rt;
|
||||
return g_rt;
|
||||
}
|
||||
|
||||
void asmjit::build_transaction_enter(asmjit::X86Assembler& c, asmjit::Label fallback, const asmjit::X86Gp& ctr, uint less_than)
|
||||
{
|
||||
Label fall = c.newLabel();
|
||||
Label begin = c.newLabel();
|
||||
c.jmp(begin);
|
||||
c.bind(fall);
|
||||
|
||||
if (less_than < 65)
|
||||
{
|
||||
c.add(ctr, 1);
|
||||
c.test(x86::eax, _XABORT_RETRY);
|
||||
c.jz(fallback);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Count an attempt without RETRY flag as 65 normal attempts and continue
|
||||
c.not_(x86::eax);
|
||||
c.and_(x86::eax, _XABORT_RETRY);
|
||||
c.shl(x86::eax, 5);
|
||||
c.add(x86::eax, 1); // eax = RETRY ? 1 : 65
|
||||
c.add(ctr, x86::rax);
|
||||
}
|
||||
|
||||
c.cmp(ctr, less_than);
|
||||
c.jae(fallback);
|
||||
c.align(kAlignCode, 16);
|
||||
c.bind(begin);
|
||||
c.xbegin(fall);
|
||||
}
|
||||
|
||||
void asmjit::build_transaction_abort(asmjit::X86Assembler& c, unsigned char code)
|
||||
{
|
||||
c.db(0xc6);
|
||||
c.db(0xf8);
|
||||
c.db(code);
|
||||
}
|
||||
|
||||
#ifdef LLVM_AVAILABLE
|
||||
|
||||
#include <unordered_map>
|
||||
@@ -7,14 +236,6 @@
|
||||
#include <array>
|
||||
#include <deque>
|
||||
|
||||
#include "types.h"
|
||||
#include "StrFmt.h"
|
||||
#include "File.h"
|
||||
#include "Log.h"
|
||||
#include "mutex.h"
|
||||
#include "sysinfo.h"
|
||||
#include "VirtualMemory.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 0)
|
||||
#endif
|
||||
@@ -34,8 +255,6 @@
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include "JIT.h"
|
||||
|
||||
// Memory manager mutex
|
||||
shared_mutex s_mutex;
|
||||
|
||||
@@ -47,6 +266,7 @@ static void* const s_memory = []() -> void*
|
||||
{
|
||||
llvm::InitializeNativeTarget();
|
||||
llvm::InitializeNativeTargetAsmPrinter();
|
||||
llvm::InitializeNativeTargetAsmParser();
|
||||
LLVMLinkInMCJIT();
|
||||
|
||||
#ifdef MAP_32BIT
|
||||
@@ -72,7 +292,7 @@ static void* s_next = s_memory;
|
||||
static std::deque<std::vector<RUNTIME_FUNCTION>> s_unwater;
|
||||
static std::vector<std::vector<RUNTIME_FUNCTION>> s_unwind; // .pdata
|
||||
#else
|
||||
static std::deque<std::tuple<u8*, u64, std::size_t>> s_unfire;
|
||||
static std::deque<std::pair<u8*, std::size_t>> s_unfire;
|
||||
#endif
|
||||
|
||||
// Reset memory manager
|
||||
@@ -89,27 +309,9 @@ extern void jit_finalize()
|
||||
|
||||
s_unwind.clear();
|
||||
#else
|
||||
struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
{
|
||||
u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool finalizeMemory(std::string* = nullptr) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} mem;
|
||||
|
||||
for (auto&& t : s_unfire)
|
||||
{
|
||||
mem.deregisterEHFrames(std::get<0>(t), std::get<1>(t), std::get<2>(t));
|
||||
llvm::RTDyldMemoryManager::deregisterEHFramesInProcess(t.first, t.second);
|
||||
}
|
||||
|
||||
s_unfire.clear();
|
||||
@@ -163,7 +365,7 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
if ((u64)s_memory > 0x80000000 - s_memory_size ? (u64)addr - (u64)s_memory >= s_memory_size : addr >= 0x80000000)
|
||||
{
|
||||
// Lock memory manager
|
||||
writer_lock lock(s_mutex);
|
||||
std::lock_guard lock(s_mutex);
|
||||
|
||||
// Allocate memory for trampolines
|
||||
if (!m_tramps)
|
||||
@@ -199,7 +401,7 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
|
||||
{
|
||||
// Lock memory manager
|
||||
writer_lock lock(s_mutex);
|
||||
std::lock_guard lock(s_mutex);
|
||||
|
||||
// Simple allocation
|
||||
const u64 next = ::align((u64)s_next + size, 4096);
|
||||
@@ -220,7 +422,7 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
|
||||
{
|
||||
// Lock memory manager
|
||||
writer_lock lock(s_mutex);
|
||||
std::lock_guard lock(s_mutex);
|
||||
|
||||
// Simple allocation
|
||||
const u64 next = ::align((u64)s_next + size, 4096);
|
||||
@@ -233,7 +435,6 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
|
||||
if (!is_ro)
|
||||
{
|
||||
LOG_ERROR(GENERAL, "LLVM: Writeable data section not supported!");
|
||||
}
|
||||
|
||||
utils::memory_commit(s_next, size);
|
||||
@@ -245,7 +446,7 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
bool finalizeMemory(std::string* = nullptr) override
|
||||
{
|
||||
// Lock memory manager
|
||||
writer_lock lock(s_mutex);
|
||||
std::lock_guard lock(s_mutex);
|
||||
|
||||
// TODO: make only read-only sections read-only
|
||||
//#ifdef _WIN32
|
||||
@@ -263,7 +464,7 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// Lock memory manager
|
||||
writer_lock lock(s_mutex);
|
||||
std::lock_guard lock(s_mutex);
|
||||
|
||||
// Use s_memory as a BASE, compute the difference
|
||||
const u64 unwind_diff = (u64)addr - (u64)s_memory;
|
||||
@@ -287,13 +488,95 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
s_unwind.emplace_back(std::move(pdata));
|
||||
}
|
||||
#else
|
||||
s_unfire.push_front(std::make_tuple(addr, load_addr, size));
|
||||
s_unfire.push_front(std::make_pair(addr, size));
|
||||
#endif
|
||||
|
||||
return RTDyldMemoryManager::registerEHFrames(addr, load_addr, size);
|
||||
return RTDyldMemoryManager::registerEHFramesInProcess(addr, size);
|
||||
}
|
||||
|
||||
void deregisterEHFrames(u8* addr, u64 load_addr, std::size_t size) override
|
||||
void deregisterEHFrames() override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Simple memory manager
|
||||
struct MemoryManager2 : llvm::RTDyldMemoryManager
|
||||
{
|
||||
MemoryManager2() = default;
|
||||
|
||||
~MemoryManager2() override
|
||||
{
|
||||
}
|
||||
|
||||
u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
|
||||
{
|
||||
return jit_runtime::alloc(size, align, true);
|
||||
}
|
||||
|
||||
u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
|
||||
{
|
||||
return jit_runtime::alloc(size, align, false);
|
||||
}
|
||||
|
||||
bool finalizeMemory(std::string* = nullptr) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void registerEHFrames(u8* addr, u64 load_addr, std::size_t size) override
|
||||
{
|
||||
#ifndef _WIN32
|
||||
RTDyldMemoryManager::registerEHFramesInProcess(addr, size);
|
||||
s_unfire.push_front(std::make_pair(addr, size));
|
||||
#endif
|
||||
}
|
||||
|
||||
void deregisterEHFrames() override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Simple memory manager. I promise there will be no MemoryManager4.
|
||||
struct MemoryManager3 : llvm::RTDyldMemoryManager
|
||||
{
|
||||
std::vector<std::pair<u8*, std::size_t>> allocs;
|
||||
|
||||
MemoryManager3() = default;
|
||||
|
||||
~MemoryManager3() override
|
||||
{
|
||||
for (auto& a : allocs)
|
||||
{
|
||||
utils::memory_release(a.first, a.second);
|
||||
}
|
||||
}
|
||||
|
||||
u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
|
||||
{
|
||||
u8* r = static_cast<u8*>(utils::memory_reserve(size));
|
||||
utils::memory_commit(r, size, utils::protection::wx);
|
||||
allocs.emplace_back(r, size);
|
||||
return r;
|
||||
}
|
||||
|
||||
u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
|
||||
{
|
||||
u8* r = static_cast<u8*>(utils::memory_reserve(size));
|
||||
utils::memory_commit(r, size);
|
||||
allocs.emplace_back(r, size);
|
||||
return r;
|
||||
}
|
||||
|
||||
bool finalizeMemory(std::string* = nullptr) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void registerEHFrames(u8* addr, u64 load_addr, std::size_t size) override
|
||||
{
|
||||
}
|
||||
|
||||
void deregisterEHFrames() override
|
||||
{
|
||||
}
|
||||
};
|
||||
@@ -308,7 +591,7 @@ struct EventListener : llvm::JITEventListener
|
||||
{
|
||||
}
|
||||
|
||||
void NotifyObjectEmitted(const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& inf) override
|
||||
void notifyObjectLoaded(ObjectKey K, const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& inf) override
|
||||
{
|
||||
#ifdef _WIN32
|
||||
for (auto it = obj.section_begin(), end = obj.section_end(); it != end; ++it)
|
||||
@@ -336,7 +619,7 @@ struct EventListener : llvm::JITEventListener
|
||||
}
|
||||
|
||||
// Lock memory manager
|
||||
writer_lock lock(s_mutex);
|
||||
std::lock_guard lock(s_mutex);
|
||||
|
||||
// Use s_memory as a BASE, compute the difference
|
||||
const u64 code_diff = (u64)m_mem.m_code_addr - (u64)s_memory;
|
||||
@@ -373,15 +656,15 @@ public:
|
||||
std::string name = m_path;
|
||||
name.append(module->getName());
|
||||
fs::file(name, fs::rewrite).write(obj.getBufferStart(), obj.getBufferSize());
|
||||
LOG_SUCCESS(GENERAL, "LLVM: Created module: %s", module->getName().data());
|
||||
LOG_NOTICE(GENERAL, "LLVM: Created module: %s", module->getName().data());
|
||||
}
|
||||
|
||||
static std::unique_ptr<llvm::MemoryBuffer> load(const std::string& path)
|
||||
{
|
||||
if (fs::file cached{path, fs::read})
|
||||
{
|
||||
auto buf = llvm::MemoryBuffer::getNewUninitMemBuffer(cached.size());
|
||||
cached.read(const_cast<char*>(buf->getBufferStart()), buf->getBufferSize());
|
||||
auto buf = llvm::WritableMemoryBuffer::getNewUninitMemBuffer(cached.size());
|
||||
cached.read(buf->getBufferStart(), buf->getBufferSize());
|
||||
return buf;
|
||||
}
|
||||
|
||||
@@ -395,7 +678,7 @@ public:
|
||||
|
||||
if (auto buf = load(path))
|
||||
{
|
||||
LOG_SUCCESS(GENERAL, "LLVM: Loaded module: %s", module->getName().data());
|
||||
LOG_NOTICE(GENERAL, "LLVM: Loaded module: %s", module->getName().data());
|
||||
return buf;
|
||||
}
|
||||
|
||||
@@ -403,10 +686,10 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, std::string _cpu)
|
||||
: m_link(_link)
|
||||
, m_cpu(std::move(_cpu))
|
||||
std::string jit_compiler::cpu(const std::string& _cpu)
|
||||
{
|
||||
std::string m_cpu = _cpu;
|
||||
|
||||
if (m_cpu.empty())
|
||||
{
|
||||
m_cpu = llvm::sys::getHostCPUName();
|
||||
@@ -417,24 +700,63 @@ jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, st
|
||||
m_cpu == "broadwell" ||
|
||||
m_cpu == "skylake" ||
|
||||
m_cpu == "skylake-avx512" ||
|
||||
m_cpu == "cannonlake")
|
||||
m_cpu == "cascadelake" ||
|
||||
m_cpu == "cannonlake" ||
|
||||
m_cpu == "icelake" ||
|
||||
m_cpu == "icelake-client" ||
|
||||
m_cpu == "icelake-server")
|
||||
{
|
||||
// Downgrade if AVX is not supported by some chips
|
||||
if (!utils::has_avx())
|
||||
{
|
||||
m_cpu = "nehalem";
|
||||
}
|
||||
}
|
||||
|
||||
if (m_cpu == "skylake-avx512" ||
|
||||
m_cpu == "cascadelake" ||
|
||||
m_cpu == "cannonlake" ||
|
||||
m_cpu == "icelake" ||
|
||||
m_cpu == "icelake-client" ||
|
||||
m_cpu == "icelake-server")
|
||||
{
|
||||
// Downgrade if AVX-512 is disabled or not supported
|
||||
if (!utils::has_512())
|
||||
{
|
||||
m_cpu = "skylake";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m_cpu;
|
||||
}
|
||||
|
||||
jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, const std::string& _cpu, u32 flags)
|
||||
: m_link(_link)
|
||||
, m_cpu(cpu(_cpu))
|
||||
{
|
||||
std::string result;
|
||||
|
||||
if (m_link.empty())
|
||||
{
|
||||
std::unique_ptr<llvm::RTDyldMemoryManager> mem;
|
||||
|
||||
if (flags & 0x1)
|
||||
{
|
||||
mem = std::make_unique<MemoryManager3>();
|
||||
}
|
||||
else
|
||||
{
|
||||
mem = std::make_unique<MemoryManager2>();
|
||||
}
|
||||
|
||||
// Auxiliary JIT (does not use custom memory manager, only writes the objects)
|
||||
m_engine.reset(llvm::EngineBuilder(std::make_unique<llvm::Module>("null_", m_context))
|
||||
.setErrorStr(&result)
|
||||
.setEngineKind(llvm::EngineKind::JIT)
|
||||
.setMCJITMemoryManager(std::move(mem))
|
||||
.setOptLevel(llvm::CodeGenOpt::Aggressive)
|
||||
.setCodeModel(llvm::CodeModel::Small)
|
||||
.setCodeModel(flags & 0x2 ? llvm::CodeModel::Large : llvm::CodeModel::Small)
|
||||
.setMCPU(m_cpu)
|
||||
.create());
|
||||
}
|
||||
@@ -446,9 +768,10 @@ jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, st
|
||||
|
||||
m_engine.reset(llvm::EngineBuilder(std::make_unique<llvm::Module>("null", m_context))
|
||||
.setErrorStr(&result)
|
||||
.setEngineKind(llvm::EngineKind::JIT)
|
||||
.setMCJITMemoryManager(std::move(mem))
|
||||
.setOptLevel(llvm::CodeGenOpt::Aggressive)
|
||||
.setCodeModel(llvm::CodeModel::Small)
|
||||
.setCodeModel(flags & 0x2 ? llvm::CodeModel::Large : llvm::CodeModel::Small)
|
||||
.setMCPU(m_cpu)
|
||||
.create());
|
||||
|
||||
@@ -485,6 +808,19 @@ void jit_compiler::add(std::unique_ptr<llvm::Module> module, const std::string&
|
||||
}
|
||||
}
|
||||
|
||||
void jit_compiler::add(std::unique_ptr<llvm::Module> module)
|
||||
{
|
||||
const auto ptr = module.get();
|
||||
m_engine->addModule(std::move(module));
|
||||
m_engine->generateCodeForModule(ptr);
|
||||
|
||||
for (auto& func : ptr->functions())
|
||||
{
|
||||
// Delete IR to lower memory consumption
|
||||
func.deleteBody();
|
||||
}
|
||||
}
|
||||
|
||||
void jit_compiler::add(const std::string& path)
|
||||
{
|
||||
m_engine->addObjectFile(std::move(llvm::object::ObjectFile::createObjectFile(*ObjectCache::load(path)).get()));
|
||||
@@ -500,33 +836,4 @@ u64 jit_compiler::get(const std::string& name)
|
||||
return m_engine->getGlobalValueAddress(name);
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, u64> jit_compiler::add(std::unordered_map<std::string, std::string> data)
|
||||
{
|
||||
// Lock memory manager
|
||||
writer_lock lock(s_mutex);
|
||||
|
||||
std::unordered_map<std::string, u64> result;
|
||||
|
||||
std::size_t size = 0;
|
||||
|
||||
for (auto&& pair : data)
|
||||
{
|
||||
size += ::align(pair.second.size(), 16);
|
||||
}
|
||||
|
||||
utils::memory_commit(s_next, size, utils::protection::wx);
|
||||
std::memset(s_next, 0xc3, ::align(size, 4096));
|
||||
|
||||
for (auto&& pair : data)
|
||||
{
|
||||
std::memcpy(s_next, pair.second.data(), pair.second.size());
|
||||
result.emplace(pair.first, (u64)s_next);
|
||||
s_next = (void*)::align((u64)s_next + pair.second.size(), 16);
|
||||
}
|
||||
|
||||
s_next = (void*)::align((u64)s_next, 4096);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+98
-8
@@ -1,9 +1,97 @@
|
||||
#pragma once
|
||||
|
||||
#define ASMJIT_EMBED
|
||||
#define ASMJIT_DEBUG
|
||||
|
||||
#include <asmjit/asmjit.h>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
|
||||
enum class jit_class
|
||||
{
|
||||
ppu_code,
|
||||
ppu_data,
|
||||
spu_code,
|
||||
spu_data,
|
||||
};
|
||||
|
||||
// ASMJIT runtime for emitting code in a single 2G region
|
||||
struct jit_runtime final : asmjit::HostRuntime
|
||||
{
|
||||
jit_runtime();
|
||||
~jit_runtime() override;
|
||||
|
||||
// Allocate executable memory
|
||||
asmjit::Error _add(void** dst, asmjit::CodeHolder* code) noexcept override;
|
||||
|
||||
// Do nothing (deallocation is delayed)
|
||||
asmjit::Error _release(void* p) noexcept override;
|
||||
|
||||
// Allocate memory
|
||||
static u8* alloc(std::size_t size, uint align, bool exec = true) noexcept;
|
||||
|
||||
// Should be called at least once after global initialization
|
||||
static void initialize();
|
||||
|
||||
// Deallocate all memory
|
||||
static void finalize() noexcept;
|
||||
};
|
||||
|
||||
namespace asmjit
|
||||
{
|
||||
// Should only be used to build global functions
|
||||
asmjit::JitRuntime& get_global_runtime();
|
||||
|
||||
// Emit xbegin and adjacent loop, return label at xbegin
|
||||
void build_transaction_enter(X86Assembler& c, Label fallback, const X86Gp& ctr, uint less_than);
|
||||
|
||||
// Emit xabort
|
||||
void build_transaction_abort(X86Assembler& c, unsigned char code);
|
||||
}
|
||||
|
||||
// Build runtime function with asmjit::X86Assembler
|
||||
template <typename FT, typename F>
|
||||
FT build_function_asm(F&& builder)
|
||||
{
|
||||
using namespace asmjit;
|
||||
|
||||
auto& rt = get_global_runtime();
|
||||
|
||||
CodeHolder code;
|
||||
code.init(rt.getCodeInfo());
|
||||
code._globalHints = asmjit::CodeEmitter::kHintOptimizedAlign;
|
||||
|
||||
std::array<X86Gp, 4> args;
|
||||
#ifdef _WIN32
|
||||
args[0] = x86::rcx;
|
||||
args[1] = x86::rdx;
|
||||
args[2] = x86::r8;
|
||||
args[3] = x86::r9;
|
||||
#else
|
||||
args[0] = x86::rdi;
|
||||
args[1] = x86::rsi;
|
||||
args[2] = x86::rdx;
|
||||
args[3] = x86::rcx;
|
||||
#endif
|
||||
|
||||
X86Assembler compiler(&code);
|
||||
builder(std::ref(compiler), args);
|
||||
|
||||
FT result;
|
||||
|
||||
if (rt.add(&result, &code))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef LLVM_AVAILABLE
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "types.h"
|
||||
@@ -40,7 +128,7 @@ class jit_compiler final
|
||||
std::string m_cpu;
|
||||
|
||||
public:
|
||||
jit_compiler(const std::unordered_map<std::string, u64>& _link, std::string _cpu);
|
||||
jit_compiler(const std::unordered_map<std::string, u64>& _link, const std::string& _cpu, u32 flags = 0);
|
||||
~jit_compiler();
|
||||
|
||||
// Get LLVM context
|
||||
@@ -49,9 +137,17 @@ public:
|
||||
return m_context;
|
||||
}
|
||||
|
||||
auto& get_engine() const
|
||||
{
|
||||
return *m_engine;
|
||||
}
|
||||
|
||||
// Add module (path to obj cache dir)
|
||||
void add(std::unique_ptr<llvm::Module> module, const std::string& path);
|
||||
|
||||
// Add module (not cached)
|
||||
void add(std::unique_ptr<llvm::Module> module);
|
||||
|
||||
// Add object (path to obj file)
|
||||
void add(const std::string& path);
|
||||
|
||||
@@ -61,14 +157,8 @@ public:
|
||||
// Get compiled function address
|
||||
u64 get(const std::string& name);
|
||||
|
||||
// Add functions directly to the memory manager (name -> code)
|
||||
static std::unordered_map<std::string, u64> add(std::unordered_map<std::string, std::string>);
|
||||
|
||||
// Get CPU info
|
||||
const std::string& cpu() const
|
||||
{
|
||||
return m_cpu;
|
||||
}
|
||||
static std::string cpu(const std::string& _cpu);
|
||||
|
||||
// Check JIT purpose
|
||||
bool is_primary() const
|
||||
|
||||
+67
-26
@@ -6,10 +6,13 @@
|
||||
#include "Utilities/sysinfo.h"
|
||||
#include "Utilities/Thread.h"
|
||||
#include "rpcs3_version.h"
|
||||
#include <cstring>
|
||||
#include <cstdarg>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
@@ -18,6 +21,7 @@ using namespace std::literals::chrono_literals;
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#include <zlib.h>
|
||||
@@ -71,7 +75,7 @@ namespace logs
|
||||
#endif
|
||||
uchar* m_fptr{};
|
||||
z_stream m_zs{};
|
||||
semaphore<> m_m;
|
||||
shared_mutex m_m;
|
||||
|
||||
alignas(128) atomic_t<u64> m_buf{0}; // MSB (40 bit): push begin, LSB (24 bis): push size
|
||||
alignas(128) atomic_t<u64> m_out{0}; // Amount of bytes written to file
|
||||
@@ -177,17 +181,16 @@ namespace logs
|
||||
channel HLE("HLE");
|
||||
channel PPU("PPU");
|
||||
channel SPU("SPU");
|
||||
channel ARMv7("ARMv7");
|
||||
|
||||
// Channel registry mutex
|
||||
semaphore<> g_mutex;
|
||||
shared_mutex g_mutex;
|
||||
|
||||
// Must be set to true in main()
|
||||
atomic_t<bool> g_init{false};
|
||||
|
||||
void reset()
|
||||
{
|
||||
semaphore_lock lock(g_mutex);
|
||||
std::lock_guard lock(g_mutex);
|
||||
|
||||
for (auto&& pair : get_logger()->channels)
|
||||
{
|
||||
@@ -197,7 +200,7 @@ namespace logs
|
||||
|
||||
void set_level(const std::string& ch_name, level value)
|
||||
{
|
||||
semaphore_lock lock(g_mutex);
|
||||
std::lock_guard lock(g_mutex);
|
||||
|
||||
get_logger()->channels[ch_name].set_level(value);
|
||||
}
|
||||
@@ -207,7 +210,7 @@ namespace logs
|
||||
{
|
||||
if (!g_init)
|
||||
{
|
||||
semaphore_lock lock(g_mutex);
|
||||
std::lock_guard lock(g_mutex);
|
||||
get_logger()->messages.clear();
|
||||
g_init = true;
|
||||
}
|
||||
@@ -223,7 +226,7 @@ void logs::listener::add(logs::listener* _new)
|
||||
// Get first (main) listener
|
||||
listener* lis = get_logger();
|
||||
|
||||
semaphore_lock lock(g_mutex);
|
||||
std::lock_guard lock(g_mutex);
|
||||
|
||||
// Install new listener at the end of linked list
|
||||
while (lis->m_next || !lis->m_next.compare_and_swap_test(nullptr, _new))
|
||||
@@ -238,7 +241,7 @@ void logs::listener::add(logs::listener* _new)
|
||||
}
|
||||
}
|
||||
|
||||
void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, const u64* args)
|
||||
void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, ...) const
|
||||
{
|
||||
// Get timestamp
|
||||
const u64 stamp = get_stamp();
|
||||
@@ -246,7 +249,7 @@ void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, const u
|
||||
// Register channel
|
||||
if (ch->enabled == level::_uninit)
|
||||
{
|
||||
semaphore_lock lock(g_mutex);
|
||||
std::lock_guard lock(g_mutex);
|
||||
|
||||
auto& info = get_logger()->channels[ch->name];
|
||||
|
||||
@@ -267,9 +270,23 @@ void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, const u
|
||||
}
|
||||
}
|
||||
|
||||
// Get text
|
||||
thread_local std::string text; text.clear();
|
||||
fmt::raw_append(text, fmt, sup, args);
|
||||
// Get text, extract va_args
|
||||
thread_local std::string text;
|
||||
thread_local std::vector<u64> args;
|
||||
|
||||
std::size_t args_count = 0;
|
||||
for (auto v = sup; v->fmt_string; v++)
|
||||
args_count++;
|
||||
|
||||
text.clear();
|
||||
args.resize(args_count);
|
||||
|
||||
va_list c_args;
|
||||
va_start(c_args, sup);
|
||||
for (u64& arg : args)
|
||||
arg = va_arg(c_args, u64);
|
||||
va_end(c_args);
|
||||
fmt::raw_append(text, fmt, sup, args.data());
|
||||
std::string prefix = g_tls_log_prefix();
|
||||
|
||||
// Get first (main) listener
|
||||
@@ -277,7 +294,7 @@ void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, const u
|
||||
|
||||
if (!g_init)
|
||||
{
|
||||
semaphore_lock lock(g_mutex);
|
||||
std::lock_guard lock(g_mutex);
|
||||
|
||||
if (!g_init)
|
||||
{
|
||||
@@ -305,12 +322,12 @@ void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, const u
|
||||
logs::file_writer::file_writer(const std::string& name)
|
||||
: m_name(name)
|
||||
{
|
||||
const std::string log_name = fs::get_config_dir() + name + ".log";
|
||||
const std::string buf_name = fs::get_config_dir() + name + ".buf";
|
||||
const std::string log_name = fs::get_cache_dir() + name + ".log";
|
||||
const std::string buf_name = fs::get_cache_dir() + name + ".buf";
|
||||
|
||||
try
|
||||
{
|
||||
if (!m_file.open(buf_name, fs::read + fs::write + fs::create + fs::trunc + fs::unshare))
|
||||
if (!m_file.open(buf_name, fs::read + fs::rewrite + fs::lock))
|
||||
{
|
||||
if (fs::g_tls_error == fs::error::acces)
|
||||
{
|
||||
@@ -335,7 +352,7 @@ logs::file_writer::file_writer(const std::string& name)
|
||||
|
||||
// Check free space
|
||||
fs::device_stat stats{};
|
||||
if (!fs::statfs(fs::get_config_dir(), stats) || stats.avail_free < s_log_size * 8)
|
||||
if (!fs::statfs(fs::get_cache_dir(), stats) || stats.avail_free < s_log_size * 8)
|
||||
{
|
||||
fmt::throw_exception("Not enough free space (%f KB)", stats.avail_free / 1000000.);
|
||||
}
|
||||
@@ -355,18 +372,23 @@ logs::file_writer::file_writer(const std::string& name)
|
||||
verify(name.c_str()), m_fptr;
|
||||
|
||||
// Rotate backups (TODO)
|
||||
fs::remove_file(fs::get_config_dir() + name + "1.log.gz");
|
||||
fs::create_dir(fs::get_config_dir() + "old_logs");
|
||||
fs::rename(fs::get_config_dir() + m_name + ".log.gz", fs::get_config_dir() + "old_logs/" + m_name + ".log.gz", true);
|
||||
fs::remove_file(fs::get_cache_dir() + name + "1.log.gz");
|
||||
fs::create_dir(fs::get_cache_dir() + "old_logs");
|
||||
fs::rename(fs::get_cache_dir() + m_name + ".log.gz", fs::get_cache_dir() + "old_logs/" + m_name + ".log.gz", true);
|
||||
|
||||
// Actual log file (allowed to fail)
|
||||
m_fout.open(log_name, fs::rewrite);
|
||||
|
||||
// Compressed log
|
||||
if (!m_fout2.open(log_name + ".gz", fs::rewrite) || deflateInit2(&m_zs, 9, Z_DEFLATED, 16 + 15, 9, Z_DEFAULT_STRATEGY) != Z_OK)
|
||||
// Compressed log, make it inaccessible (foolproof)
|
||||
if (!m_fout2.open(log_name + ".gz", fs::rewrite + fs::unread) || deflateInit2(&m_zs, 9, Z_DEFLATED, 16 + 15, 9, Z_DEFAULT_STRATEGY) != Z_OK)
|
||||
{
|
||||
m_fout2.close();
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Autodelete compressed log file
|
||||
m_fout2.set_delete();
|
||||
#endif
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
@@ -444,16 +466,21 @@ logs::file_writer::~file_writer()
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Cancel compressed log file autodeletion
|
||||
m_fout2.set_delete(false);
|
||||
|
||||
UnmapViewOfFile(m_fptr);
|
||||
CloseHandle(m_fmap);
|
||||
#else
|
||||
// Restore compressed log file permissions
|
||||
::fchmod(m_fout2.get_handle(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
::munmap(m_fptr, s_log_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool logs::file_writer::flush(u64 bufv)
|
||||
{
|
||||
semaphore_lock lock(m_m);
|
||||
std::lock_guard lock(m_m);
|
||||
|
||||
const u64 st = +m_out;
|
||||
const u64 end = std::min<u64>((st + s_log_size) & ~(s_log_size - 1), bufv >> 24);
|
||||
@@ -562,16 +589,30 @@ logs::file_listener::file_listener(const std::string& name)
|
||||
// Write UTF-8 BOM
|
||||
file_writer::log(logs::level::always, "\xEF\xBB\xBF", 3);
|
||||
|
||||
const std::string firmware_version = utils::get_firmware_version();
|
||||
const std::string firmware_string = firmware_version.empty() ? "" : (" | Firmware version: " + firmware_version);
|
||||
|
||||
// Write initial message
|
||||
stored_message ver;
|
||||
ver.m.ch = nullptr;
|
||||
ver.m.sev = level::always;
|
||||
ver.stamp = 0;
|
||||
ver.text = fmt::format("RPCS3 v%s | %s\n%s", rpcs3::version.to_string(), rpcs3::get_branch(), utils::get_system_info());
|
||||
ver.text = fmt::format("RPCS3 v%s | %s%s\n%s", rpcs3::version.to_string(), rpcs3::get_branch(), firmware_string, utils::get_system_info());
|
||||
|
||||
file_writer::log(logs::level::always, ver.text.data(), ver.text.size());
|
||||
file_writer::log(logs::level::always, "\n", 1);
|
||||
messages.emplace_back(std::move(ver));
|
||||
|
||||
// Write OS version
|
||||
stored_message os;
|
||||
os.m.ch = nullptr;
|
||||
os.m.sev = level::notice;
|
||||
os.stamp = 0;
|
||||
os.text = utils::get_OS_version();
|
||||
|
||||
file_writer::log(logs::level::notice, os.text.data(), os.text.size());
|
||||
file_writer::log(logs::level::notice, "\n", 1);
|
||||
messages.emplace_back(std::move(os));
|
||||
}
|
||||
|
||||
void logs::file_listener::log(u64 stamp, const logs::message& msg, const std::string& prefix, const std::string& _text)
|
||||
@@ -592,14 +633,14 @@ void logs::file_listener::log(u64 stamp, const logs::message& msg, const std::st
|
||||
case level::_uninit: text = u8"· "; break;
|
||||
}
|
||||
|
||||
// Print miscosecond timestamp
|
||||
// Print µs timestamp
|
||||
const u64 hours = stamp / 3600'000'000;
|
||||
const u64 mins = (stamp % 3600'000'000) / 60'000'000;
|
||||
const u64 secs = (stamp % 60'000'000) / 1'000'000;
|
||||
const u64 frac = (stamp % 1'000'000);
|
||||
fmt::append(text, "%u:%02u:%02u.%06u ", hours, mins, secs, frac);
|
||||
|
||||
if (prefix.size() > 0)
|
||||
if (!prefix.empty())
|
||||
{
|
||||
text += "{";
|
||||
text += prefix;
|
||||
|
||||
+16
-17
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Atomic.h"
|
||||
#include "util/atomic.hpp"
|
||||
#include "StrFmt.h"
|
||||
#include <climits>
|
||||
|
||||
@@ -23,14 +23,17 @@ namespace logs
|
||||
|
||||
struct channel;
|
||||
|
||||
// Message information (temporary data)
|
||||
// Message information
|
||||
struct message
|
||||
{
|
||||
channel* ch;
|
||||
level sev;
|
||||
|
||||
private:
|
||||
// Send log message to global logger instance
|
||||
void broadcast(const char*, const fmt_type_info*, const u64*);
|
||||
void broadcast(const char*, const fmt_type_info*, ...) const;
|
||||
|
||||
friend struct channel;
|
||||
};
|
||||
|
||||
class listener
|
||||
@@ -67,21 +70,16 @@ namespace logs
|
||||
{
|
||||
}
|
||||
|
||||
// Formatting function
|
||||
template<typename... Args>
|
||||
SAFE_BUFFERS FORCE_INLINE void format(level sev, const char* fmt, const Args&... args)
|
||||
{
|
||||
if (UNLIKELY(sev <= enabled))
|
||||
{
|
||||
message{this, sev}.broadcast(fmt, fmt::get_type_info<fmt_unveil_t<Args>...>(), fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
|
||||
}
|
||||
}
|
||||
|
||||
#define GEN_LOG_METHOD(_sev)\
|
||||
template<typename... Args>\
|
||||
SAFE_BUFFERS void _sev(const char* fmt, const Args&... args)\
|
||||
const message msg_##_sev{this, level::_sev};\
|
||||
template <typename... Args>\
|
||||
void _sev(const char* fmt, const Args&... args)\
|
||||
{\
|
||||
return format<Args...>(level::_sev, fmt, args...);\
|
||||
if (UNLIKELY(level::_sev <= enabled))\
|
||||
{\
|
||||
static constexpr fmt_type_info type_list[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};\
|
||||
msg_##_sev.broadcast(fmt, type_list, u64{fmt_unveil<Args>::get(args)}...);\
|
||||
}\
|
||||
}
|
||||
|
||||
GEN_LOG_METHOD(fatal)
|
||||
@@ -104,7 +102,6 @@ namespace logs
|
||||
extern channel HLE;
|
||||
extern channel PPU;
|
||||
extern channel SPU;
|
||||
extern channel ARMv7;
|
||||
|
||||
// Log level control: set all channels to level::notice
|
||||
void reset();
|
||||
@@ -113,6 +110,8 @@ namespace logs
|
||||
void set_level(const std::string&, level);
|
||||
}
|
||||
|
||||
#define LOG_CHANNEL(ch, ...) ::logs::channel ch(#ch, ##__VA_ARGS__)
|
||||
|
||||
// Legacy:
|
||||
|
||||
#define LOG_SUCCESS(ch, fmt, ...) logs::ch.success("" fmt, ##__VA_ARGS__)
|
||||
|
||||
+69
-13
@@ -4,6 +4,7 @@
|
||||
#include "cfmt.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string_view>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
@@ -23,34 +24,81 @@ void fmt_class_string<std::pair<const fmt_type_info*, u64>>::format(std::string&
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<fmt::base57>::format(std::string& out, u64 arg)
|
||||
{
|
||||
const auto& _arg = get_object(arg);
|
||||
|
||||
if (_arg.data && _arg.size)
|
||||
{
|
||||
// Precomputed tail sizes if input data is not multiple of 8
|
||||
static constexpr u8 s_tail[8] = {0, 2, 3, 5, 6, 7, 9, 10};
|
||||
|
||||
// Get full output size
|
||||
const std::size_t out_size = _arg.size / 8 * 11 + s_tail[_arg.size % 8];
|
||||
|
||||
out.resize(out.size() + out_size);
|
||||
|
||||
const auto ptr = &out.front() + (out.size() - out_size);
|
||||
|
||||
// Each 8 bytes of input data produce 11 bytes of base57 output
|
||||
for (std::size_t i = 0, p = 0; i < _arg.size; i += 8, p += 11)
|
||||
{
|
||||
// Load up to 8 bytes
|
||||
be_t<u64> be_value;
|
||||
|
||||
if (_arg.size - i < sizeof(be_value))
|
||||
{
|
||||
std::memset(&be_value, 0, sizeof(be_value));
|
||||
std::memcpy(&be_value, _arg.data + i, _arg.size - i);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::memcpy(&be_value, _arg.data + i, sizeof(be_value));
|
||||
}
|
||||
|
||||
u64 value = be_value;
|
||||
|
||||
for (int j = 10; j >= 0; j--)
|
||||
{
|
||||
if (p + j < out_size)
|
||||
{
|
||||
ptr[p + j] = "0123456789ACEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"[value % 57];
|
||||
}
|
||||
|
||||
value /= 57;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fmt_class_string<const void*>::format(std::string& out, u64 arg)
|
||||
{
|
||||
if (arg)
|
||||
{
|
||||
fmt::append(out, "%p", reinterpret_cast<const void*>(static_cast<std::uintptr_t>(arg)));
|
||||
}
|
||||
else
|
||||
{
|
||||
out += "(NULL)";
|
||||
}
|
||||
fmt::append(out, "%p", arg);
|
||||
}
|
||||
|
||||
void fmt_class_string<const char*>::format(std::string& out, u64 arg)
|
||||
{
|
||||
if (arg)
|
||||
{
|
||||
out += reinterpret_cast<const char*>(static_cast<std::uintptr_t>(arg));
|
||||
out += reinterpret_cast<const char*>(arg);
|
||||
}
|
||||
else
|
||||
{
|
||||
out += "(NULL)";
|
||||
out += "(NULLSTR)";
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<std::string>::format(std::string& out, u64 arg)
|
||||
{
|
||||
out += get_object(arg).c_str(); // TODO?
|
||||
out += get_object(arg);
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<std::string_view>::format(std::string& out, u64 arg)
|
||||
{
|
||||
out += get_object(arg);
|
||||
}
|
||||
|
||||
template <>
|
||||
@@ -129,13 +177,13 @@ void fmt_class_string<ullong>::format(std::string& out, u64 arg)
|
||||
template <>
|
||||
void fmt_class_string<float>::format(std::string& out, u64 arg)
|
||||
{
|
||||
fmt::append(out, "%gf", static_cast<float>(reinterpret_cast<f64&>(arg)));
|
||||
fmt::append(out, "%gf", static_cast<float>(std::bit_cast<f64>(arg)));
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<double>::format(std::string& out, u64 arg)
|
||||
{
|
||||
fmt::append(out, "%g", reinterpret_cast<f64&>(arg));
|
||||
fmt::append(out, "%g", std::bit_cast<f64>(arg));
|
||||
}
|
||||
|
||||
template <>
|
||||
@@ -375,6 +423,14 @@ std::string fmt::to_upper(const std::string& string)
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string fmt::to_lower(const std::string& string)
|
||||
{
|
||||
std::string result;
|
||||
result.resize(string.size());
|
||||
std::transform(string.begin(), string.end(), result.begin(), ::tolower);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool fmt::match(const std::string& source, const std::string& mask)
|
||||
{
|
||||
std::size_t source_position = 0, mask_position = 0;
|
||||
|
||||
+33
-20
@@ -62,7 +62,7 @@ struct fmt_unveil<T, std::enable_if_t<std::is_floating_point<T>::value && sizeof
|
||||
// Convert FP to f64 and reinterpret as u64
|
||||
static inline u64 get(const f64& arg)
|
||||
{
|
||||
return *reinterpret_cast<const u64*>(reinterpret_cast<const u8*>(&arg));
|
||||
return std::bit_cast<u64>(arg);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -91,9 +91,9 @@ struct fmt_unveil<T, std::enable_if_t<std::is_enum<T>::value>>
|
||||
template <typename T>
|
||||
struct fmt_unveil<T*, void>
|
||||
{
|
||||
using type = const T*;
|
||||
using type = std::add_const_t<T>*;
|
||||
|
||||
static inline u64 get(const T* arg)
|
||||
static inline u64 get(type arg)
|
||||
{
|
||||
return reinterpret_cast<std::uintptr_t>(arg);
|
||||
}
|
||||
@@ -102,9 +102,9 @@ struct fmt_unveil<T*, void>
|
||||
template <typename T, std::size_t N>
|
||||
struct fmt_unveil<T[N], void>
|
||||
{
|
||||
using type = const T*;
|
||||
using type = std::add_const_t<T>*;
|
||||
|
||||
static inline u64 get(const T* arg)
|
||||
static inline u64 get(type arg)
|
||||
{
|
||||
return reinterpret_cast<std::uintptr_t>(arg);
|
||||
}
|
||||
@@ -140,18 +140,9 @@ struct fmt_class_string
|
||||
// Enum -> string function type
|
||||
using convert_t = const char*(*)(T value);
|
||||
|
||||
// Enum -> string function registered
|
||||
static convert_t convert_enum;
|
||||
|
||||
// Helper function (safely converts arg to enum value)
|
||||
static SAFE_BUFFERS FORCE_INLINE void format_enum(std::string& out, u64 arg, convert_t convert)
|
||||
{
|
||||
// Save convert function
|
||||
if (convert_enum == nullptr)
|
||||
{
|
||||
convert_enum = convert;
|
||||
}
|
||||
|
||||
const auto value = static_cast<std::underlying_type_t<T>>(arg);
|
||||
|
||||
// Check narrowing
|
||||
@@ -203,9 +194,6 @@ struct fmt_class_string
|
||||
static constexpr const char* unknown = nullptr;
|
||||
};
|
||||
|
||||
template <typename T, typename V>
|
||||
const char*(*fmt_class_string<T, V>::convert_enum)(T) = nullptr;
|
||||
|
||||
template <>
|
||||
struct fmt_class_string<const void*, void>
|
||||
{
|
||||
@@ -250,15 +238,38 @@ using fmt_args_t = const u64(&&)[sizeof...(Args) + 1];
|
||||
|
||||
namespace fmt
|
||||
{
|
||||
// Base-57 format helper
|
||||
struct base57
|
||||
{
|
||||
const uchar* data;
|
||||
std::size_t size;
|
||||
|
||||
template <typename T>
|
||||
base57(const T& arg)
|
||||
: data(reinterpret_cast<const uchar*>(&arg))
|
||||
, size(sizeof(T))
|
||||
{
|
||||
}
|
||||
|
||||
base57(const uchar* data, std::size_t size)
|
||||
: data(data)
|
||||
, size(size)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
SAFE_BUFFERS FORCE_INLINE const fmt_type_info* get_type_info()
|
||||
{
|
||||
// Constantly initialized null-terminated list of type-specific information
|
||||
static constexpr fmt_type_info result[sizeof...(Args) + 1]{fmt_type_info::make<Args>()...};
|
||||
static constexpr fmt_type_info result[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
constexpr const fmt_type_info type_info_v[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};
|
||||
|
||||
// Internal formatting function
|
||||
void raw_append(std::string& out, const char*, const fmt_type_info*, const u64*) noexcept;
|
||||
|
||||
@@ -266,7 +277,8 @@ namespace fmt
|
||||
template <typename... Args>
|
||||
SAFE_BUFFERS FORCE_INLINE void append(std::string& out, const char* fmt, const Args&... args)
|
||||
{
|
||||
raw_append(out, fmt, fmt::get_type_info<fmt_unveil_t<Args>...>(), fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
|
||||
static constexpr fmt_type_info type_list[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};
|
||||
raw_append(out, fmt, type_list, fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
|
||||
}
|
||||
|
||||
// Formatting function
|
||||
@@ -286,6 +298,7 @@ namespace fmt
|
||||
template <typename T = std::runtime_error, typename... Args>
|
||||
[[noreturn]] SAFE_BUFFERS FORCE_INLINE void throw_exception(const char* fmt, const Args&... args)
|
||||
{
|
||||
raw_throw_exception<T>(fmt, fmt::get_type_info<fmt_unveil_t<Args>...>(), fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
|
||||
static constexpr fmt_type_info type_list[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};
|
||||
raw_throw_exception<T>(fmt, type_list, fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -5,6 +5,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
|
||||
// Copy null-terminated string from std::string to char array with truncation
|
||||
template <std::size_t N>
|
||||
@@ -100,10 +101,10 @@ namespace fmt
|
||||
auto end = source.end();
|
||||
for (--end; it != end; ++it)
|
||||
{
|
||||
result += *it + separator;
|
||||
result += std::string{*it} + separator;
|
||||
}
|
||||
|
||||
return result + source.back();
|
||||
return result + std::string{source.back()};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -134,6 +135,7 @@ namespace fmt
|
||||
}
|
||||
|
||||
std::string to_upper(const std::string& string);
|
||||
std::string to_lower(const std::string& string);
|
||||
|
||||
bool match(const std::string& source, const std::string& mask);
|
||||
}
|
||||
|
||||
+465
-288
File diff suppressed because it is too large
Load Diff
+286
-211
@@ -1,14 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Atomic.h"
|
||||
#include "util/atomic.hpp"
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
#include "sema.h"
|
||||
#include "mutex.h"
|
||||
#include "cond.h"
|
||||
#include "lockless.h"
|
||||
|
||||
// Report error and call std::abort(), defined in main.cpp
|
||||
[[noreturn]] void report_fatal_error(const std::string&);
|
||||
@@ -33,92 +34,82 @@ enum class thread_class : u32
|
||||
ppu
|
||||
};
|
||||
|
||||
// Simple list of void() functors
|
||||
class task_stack
|
||||
enum class thread_state
|
||||
{
|
||||
struct task_base
|
||||
{
|
||||
std::unique_ptr<task_base> next;
|
||||
|
||||
virtual ~task_base();
|
||||
|
||||
virtual void invoke()
|
||||
{
|
||||
if (next)
|
||||
{
|
||||
next->invoke();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
struct task_type final : task_base
|
||||
{
|
||||
std::remove_reference_t<F> func;
|
||||
|
||||
task_type(F&& func)
|
||||
: func(std::forward<F>(func))
|
||||
{
|
||||
}
|
||||
|
||||
void invoke() final override
|
||||
{
|
||||
func();
|
||||
task_base::invoke();
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<task_base> m_stack;
|
||||
|
||||
public:
|
||||
task_stack() = default;
|
||||
|
||||
template <typename F>
|
||||
task_stack(F&& func)
|
||||
: m_stack(new task_type<F>(std::forward<F>(func)))
|
||||
created, // Initial state
|
||||
detached, // The thread has been detached to destroy its own named_thread object (can be dangerously misused)
|
||||
aborting, // The thread has been joined in the destructor or explicitly aborted (mutually exclusive with detached)
|
||||
finished // Final state, always set at the end of thread execution
|
||||
};
|
||||
|
||||
template <class Context>
|
||||
class named_thread;
|
||||
|
||||
template <typename T>
|
||||
struct result_storage
|
||||
{
|
||||
alignas(T) std::byte data[sizeof(T)];
|
||||
|
||||
static constexpr bool empty = false;
|
||||
|
||||
using type = T;
|
||||
|
||||
T* get()
|
||||
{
|
||||
return reinterpret_cast<T*>(&data);
|
||||
}
|
||||
|
||||
void push(task_stack stack)
|
||||
const T* get() const
|
||||
{
|
||||
auto _top = stack.m_stack.release();
|
||||
auto _next = m_stack.release();
|
||||
m_stack.reset(_top);
|
||||
while (UNLIKELY(_top->next)) _top = _top->next.get();
|
||||
_top->next.reset(_next);
|
||||
return reinterpret_cast<const T*>(&data);
|
||||
}
|
||||
|
||||
void reset()
|
||||
void destroy() noexcept
|
||||
{
|
||||
m_stack.reset();
|
||||
}
|
||||
|
||||
void invoke() const
|
||||
{
|
||||
if (m_stack)
|
||||
{
|
||||
m_stack->invoke();
|
||||
}
|
||||
get()->~T();
|
||||
}
|
||||
};
|
||||
|
||||
// Thread control class
|
||||
class thread_ctrl final
|
||||
template <>
|
||||
struct result_storage<void>
|
||||
{
|
||||
// Current thread
|
||||
static thread_local thread_ctrl* g_tls_this_thread;
|
||||
static constexpr bool empty = true;
|
||||
|
||||
// Target cpu core layout
|
||||
static atomic_t<native_core_arrangement> g_native_core_layout;
|
||||
using type = void;
|
||||
};
|
||||
|
||||
// Self pointer
|
||||
std::shared_ptr<thread_ctrl> m_self;
|
||||
template <class Context, typename... Args>
|
||||
using result_storage_t = result_storage<std::invoke_result_t<Context, Args...>>;
|
||||
|
||||
// Detect on_abort() method (should return void)
|
||||
template <typename T, typename = void>
|
||||
struct thread_on_abort : std::bool_constant<false> {};
|
||||
|
||||
template <typename T>
|
||||
struct thread_on_abort<T, decltype(std::declval<named_thread<T>&>().on_abort())> : std::bool_constant<true> {};
|
||||
|
||||
// Detect on_cleanup() static member function (should return void) (in C++20 can use destroying delete instead)
|
||||
template <typename T, typename = void>
|
||||
struct thread_on_cleanup : std::bool_constant<false> {};
|
||||
|
||||
template <typename T>
|
||||
struct thread_on_cleanup<T, decltype(named_thread<T>::on_cleanup(std::declval<named_thread<T>*>()))> : std::bool_constant<true> {};
|
||||
|
||||
// Thread base class
|
||||
class thread_base
|
||||
{
|
||||
// Native thread entry point function type
|
||||
#ifdef _WIN32
|
||||
using native_entry = uint(__stdcall*)(void* arg);
|
||||
#else
|
||||
using native_entry = void*(*)(void* arg);
|
||||
#endif
|
||||
|
||||
// Thread handle (platform-specific)
|
||||
atomic_t<std::uintptr_t> m_thread{0};
|
||||
|
||||
// Thread mutex
|
||||
mutable semaphore<> m_mutex;
|
||||
mutable shared_mutex m_mutex;
|
||||
|
||||
// Thread condition variable
|
||||
cond_variable m_cond;
|
||||
@@ -127,77 +118,123 @@ class thread_ctrl final
|
||||
atomic_t<u32> m_signal{0};
|
||||
|
||||
// Thread joining condition variable
|
||||
cond_variable m_jcv;
|
||||
mutable cond_variable m_jcv;
|
||||
|
||||
// Remotely set or caught exception
|
||||
std::exception_ptr m_exception;
|
||||
// Thread state
|
||||
atomic_t<thread_state> m_state = thread_state::created;
|
||||
|
||||
// Thread initial task or atexit task
|
||||
task_stack m_task;
|
||||
// Thread name
|
||||
lf_value<std::string> m_name;
|
||||
|
||||
// Fixed name
|
||||
std::string m_name;
|
||||
//
|
||||
atomic_t<u64> m_cycles = 0;
|
||||
|
||||
// Start thread
|
||||
static void start(const std::shared_ptr<thread_ctrl>&, task_stack);
|
||||
void start(native_entry);
|
||||
|
||||
// Called at the thread start
|
||||
void initialize();
|
||||
|
||||
// Called at the thread end
|
||||
void finalize(std::exception_ptr) noexcept;
|
||||
// Called at the thread end, returns true if needs destruction
|
||||
bool finalize(int) noexcept;
|
||||
|
||||
// Add task (atexit)
|
||||
static void _push(task_stack);
|
||||
// Cleanup after possibly deleting the thread instance
|
||||
static void finalize() noexcept;
|
||||
|
||||
friend class thread_ctrl;
|
||||
|
||||
template <class Context>
|
||||
friend class named_thread;
|
||||
|
||||
protected:
|
||||
thread_base(std::string_view name);
|
||||
|
||||
~thread_base();
|
||||
|
||||
public:
|
||||
// Get CPU cycles since last time this function was called. First call returns 0.
|
||||
u64 get_cycles();
|
||||
|
||||
// Wait for the thread (it does NOT change thread state, and can be called from multiple threads)
|
||||
void join() const;
|
||||
|
||||
// Notify the thread
|
||||
void notify();
|
||||
};
|
||||
|
||||
// Collection of global function for current thread
|
||||
class thread_ctrl final
|
||||
{
|
||||
// Current thread
|
||||
static thread_local thread_base* g_tls_this_thread;
|
||||
|
||||
// Target cpu core layout
|
||||
static atomic_t<native_core_arrangement> g_native_core_layout;
|
||||
|
||||
// Internal waiting function, may throw. Infinite value is -1.
|
||||
static bool _wait_for(u64 usec);
|
||||
|
||||
// Internal throwing function. Mutex must be locked and will be unlocked.
|
||||
[[noreturn]] void _throw();
|
||||
|
||||
// Internal notification function
|
||||
void _notify(cond_variable thread_ctrl::*);
|
||||
friend class thread_base;
|
||||
|
||||
public:
|
||||
thread_ctrl(std::string&& name);
|
||||
|
||||
thread_ctrl(const thread_ctrl&) = delete;
|
||||
|
||||
~thread_ctrl();
|
||||
|
||||
// Get thread name
|
||||
const std::string& get_name() const
|
||||
// Get current thread name
|
||||
static std::string_view get_name()
|
||||
{
|
||||
return m_name;
|
||||
return g_tls_this_thread->m_name.get();
|
||||
}
|
||||
|
||||
// Get exception
|
||||
std::exception_ptr get_exception() const;
|
||||
// Get thread name
|
||||
template <typename T>
|
||||
static std::string_view get_name(const named_thread<T>& thread)
|
||||
{
|
||||
return static_cast<const thread_base&>(thread).m_name.get();
|
||||
}
|
||||
|
||||
// Set exception
|
||||
void set_exception(std::exception_ptr ptr);
|
||||
// Set current thread name (not recommended)
|
||||
static void set_name(std::string_view name)
|
||||
{
|
||||
g_tls_this_thread->m_name.assign(name);
|
||||
}
|
||||
|
||||
// Get thread result (may throw, simultaneous joining allowed)
|
||||
void join();
|
||||
// Set thread name (not recommended)
|
||||
template <typename T>
|
||||
static void set_name(named_thread<T>& thread, std::string_view name)
|
||||
{
|
||||
static_cast<thread_base&>(thread).m_name.assign(name);
|
||||
}
|
||||
|
||||
// Notify the thread
|
||||
void notify();
|
||||
template <typename T>
|
||||
static u64 get_cycles(named_thread<T>& thread)
|
||||
{
|
||||
return static_cast<thread_base&>(thread).get_cycles();
|
||||
}
|
||||
|
||||
// Wait once with timeout. Abortable, may throw. May spuriously return false.
|
||||
template <typename T>
|
||||
static void notify(named_thread<T>& thread)
|
||||
{
|
||||
static_cast<thread_base&>(thread).notify();
|
||||
}
|
||||
|
||||
// Read current state
|
||||
static inline thread_state state()
|
||||
{
|
||||
return g_tls_this_thread->m_state;
|
||||
}
|
||||
|
||||
// Wait once with timeout. May spuriously return false.
|
||||
static inline bool wait_for(u64 usec)
|
||||
{
|
||||
return _wait_for(usec);
|
||||
}
|
||||
|
||||
// Wait. Abortable, may throw.
|
||||
// Wait.
|
||||
static inline void wait()
|
||||
{
|
||||
_wait_for(-1);
|
||||
}
|
||||
|
||||
// Wait until pred(). Abortable, may throw.
|
||||
template<typename F, typename RT = std::result_of_t<F()>>
|
||||
// Wait until pred().
|
||||
template <typename F, typename RT = std::invoke_result_t<F>>
|
||||
static inline RT wait(F&& pred)
|
||||
{
|
||||
while (true)
|
||||
@@ -211,145 +248,183 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Wait eternally until aborted.
|
||||
[[noreturn]] static inline void eternalize()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
_wait_for(-1);
|
||||
}
|
||||
}
|
||||
|
||||
// Test exception (may throw).
|
||||
static void test();
|
||||
|
||||
// Get current thread (may be nullptr)
|
||||
static thread_ctrl* get_current()
|
||||
static thread_base* get_current()
|
||||
{
|
||||
return g_tls_this_thread;
|
||||
}
|
||||
|
||||
// Register function at thread exit (for the current thread)
|
||||
template<typename F>
|
||||
static inline void atexit(F&& func)
|
||||
{
|
||||
_push(std::forward<F>(func));
|
||||
}
|
||||
|
||||
// Create detached named thread
|
||||
template<typename N, typename F>
|
||||
static inline void spawn(N&& name, F&& func)
|
||||
{
|
||||
auto out = std::make_shared<thread_ctrl>(std::forward<N>(name));
|
||||
|
||||
thread_ctrl::start(out, std::forward<F>(func));
|
||||
}
|
||||
|
||||
// Named thread factory
|
||||
template<typename N, typename F>
|
||||
static inline void spawn(std::shared_ptr<thread_ctrl>& out, N&& name, F&& func)
|
||||
{
|
||||
out = std::make_shared<thread_ctrl>(std::forward<N>(name));
|
||||
|
||||
thread_ctrl::start(out, std::forward<F>(func));
|
||||
}
|
||||
|
||||
// Detect layout
|
||||
static void detect_cpu_layout();
|
||||
|
||||
// Returns a core affinity mask. Set whether to generate the high priority set or not
|
||||
static u16 get_affinity_mask(thread_class group);
|
||||
static u64 get_affinity_mask(thread_class group);
|
||||
|
||||
// Sets the native thread priority
|
||||
static void set_native_priority(int priority);
|
||||
|
||||
// Sets the preferred affinity mask for this thread
|
||||
static void set_thread_affinity_mask(u16 mask);
|
||||
static void set_thread_affinity_mask(u64 mask);
|
||||
|
||||
// Spawn a detached named thread
|
||||
template <typename F>
|
||||
static void spawn(std::string_view name, F&& func)
|
||||
{
|
||||
new named_thread<F>(thread_state::detached, name, std::forward<F>(func));
|
||||
}
|
||||
};
|
||||
|
||||
class named_thread
|
||||
// Derived from the callable object Context, possibly a lambda
|
||||
template <class Context>
|
||||
class named_thread final : public Context, result_storage_t<Context>, thread_base
|
||||
{
|
||||
// Pointer to managed resource (shared with actual thread)
|
||||
std::shared_ptr<thread_ctrl> m_thread;
|
||||
using result = result_storage_t<Context>;
|
||||
using thread = thread_base;
|
||||
|
||||
// Type-erased thread entry point
|
||||
#ifdef _WIN32
|
||||
static inline uint __stdcall entry_point(void* arg) try
|
||||
#else
|
||||
static inline void* entry_point(void* arg) try
|
||||
#endif
|
||||
{
|
||||
const auto _this = static_cast<named_thread*>(static_cast<thread*>(arg));
|
||||
|
||||
// Perform self-cleanup if necessary
|
||||
if (_this->entry_point())
|
||||
{
|
||||
// Call on_cleanup() static member function if it's available
|
||||
if constexpr (thread_on_cleanup<Context>())
|
||||
{
|
||||
Context::on_cleanup(_this);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete _this;
|
||||
}
|
||||
}
|
||||
|
||||
thread::finalize();
|
||||
return 0;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
catch_all_exceptions();
|
||||
}
|
||||
|
||||
bool entry_point()
|
||||
{
|
||||
thread::initialize();
|
||||
|
||||
if constexpr (result::empty)
|
||||
{
|
||||
// No result
|
||||
Context::operator()();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Construct the result using placement new (copy elision should happen)
|
||||
new (result::get()) typename result::type(Context::operator()());
|
||||
}
|
||||
|
||||
return thread::finalize(0);
|
||||
}
|
||||
|
||||
// Detached thread constructor
|
||||
named_thread(thread_state s, std::string_view name, Context&& f)
|
||||
: Context(std::forward<Context>(f))
|
||||
, thread(name)
|
||||
{
|
||||
thread::m_state.raw() = s;
|
||||
thread::start(&named_thread::entry_point);
|
||||
}
|
||||
|
||||
friend class thread_ctrl;
|
||||
|
||||
public:
|
||||
named_thread();
|
||||
// Normal forwarding constructor
|
||||
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<Context, Args&&...>>>
|
||||
named_thread(std::string_view name, Args&&... args)
|
||||
: Context(std::forward<Args>(args)...)
|
||||
, thread(name)
|
||||
{
|
||||
thread::start(&named_thread::entry_point);
|
||||
}
|
||||
|
||||
virtual ~named_thread();
|
||||
// Lambda constructor, also the implicit deduction guide candidate
|
||||
named_thread(std::string_view name, Context&& f)
|
||||
: Context(std::forward<Context>(f))
|
||||
, thread(name)
|
||||
{
|
||||
thread::start(&named_thread::entry_point);
|
||||
}
|
||||
|
||||
// Deleted copy/move constructors + copy/move operators
|
||||
named_thread(const named_thread&) = delete;
|
||||
|
||||
// Get thread name
|
||||
virtual std::string get_name() const;
|
||||
named_thread& operator=(const named_thread&) = delete;
|
||||
|
||||
protected:
|
||||
// Start thread (cannot be called from the constructor: should throw in such case)
|
||||
void start_thread(const std::shared_ptr<void>& _this);
|
||||
|
||||
// Thread task (called in the thread)
|
||||
virtual void on_task() = 0;
|
||||
|
||||
// Thread finalization (called after on_task)
|
||||
virtual void on_exit() {}
|
||||
|
||||
// Called once upon thread spawn within the thread's own context
|
||||
virtual void on_spawn() {}
|
||||
|
||||
public:
|
||||
// ID initialization
|
||||
virtual void on_init(const std::shared_ptr<void>& _this)
|
||||
// Wait for the completion and access result (if not void)
|
||||
[[nodiscard]] decltype(auto) operator()()
|
||||
{
|
||||
return start_thread(_this);
|
||||
thread::join();
|
||||
|
||||
if constexpr (!result::empty)
|
||||
{
|
||||
return *result::get();
|
||||
}
|
||||
}
|
||||
|
||||
// ID finalization
|
||||
virtual void on_stop()
|
||||
// Wait for the completion and access result (if not void)
|
||||
[[nodiscard]] decltype(auto) operator()() const
|
||||
{
|
||||
m_thread->join();
|
||||
thread::join();
|
||||
|
||||
if constexpr (!result::empty)
|
||||
{
|
||||
return *result::get();
|
||||
}
|
||||
}
|
||||
|
||||
// Access thread_ctrl
|
||||
thread_ctrl* get() const
|
||||
// Access thread state
|
||||
operator thread_state() const
|
||||
{
|
||||
return m_thread.get();
|
||||
return thread::m_state.load();
|
||||
}
|
||||
|
||||
void join() const
|
||||
// Try to abort/detach
|
||||
named_thread& operator=(thread_state s)
|
||||
{
|
||||
return m_thread->join();
|
||||
if (s != thread_state::aborting && s != thread_state::detached)
|
||||
{
|
||||
ASSUME(0);
|
||||
}
|
||||
|
||||
if (thread::m_state.compare_and_swap_test(thread_state::created, s))
|
||||
{
|
||||
if (s == thread_state::aborting)
|
||||
{
|
||||
// Call on_abort() method if it's available
|
||||
if constexpr (thread_on_abort<Context>())
|
||||
{
|
||||
Context::on_abort();
|
||||
}
|
||||
}
|
||||
|
||||
thread::notify();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void notify() const
|
||||
// Context type doesn't need virtual destructor
|
||||
~named_thread()
|
||||
{
|
||||
return m_thread->notify();
|
||||
}
|
||||
};
|
||||
|
||||
// Wrapper for named thread, joins automatically in the destructor, can only be used in function scope
|
||||
class scope_thread final
|
||||
{
|
||||
std::shared_ptr<thread_ctrl> m_thread;
|
||||
|
||||
public:
|
||||
template<typename N, typename F>
|
||||
scope_thread(N&& name, F&& func)
|
||||
{
|
||||
thread_ctrl::spawn(m_thread, std::forward<N>(name), std::forward<F>(func));
|
||||
}
|
||||
|
||||
// Deleted copy/move constructors + copy/move operators
|
||||
scope_thread(const scope_thread&) = delete;
|
||||
|
||||
// Destructor with exceptions allowed
|
||||
~scope_thread() noexcept(false)
|
||||
{
|
||||
m_thread->join();
|
||||
}
|
||||
|
||||
// Access thread_ctrl
|
||||
thread_ctrl* get() const
|
||||
{
|
||||
return m_thread.get();
|
||||
*this = thread_state::aborting;
|
||||
thread::join();
|
||||
|
||||
if constexpr (!result::empty)
|
||||
{
|
||||
result::destroy();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
class Timer
|
||||
@@ -49,4 +51,11 @@ public:
|
||||
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(now - m_start).count();
|
||||
}
|
||||
|
||||
u64 GetMsSince(std::chrono::steady_clock::time_point timestamp)
|
||||
{
|
||||
std::chrono::steady_clock::time_point now = m_stopped ? m_end : std::chrono::steady_clock::now();
|
||||
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(now - timestamp).count();
|
||||
}
|
||||
};
|
||||
|
||||
+194
-4
@@ -7,10 +7,27 @@
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#ifdef __NR_memfd_create
|
||||
#elif __x86_64__
|
||||
#define __NR_memfd_create 319
|
||||
#elif __aarch64__
|
||||
#define __NR_memfd_create 279
|
||||
#endif
|
||||
|
||||
static int memfd_create_(const char *name, uint flags)
|
||||
{
|
||||
return syscall(__NR_memfd_create, name, flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace utils
|
||||
{
|
||||
// Convert memory protection (internal)
|
||||
@@ -63,7 +80,7 @@ namespace utils
|
||||
#ifdef _WIN32
|
||||
verify(HERE), ::VirtualAlloc(pointer, size, MEM_COMMIT, +prot);
|
||||
#else
|
||||
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), ::align(size, 4096), +prot) != -1;
|
||||
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), size + ((u64)pointer & 4095), +prot) != -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -76,13 +93,186 @@ namespace utils
|
||||
#endif
|
||||
}
|
||||
|
||||
void memory_reset(void* pointer, std::size_t size, protection prot)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
memory_decommit(pointer, size);
|
||||
memory_commit(pointer, size, prot);
|
||||
#else
|
||||
verify(HERE), ::mmap(pointer, size, +prot, MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void memory_release(void* pointer, std::size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
verify(HERE), ::VirtualFree(pointer, 0, MEM_RELEASE);
|
||||
#else
|
||||
verify(HERE), ::munmap(pointer, size) != -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void memory_protect(void* pointer, std::size_t size, protection prot)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DWORD old;
|
||||
verify(HERE), ::VirtualProtect(pointer, size, +prot, &old);
|
||||
for (u64 addr = (u64)pointer, end = addr + size; addr < end;)
|
||||
{
|
||||
const u64 boundary = (addr + 0x10000) & -0x10000;
|
||||
const u64 block_size = std::min(boundary, end) - addr;
|
||||
|
||||
DWORD old;
|
||||
if (!::VirtualProtect((LPVOID)addr, block_size, +prot, &old))
|
||||
{
|
||||
fmt::throw_exception("VirtualProtect failed (%p, 0x%x, addr=0x%x, error=%#x)", pointer, size, addr, GetLastError());
|
||||
}
|
||||
|
||||
// Next region
|
||||
addr += block_size;
|
||||
}
|
||||
#else
|
||||
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), ::align(size, 4096), +prot) != -1;
|
||||
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), size + ((u64)pointer & 4095), +prot) != -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
shm::shm(u32 size)
|
||||
: m_size(::align(size, 0x10000))
|
||||
{
|
||||
#ifdef _WIN32
|
||||
m_handle = ::CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, m_size, NULL);
|
||||
verify(HERE), m_handle != INVALID_HANDLE_VALUE;
|
||||
#elif __linux__
|
||||
m_file = ::memfd_create_("", 0);
|
||||
verify(HERE), m_file >= 0;
|
||||
verify(HERE), ::ftruncate(m_file, m_size) >= 0;
|
||||
#else
|
||||
while ((m_file = ::shm_open("/rpcs3-mem1", O_RDWR | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR)) == -1)
|
||||
{
|
||||
if (m_file == -1 && errno == EMFILE)
|
||||
{
|
||||
fmt::throw_exception("Too many open files. Raise the limit and try again.");
|
||||
}
|
||||
|
||||
verify(HERE), errno == EEXIST;
|
||||
}
|
||||
|
||||
verify(HERE), ::shm_unlink("/rpcs3-mem1") >= 0;
|
||||
verify(HERE), ::ftruncate(m_file, m_size) >= 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
shm::~shm()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
::CloseHandle(m_handle);
|
||||
#else
|
||||
::close(m_file);
|
||||
#endif
|
||||
}
|
||||
|
||||
u8* shm::map(void* ptr, protection prot) const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DWORD access = FILE_MAP_WRITE;
|
||||
switch (prot)
|
||||
{
|
||||
case protection::rw:
|
||||
case protection::ro:
|
||||
case protection::no:
|
||||
break;
|
||||
case protection::wx:
|
||||
case protection::rx:
|
||||
access |= FILE_MAP_EXECUTE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (auto ret = static_cast<u8*>(::MapViewOfFileEx(m_handle, access, 0, 0, m_size, ptr)))
|
||||
{
|
||||
if (prot != protection::rw && prot != protection::wx)
|
||||
{
|
||||
DWORD old;
|
||||
if (!::VirtualProtect(ret, m_size, +prot, &old))
|
||||
{
|
||||
::UnmapViewOfFile(ret);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
#else
|
||||
return static_cast<u8*>(::mmap((void*)((u64)ptr & -0x10000), m_size, +prot, MAP_SHARED | (ptr ? MAP_FIXED : 0), m_file, 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
u8* shm::map_critical(void* ptr, protection prot)
|
||||
{
|
||||
const auto target = (u8*)((u64)ptr & -0x10000);
|
||||
|
||||
#ifdef _WIN32
|
||||
::MEMORY_BASIC_INFORMATION mem;
|
||||
if (!::VirtualQuery(target, &mem, sizeof(mem)) || mem.State != MEM_RESERVE || !::VirtualFree(mem.AllocationBase, 0, MEM_RELEASE))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto base = (u8*)mem.AllocationBase;
|
||||
const auto size = mem.RegionSize + (target - base);
|
||||
|
||||
if (base < target && !::VirtualAlloc(base, target - base, MEM_RESERVE, PAGE_NOACCESS))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (target + m_size < base + size && !::VirtualAlloc(target + m_size, base + size - target - m_size, MEM_RESERVE, PAGE_NOACCESS))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return this->map(target, prot);
|
||||
}
|
||||
|
||||
void shm::unmap(void* ptr) const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
::UnmapViewOfFile(ptr);
|
||||
#else
|
||||
::munmap(ptr, m_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
void shm::unmap_critical(void* ptr)
|
||||
{
|
||||
const auto target = (u8*)((u64)ptr & -0x10000);
|
||||
|
||||
this->unmap(target);
|
||||
|
||||
#ifdef _WIN32
|
||||
::MEMORY_BASIC_INFORMATION mem, mem2;
|
||||
if (!::VirtualQuery(target - 1, &mem, sizeof(mem)) || !::VirtualQuery(target + m_size, &mem2, sizeof(mem2)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (mem.State == MEM_RESERVE && !::VirtualFree(mem.AllocationBase, 0, MEM_RELEASE))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (mem2.State == MEM_RESERVE && !::VirtualFree(mem2.AllocationBase, 0, MEM_RELEASE))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto size1 = mem.State == MEM_RESERVE ? target - (u8*)mem.AllocationBase : 0;
|
||||
const auto size2 = mem2.State == MEM_RESERVE ? mem2.RegionSize : 0;
|
||||
|
||||
if (!::VirtualAlloc(mem.State == MEM_RESERVE ? mem.AllocationBase : target, m_size + size1 + size2, MEM_RESERVE, PAGE_NOACCESS))
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace utils
|
||||
{
|
||||
// Memory protection type
|
||||
@@ -25,11 +27,52 @@ namespace utils
|
||||
*/
|
||||
void memory_commit(void* pointer, std::size_t size, protection prot = protection::rw);
|
||||
|
||||
/**
|
||||
* Decommit all memory committed via commit_page_memory.
|
||||
*/
|
||||
// Decommit all memory committed via commit_page_memory.
|
||||
void memory_decommit(void* pointer, std::size_t size);
|
||||
|
||||
// Decommit all memory and commit it again.
|
||||
void memory_reset(void* pointer, std::size_t size, protection prot = protection::rw);
|
||||
|
||||
// Free memory after reserved by memory_reserve, should specify original size
|
||||
void memory_release(void* pointer, std::size_t size);
|
||||
|
||||
// Set memory protection
|
||||
void memory_protect(void* pointer, std::size_t size, protection prot);
|
||||
|
||||
// Shared memory handle
|
||||
class shm
|
||||
{
|
||||
#ifdef _WIN32
|
||||
void* m_handle;
|
||||
#else
|
||||
int m_file;
|
||||
#endif
|
||||
u32 m_size;
|
||||
|
||||
public:
|
||||
explicit shm(u32 size);
|
||||
|
||||
shm(const shm&) = delete;
|
||||
|
||||
shm& operator=(const shm&) = delete;
|
||||
|
||||
~shm();
|
||||
|
||||
// Map shared memory
|
||||
u8* map(void* ptr, protection prot = protection::rw) const;
|
||||
|
||||
// Map shared memory over reserved memory region, which is unsafe (non-atomic) under Win32
|
||||
u8* map_critical(void* ptr, protection prot = protection::rw);
|
||||
|
||||
// Unmap shared memory
|
||||
void unmap(void* ptr) const;
|
||||
|
||||
// Unmap shared memory, undoing map_critical
|
||||
void unmap_critical(void* ptr);
|
||||
|
||||
u32 size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,592 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "StrFmt.h"
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace utils
|
||||
{
|
||||
class address_range_vector;
|
||||
|
||||
|
||||
/**
|
||||
* Constexprs
|
||||
*/
|
||||
constexpr inline u32 page_start(u32 addr)
|
||||
{
|
||||
return addr & ~4095u;
|
||||
}
|
||||
|
||||
constexpr inline u32 next_page(u32 addr)
|
||||
{
|
||||
return page_start(addr + 4096u);
|
||||
}
|
||||
|
||||
constexpr inline u32 page_end(u32 addr)
|
||||
{
|
||||
return next_page(addr) - 1;
|
||||
}
|
||||
|
||||
constexpr inline u32 is_page_aligned(u32 addr)
|
||||
{
|
||||
return page_start(addr) == addr;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Address Range utility class
|
||||
*/
|
||||
class address_range
|
||||
{
|
||||
public:
|
||||
u32 start = UINT32_MAX; // First address in range
|
||||
u32 end = 0; // Last address
|
||||
|
||||
private:
|
||||
// Helper constexprs
|
||||
static constexpr inline bool range_overlaps(u32 start1, u32 end1, u32 start2, u32 end2)
|
||||
{
|
||||
return (start1 <= end2 && start2 <= end1);
|
||||
}
|
||||
|
||||
static constexpr inline bool address_overlaps(u32 address, u32 start, u32 end)
|
||||
{
|
||||
return (start <= address && address <= end);
|
||||
}
|
||||
|
||||
static constexpr inline bool range_inside_range(u32 start1, u32 end1, u32 start2, u32 end2)
|
||||
{
|
||||
return (start1 >= start2 && end1 <= end2);
|
||||
}
|
||||
|
||||
address_range(u32 _start, u32 _end) : start(_start), end(_end) {}
|
||||
|
||||
public:
|
||||
// Constructors
|
||||
address_range() = default;
|
||||
address_range(const address_range &other) : start(other.start), end(other.end) {}
|
||||
|
||||
static inline address_range start_length(u32 _start, u32 _length)
|
||||
{
|
||||
return address_range(_start, _start + (_length - 1));
|
||||
}
|
||||
|
||||
static inline address_range start_end(u32 _start, u32 _end)
|
||||
{
|
||||
return address_range(_start, _end);
|
||||
}
|
||||
|
||||
// Length
|
||||
u32 length() const
|
||||
{
|
||||
AUDIT(valid());
|
||||
return end - start + 1;
|
||||
}
|
||||
|
||||
void set_length(const u32 new_length)
|
||||
{
|
||||
end = start + new_length - 1;
|
||||
ASSERT(valid());
|
||||
}
|
||||
|
||||
u32 next_address() const
|
||||
{
|
||||
return end + 1;
|
||||
}
|
||||
|
||||
u32 prev_address() const
|
||||
{
|
||||
return start - 1;
|
||||
}
|
||||
|
||||
// Overlapping checks
|
||||
bool overlaps(const address_range &other) const
|
||||
{
|
||||
AUDIT(valid() && other.valid());
|
||||
return range_overlaps(start, end, other.start, other.end);
|
||||
}
|
||||
|
||||
bool overlaps(const u32 addr) const
|
||||
{
|
||||
AUDIT(valid());
|
||||
return address_overlaps(addr, start, end);
|
||||
}
|
||||
|
||||
bool inside(const address_range &other) const
|
||||
{
|
||||
AUDIT(valid() && other.valid());
|
||||
return range_inside_range(start, end, other.start, other.end);
|
||||
}
|
||||
|
||||
inline bool inside(const address_range_vector &vec) const;
|
||||
inline bool overlaps(const address_range_vector &vec) const;
|
||||
|
||||
bool touches(const address_range &other) const
|
||||
{
|
||||
AUDIT(valid() && other.valid());
|
||||
// returns true if there is overlap, or if sections are side-by-side
|
||||
return overlaps(other) || other.start == next_address() || other.end == prev_address();
|
||||
}
|
||||
|
||||
// Utilities
|
||||
s32 signed_distance(const address_range &other) const
|
||||
{
|
||||
if (touches(other))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// other after this
|
||||
if (other.start > end)
|
||||
{
|
||||
return (s32)(other.start - end - 1);
|
||||
}
|
||||
|
||||
// this after other
|
||||
AUDIT(start > other.end);
|
||||
return -((s32)(start - other.end - 1));
|
||||
}
|
||||
|
||||
u32 distance(const address_range &other) const
|
||||
{
|
||||
if (touches(other))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// other after this
|
||||
if (other.start > end)
|
||||
{
|
||||
return (other.start - end - 1);
|
||||
}
|
||||
|
||||
// this after other
|
||||
AUDIT(start > other.end);
|
||||
return (start - other.end - 1);
|
||||
}
|
||||
|
||||
address_range get_min_max(const address_range &other) const
|
||||
{
|
||||
return {
|
||||
std::min(valid() ? start : UINT32_MAX, other.valid() ? other.start : UINT32_MAX),
|
||||
std::max(valid() ? end : 0, other.valid() ? other.end : 0)
|
||||
};
|
||||
}
|
||||
|
||||
void set_min_max(const address_range &other)
|
||||
{
|
||||
const address_range _range = get_min_max(other);
|
||||
start = _range.start;
|
||||
end = _range.end;
|
||||
}
|
||||
|
||||
bool is_page_range() const
|
||||
{
|
||||
return (valid() && start % 4096u == 0 && length() % 4096u == 0);
|
||||
}
|
||||
|
||||
address_range to_page_range() const
|
||||
{
|
||||
AUDIT(valid());
|
||||
return { page_start(start), page_end(end) };
|
||||
}
|
||||
|
||||
void page_align()
|
||||
{
|
||||
AUDIT(valid());
|
||||
start = page_start(start);
|
||||
end = page_end(end);
|
||||
AUDIT(is_page_range());
|
||||
}
|
||||
|
||||
address_range get_intersect(const address_range &clamp) const
|
||||
{
|
||||
if (!valid() || !clamp.valid())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return { std::max(start, clamp.start), std::min(end, clamp.end) };
|
||||
}
|
||||
|
||||
void intersect(const address_range &clamp)
|
||||
{
|
||||
if (!clamp.valid())
|
||||
{
|
||||
invalidate();
|
||||
}
|
||||
else
|
||||
{
|
||||
start = std::max(start, clamp.start);
|
||||
end = std::min(end, clamp.end);
|
||||
}
|
||||
}
|
||||
|
||||
// Validity
|
||||
bool valid() const
|
||||
{
|
||||
return (start <= end);
|
||||
}
|
||||
|
||||
void invalidate()
|
||||
{
|
||||
start = UINT32_MAX;
|
||||
end = 0;
|
||||
}
|
||||
|
||||
// Comparison Operators
|
||||
bool operator ==(const address_range &other) const
|
||||
{
|
||||
return (start == other.start && end == other.end);
|
||||
}
|
||||
|
||||
bool operator !=(const address_range &other) const
|
||||
{
|
||||
return (start != other.start || end != other.end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug
|
||||
*/
|
||||
std::string str() const
|
||||
{
|
||||
return fmt::format("{0x%x->0x%x}", start, end);
|
||||
}
|
||||
};
|
||||
|
||||
static inline address_range page_for(u32 addr)
|
||||
{
|
||||
return address_range::start_end(page_start(addr), page_end(addr));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Address Range Vector utility class
|
||||
*
|
||||
* Collection of address_range objects. Allows for merging and removing ranges from the set.
|
||||
*/
|
||||
class address_range_vector
|
||||
{
|
||||
public:
|
||||
using vector_type = typename std::vector<address_range>;
|
||||
using iterator = typename vector_type::iterator;
|
||||
using const_iterator = typename vector_type::const_iterator;
|
||||
using size_type = typename vector_type::size_type;
|
||||
|
||||
private:
|
||||
vector_type data;
|
||||
|
||||
public:
|
||||
// Wrapped functions
|
||||
inline void reserve(size_t nr) { data.reserve(nr); }
|
||||
inline void clear() { data.clear(); }
|
||||
inline size_type size() const { return data.size(); }
|
||||
inline bool empty() const { return data.empty(); }
|
||||
inline address_range& operator[](size_type n) { return data[n]; }
|
||||
inline const address_range& operator[](size_type n) const { return data[n]; }
|
||||
inline iterator begin() { return data.begin(); }
|
||||
inline const_iterator begin() const { return data.begin(); }
|
||||
inline iterator end() { return data.end(); }
|
||||
inline const_iterator end() const { return data.end(); }
|
||||
|
||||
// Search for ranges that touch new_range. If found, merge instead of adding new_range.
|
||||
// When adding a new range, re-use invalid ranges whenever possible
|
||||
void merge(const address_range &new_range)
|
||||
{
|
||||
// Note the case where we have
|
||||
// AAAA BBBB
|
||||
// CCCC
|
||||
// If we have data={A,B}, and new_range=C, we have to merge A with C, then B with A and invalidate B
|
||||
|
||||
if (!new_range.valid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
address_range *found = nullptr;
|
||||
address_range *invalid = nullptr;
|
||||
|
||||
for (auto &existing : data)
|
||||
{
|
||||
if (!existing.valid())
|
||||
{
|
||||
invalid = &existing;
|
||||
continue;
|
||||
}
|
||||
|
||||
// range1 overlaps, is immediately before, or is immediately after range2
|
||||
if (existing.touches(new_range))
|
||||
{
|
||||
if (found != nullptr)
|
||||
{
|
||||
// Already found a match, merge and invalidate "existing"
|
||||
found->set_min_max(existing);
|
||||
existing.invalidate();
|
||||
}
|
||||
else
|
||||
{
|
||||
// First match, merge "new_range"
|
||||
existing.set_min_max(new_range);
|
||||
found = &existing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found != nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (invalid != nullptr)
|
||||
{
|
||||
*invalid = new_range;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.push_back(new_range);
|
||||
}
|
||||
|
||||
AUDIT(check_consistency());
|
||||
}
|
||||
|
||||
void merge(const address_range_vector &other)
|
||||
{
|
||||
for (const address_range &new_range : other)
|
||||
{
|
||||
merge(new_range);
|
||||
}
|
||||
}
|
||||
|
||||
// Exclude a given range from data
|
||||
void exclude(const address_range &exclusion)
|
||||
{
|
||||
// Note the case where we have
|
||||
// AAAAAAA
|
||||
// EEE
|
||||
// where data={A} and exclusion=E.
|
||||
// In this case, we need to reduce A to the head (before E starts), and then create a new address_range B for the tail (after E ends), i.e.
|
||||
// AA BB
|
||||
// EEE
|
||||
|
||||
if (!exclusion.valid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
address_range *invalid = nullptr; // try to re-use an invalid range instead of calling push_back
|
||||
|
||||
// We use index access because we might have to push_back within the loop, which could invalidate the iterators
|
||||
size_type _size = data.size();
|
||||
for (size_type n = 0; n < _size; ++n)
|
||||
{
|
||||
address_range &existing = data[n];
|
||||
|
||||
if (!existing.valid())
|
||||
{
|
||||
// Null
|
||||
invalid = &existing;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!existing.overlaps(exclusion))
|
||||
{
|
||||
// No overlap, skip
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool head_excluded = exclusion.overlaps(existing.start); // This section has its start inside excluded range
|
||||
const bool tail_excluded = exclusion.overlaps(existing.end); // This section has its end inside excluded range
|
||||
|
||||
if (head_excluded && tail_excluded)
|
||||
{
|
||||
// Cannot be salvaged, fully excluded
|
||||
existing.invalidate();
|
||||
invalid = &existing;
|
||||
}
|
||||
else if (head_excluded)
|
||||
{
|
||||
// Head overlaps, truncate head
|
||||
existing.start = exclusion.next_address();
|
||||
}
|
||||
else if (tail_excluded)
|
||||
{
|
||||
// Tail overlaps, truncate tail
|
||||
existing.end = exclusion.prev_address();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Section sits in the middle (see comment above function header)
|
||||
AUDIT(exclusion.inside(existing));
|
||||
const auto tail_end = existing.end;
|
||||
|
||||
// Head
|
||||
existing.end = exclusion.prev_address();
|
||||
|
||||
// Tail
|
||||
if (invalid != nullptr)
|
||||
{
|
||||
invalid->start = exclusion.next_address();
|
||||
invalid->end = tail_end;
|
||||
invalid = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// IMPORTANT: adding to data invalidates "existing". This must be done last!
|
||||
data.push_back(address_range::start_end(exclusion.next_address(), tail_end));
|
||||
}
|
||||
}
|
||||
}
|
||||
AUDIT(check_consistency());
|
||||
AUDIT(!overlaps(exclusion));
|
||||
}
|
||||
|
||||
void exclude(const address_range_vector &other)
|
||||
{
|
||||
for (const address_range &exclusion : other)
|
||||
{
|
||||
exclude(exclusion);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks the consistency of this vector.
|
||||
// Will fail if ranges within the vector overlap our touch each-other
|
||||
bool check_consistency() const
|
||||
{
|
||||
size_t _size = data.size();
|
||||
|
||||
for (size_t i = 0; i < _size; ++i)
|
||||
{
|
||||
const auto &r1 = data[i];
|
||||
if (!r1.valid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t j = i + 1; j < _size; ++j)
|
||||
{
|
||||
const auto &r2 = data[j];
|
||||
if (!r2.valid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (r1.touches(r2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Test for overlap with a given range
|
||||
bool overlaps(const address_range &range) const
|
||||
{
|
||||
for (const address_range ¤t : data)
|
||||
{
|
||||
if (!current.valid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current.overlaps(range))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test for overlap with a given address_range vector
|
||||
bool overlaps(const address_range_vector &other) const
|
||||
{
|
||||
for (const address_range &rng1 : data)
|
||||
{
|
||||
if (!rng1.valid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const address_range &rng2 : other.data)
|
||||
{
|
||||
if (!rng2.valid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rng1.overlaps(rng2))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test if a given range is fully contained inside this vector
|
||||
bool contains(const address_range &range) const
|
||||
{
|
||||
for (const address_range &cur : *this)
|
||||
{
|
||||
if (!cur.valid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (range.inside(cur))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test if all ranges in this vector are full contained inside a specific range
|
||||
bool inside(const address_range &range) const
|
||||
{
|
||||
for (const address_range &cur : *this)
|
||||
{
|
||||
if (!cur.valid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!cur.inside(range))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// These declarations must be done after address_range_vector has been defined
|
||||
bool address_range::inside(const address_range_vector &vec) const
|
||||
{
|
||||
return vec.contains(*this);
|
||||
}
|
||||
|
||||
bool address_range::overlaps(const address_range_vector &vec) const
|
||||
{
|
||||
return vec.overlaps(*this);
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
|
||||
|
||||
namespace std {
|
||||
static_assert(sizeof(size_t) >= 2 * sizeof(u32), "size_t must be at least twice the size of u32");
|
||||
|
||||
template <>
|
||||
struct hash<utils::address_range>
|
||||
{
|
||||
std::size_t operator()(const utils::address_range& k) const
|
||||
{
|
||||
// we can guarantee a unique hash since our type is 64 bits and size_t as well
|
||||
return (size_t{ k.start } << 32) | size_t{ k.end };
|
||||
}
|
||||
};
|
||||
}
|
||||
+228
@@ -0,0 +1,228 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace utils
|
||||
{
|
||||
inline u32 cntlz32(u32 arg, bool nonzero = false)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
ulong res;
|
||||
return _BitScanReverse(&res, arg) || nonzero ? res ^ 31 : 32;
|
||||
#elif __LZCNT__
|
||||
return _lzcnt_u32(arg);
|
||||
#else
|
||||
return arg || nonzero ? __builtin_clz(arg) : 32;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u64 cntlz64(u64 arg, bool nonzero = false)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
ulong res;
|
||||
return _BitScanReverse64(&res, arg) || nonzero ? res ^ 63 : 64;
|
||||
#elif __LZCNT__
|
||||
return _lzcnt_u64(arg);
|
||||
#else
|
||||
return arg || nonzero ? __builtin_clzll(arg) : 64;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u32 cnttz32(u32 arg, bool nonzero = false)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
ulong res;
|
||||
return _BitScanForward(&res, arg) || nonzero ? res : 32;
|
||||
#elif __BMI__
|
||||
return _tzcnt_u32(arg);
|
||||
#else
|
||||
return arg || nonzero ? __builtin_ctz(arg) : 32;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u64 cnttz64(u64 arg, bool nonzero = false)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
ulong res;
|
||||
return _BitScanForward64(&res, arg) || nonzero ? res : 64;
|
||||
#elif __BMI__
|
||||
return _tzcnt_u64(arg);
|
||||
#else
|
||||
return arg || nonzero ? __builtin_ctzll(arg) : 64;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u8 popcnt32(u32 arg)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
const u32 a1 = arg & 0x55555555;
|
||||
const u32 a2 = (arg >> 1) & 0x55555555;
|
||||
const u32 a3 = a1 + a2;
|
||||
const u32 b1 = a3 & 0x33333333;
|
||||
const u32 b2 = (a3 >> 2) & 0x33333333;
|
||||
const u32 b3 = b1 + b2;
|
||||
const u32 c3 = (b3 + (b3 >> 4)) & 0x0f0f0f0f;
|
||||
const u32 d3 = c3 + (c3 >> 8);
|
||||
return static_cast<u8>(d3 + (d3 >> 16));
|
||||
#else
|
||||
return __builtin_popcount(arg);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Rotate helpers
|
||||
#if defined(__GNUG__)
|
||||
|
||||
inline u8 rol8(u8 x, u8 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateleft8)
|
||||
return __builtin_rotateleft8(x, n);
|
||||
#else
|
||||
u8 result = x;
|
||||
__asm__("rolb %[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u8 ror8(u8 x, u8 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateright8)
|
||||
return __builtin_rotateright8(x, n);
|
||||
#else
|
||||
u8 result = x;
|
||||
__asm__("rorb %[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u16 rol16(u16 x, u16 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateleft16)
|
||||
return __builtin_rotateleft16(x, n);
|
||||
#else
|
||||
u16 result = x;
|
||||
__asm__("rolw %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u16 ror16(u16 x, u16 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateright16)
|
||||
return __builtin_rotateright16(x, n);
|
||||
#else
|
||||
u16 result = x;
|
||||
__asm__("rorw %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u32 rol32(u32 x, u32 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateleft32)
|
||||
return __builtin_rotateleft32(x, n);
|
||||
#else
|
||||
u32 result = x;
|
||||
__asm__("roll %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u32 ror32(u32 x, u32 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateright32)
|
||||
return __builtin_rotateright32(x, n);
|
||||
#else
|
||||
u32 result = x;
|
||||
__asm__("rorl %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u64 rol64(u64 x, u64 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateleft64)
|
||||
return __builtin_rotateleft64(x, n);
|
||||
#else
|
||||
u64 result = x;
|
||||
__asm__("rolq %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u64 ror64(u64 x, u64 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateright64)
|
||||
return __builtin_rotateright64(x, n);
|
||||
#else
|
||||
u64 result = x;
|
||||
__asm__("rorq %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr u64 umulh64(u64 a, u64 b)
|
||||
{
|
||||
const __uint128_t x = a;
|
||||
const __uint128_t y = b;
|
||||
return (x * y) >> 64;
|
||||
}
|
||||
|
||||
constexpr s64 mulh64(s64 a, s64 b)
|
||||
{
|
||||
const __int128_t x = a;
|
||||
const __int128_t y = b;
|
||||
return (x * y) >> 64;
|
||||
}
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
inline u8 rol8(u8 x, u8 n)
|
||||
{
|
||||
return _rotl8(x, n);
|
||||
}
|
||||
|
||||
inline u8 ror8(u8 x, u8 n)
|
||||
{
|
||||
return _rotr8(x, n);
|
||||
}
|
||||
|
||||
inline u16 rol16(u16 x, u16 n)
|
||||
{
|
||||
return _rotl16(x, (u8)n);
|
||||
}
|
||||
|
||||
inline u16 ror16(u16 x, u16 n)
|
||||
{
|
||||
return _rotr16(x, (u8)n);
|
||||
}
|
||||
|
||||
inline u32 rol32(u32 x, u32 n)
|
||||
{
|
||||
return _rotl(x, (int)n);
|
||||
}
|
||||
|
||||
inline u32 ror32(u32 x, u32 n)
|
||||
{
|
||||
return _rotr(x, (int)n);
|
||||
}
|
||||
|
||||
inline u64 rol64(u64 x, u64 n)
|
||||
{
|
||||
return _rotl64(x, (int)n);
|
||||
}
|
||||
|
||||
inline u64 ror64(u64 x, u64 n)
|
||||
{
|
||||
return _rotr64(x, (int)n);
|
||||
}
|
||||
|
||||
inline u64 umulh64(u64 x, u64 y)
|
||||
{
|
||||
return __umulh(x, y);
|
||||
}
|
||||
|
||||
inline s64 mulh64(s64 x, s64 y)
|
||||
{
|
||||
return __mulh(x, y);
|
||||
}
|
||||
#endif
|
||||
} // namespace utils
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "bin_patch.h"
|
||||
#include "yaml-cpp/yaml.h"
|
||||
#include <yaml-cpp/yaml.h>
|
||||
#include "File.h"
|
||||
#include "Config.h"
|
||||
|
||||
@@ -86,13 +86,13 @@ void patch_engine::append(const std::string& patch)
|
||||
case patch_type::bef32:
|
||||
case patch_type::lef32:
|
||||
{
|
||||
info.value_as<f32>() = patch[2].as<f32>();
|
||||
info.value = std::bit_cast<u32>(patch[2].as<f32>());
|
||||
break;
|
||||
}
|
||||
case patch_type::bef64:
|
||||
case patch_type::lef64:
|
||||
{
|
||||
info.value_as<f64>() = patch[2].as<f64>();
|
||||
info.value = std::bit_cast<u64>(patch[2].as<f64>());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -28,12 +28,6 @@ class patch_engine
|
||||
patch_type type;
|
||||
u32 offset;
|
||||
u64 value;
|
||||
|
||||
template <typename T>
|
||||
T& value_as()
|
||||
{
|
||||
return *reinterpret_cast<T*>(reinterpret_cast<char*>(&value));
|
||||
}
|
||||
};
|
||||
|
||||
// Database
|
||||
|
||||
+284
-603
File diff suppressed because it is too large
Load Diff
+25
-19
@@ -1,11 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "asm.h"
|
||||
#include <climits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
static const size_t size_dropped = std::numeric_limits<size_t>::max();
|
||||
|
||||
/*
|
||||
C-style format parser. Appends formatted string to `out`, returns number of characters written.
|
||||
`out`: mutable reference to std::string, std::vector<char> or other compatible container
|
||||
@@ -40,7 +43,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
const auto drop_sequence = [&]
|
||||
{
|
||||
out.insert(out.end(), fmt - ctx.size, fmt);
|
||||
ctx.size = -1;
|
||||
ctx.size = size_dropped;
|
||||
};
|
||||
|
||||
const auto read_decimal = [&](uint result) -> uint
|
||||
@@ -56,7 +59,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
|
||||
const auto write_octal = [&](u64 value, u64 min_num)
|
||||
{
|
||||
out.resize(out.size() + std::max<u64>(min_num, 66 / 3 - (cntlz64(value | 1, true) + 2) / 3), '0');
|
||||
out.resize(out.size() + std::max<u64>(min_num, 66 / 3 - (utils::cntlz64(value | 1, true) + 2) / 3), '0');
|
||||
|
||||
// Write in reversed order
|
||||
for (auto i = out.rbegin(); value; i++, value /= 8)
|
||||
@@ -67,8 +70,8 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
|
||||
const auto write_hex = [&](u64 value, bool upper, u64 min_num)
|
||||
{
|
||||
out.resize(out.size() + std::max<u64>(min_num, 64 / 4 - cntlz64(value | 1, true) / 4), '0');
|
||||
|
||||
out.resize(out.size() + std::max<u64>(min_num, 64 / 4 - utils::cntlz64(value | 1, true) / 4), '0');
|
||||
|
||||
// Write in reversed order
|
||||
for (auto i = out.rbegin(); value; i++, value /= 16)
|
||||
{
|
||||
@@ -111,7 +114,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
ctx = {0};
|
||||
out.push_back(ch);
|
||||
}
|
||||
else if (ctx.size == -1)
|
||||
else if (ctx.size == size_dropped)
|
||||
{
|
||||
out.push_back(ch);
|
||||
}
|
||||
@@ -141,7 +144,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
{
|
||||
ctx.width = read_decimal(ch - '0');
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -157,7 +160,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
ctx.width = std::abs(warg);
|
||||
ctx.left |= warg < 0;
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -307,7 +310,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
|
||||
const std::size_t start = out.size();
|
||||
const std::size_t size1 = src.fmt_string(out, ctx.args);
|
||||
|
||||
|
||||
if (ctx.dot && size1 > ctx.prec)
|
||||
{
|
||||
// Shrink if necessary
|
||||
@@ -409,9 +412,10 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
}
|
||||
|
||||
const u64 mask =
|
||||
ctx.type == 1 ? 0xffull :
|
||||
ctx.type == 2 ? 0xffffull :
|
||||
ctx.type == 4 ? 0xffffffffull : 0xffffffffffffffffull;
|
||||
ctx.type == 1 ? 0xff :
|
||||
ctx.type == 2 ? 0xffff :
|
||||
ctx.type == 4 ? 0xffff'ffffu :
|
||||
0xffff'ffff'ffff'ffffu;
|
||||
|
||||
// Trunc sign-extended signed types
|
||||
const u64 val = src.template get<u64>(ctx.args) & mask;
|
||||
@@ -465,9 +469,10 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
}
|
||||
|
||||
const u64 mask =
|
||||
ctx.type == 1 ? 0xffull :
|
||||
ctx.type == 2 ? 0xffffull :
|
||||
ctx.type == 4 ? 0xffffffffull : 0xffffffffffffffffull;
|
||||
ctx.type == 1 ? 0xff :
|
||||
ctx.type == 2 ? 0xffff :
|
||||
ctx.type == 4 ? 0xffff'ffffu :
|
||||
0xffff'ffff'ffff'ffffu;
|
||||
|
||||
// Trunc sign-extended signed types
|
||||
const u64 val = src.template get<u64>(ctx.args) & mask;
|
||||
@@ -528,9 +533,10 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
}
|
||||
|
||||
const u64 mask =
|
||||
ctx.type == 1 ? 0xffull :
|
||||
ctx.type == 2 ? 0xffffull :
|
||||
ctx.type == 4 ? 0xffffffffull : 0xffffffffffffffffull;
|
||||
ctx.type == 1 ? 0xff :
|
||||
ctx.type == 2 ? 0xffff :
|
||||
ctx.type == 4 ? 0xffff'ffffu :
|
||||
0xffff'ffff'ffff'ffffu;
|
||||
|
||||
// Trunc sign-extended signed types
|
||||
const u64 val = src.template get<u64>(ctx.args) & mask;
|
||||
@@ -576,7 +582,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
// Add padding if necessary
|
||||
out.insert(ctx.left ? out.end() : out.begin() + start, ctx.width - size2, ' ');
|
||||
}
|
||||
|
||||
|
||||
src.skip(ctx.args);
|
||||
ctx = {0};
|
||||
break;
|
||||
@@ -625,7 +631,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
}
|
||||
|
||||
// Handle unfinished sequence
|
||||
if (ctx.size && ctx.size != -1)
|
||||
if (ctx.size && ctx.size != size_dropped)
|
||||
{
|
||||
fmt--, drop_sequence();
|
||||
}
|
||||
|
||||
+447
-78
@@ -1,5 +1,6 @@
|
||||
#include "cond.h"
|
||||
#include "sync.h"
|
||||
#include "lockless.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
@@ -9,100 +10,468 @@
|
||||
|
||||
bool cond_variable::imp_wait(u32 _old, u64 _timeout) noexcept
|
||||
{
|
||||
verify(HERE), _old != -1; // Very unlikely: it requires 2^32 distinct threads to wait simultaneously
|
||||
const bool is_inf = _timeout > max_timeout;
|
||||
verify("cond_variable overflow" HERE), (_old & 0xffff) != 0xffff; // Very unlikely: it requires 65535 distinct threads to wait simultaneously
|
||||
|
||||
return balanced_wait_until(m_value, _timeout, [&](u32& value, auto... ret) -> int
|
||||
{
|
||||
if (value >> 16)
|
||||
{
|
||||
// Success
|
||||
value -= 0x10001;
|
||||
return +1;
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(ret))
|
||||
{
|
||||
// Retire
|
||||
value -= 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
#ifdef _WIN32
|
||||
LARGE_INTEGER timeout;
|
||||
timeout.QuadPart = _timeout * -10;
|
||||
|
||||
if (HRESULT rc = NtWaitForKeyedEvent(nullptr, &m_value, false, is_inf ? nullptr : &timeout))
|
||||
if (_old >= 0x10000 && !OptWaitOnAddress && m_value)
|
||||
{
|
||||
verify(HERE), rc == WAIT_TIMEOUT;
|
||||
|
||||
// Retire
|
||||
if (!m_value.fetch_op([](u32& value) { if (value) value--; }))
|
||||
{
|
||||
NtWaitForKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
timespec timeout;
|
||||
timeout.tv_sec = _timeout / 1000000;
|
||||
timeout.tv_nsec = (_timeout % 1000000) * 1000;
|
||||
|
||||
for (u32 value = _old + 1;; value = m_value)
|
||||
{
|
||||
const int err = futex((int*)&m_value.raw(), FUTEX_WAIT_PRIVATE, value, is_inf ? nullptr : &timeout, nullptr, 0) == 0
|
||||
? 0
|
||||
: errno;
|
||||
|
||||
// Normal or timeout wakeup
|
||||
if (!err || (!is_inf && err == ETIMEDOUT))
|
||||
{
|
||||
// Cleanup (remove waiter)
|
||||
verify(HERE), m_value--;
|
||||
return !err;
|
||||
}
|
||||
|
||||
// Not a wakeup
|
||||
verify(HERE), err == EAGAIN;
|
||||
// Workaround possibly stolen signal
|
||||
imp_wake(1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void cond_variable::imp_wake(u32 _count) noexcept
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// Try to subtract required amount of waiters
|
||||
const u32 count = m_value.atomic_op([=](u32& value)
|
||||
// TODO (notify_one)
|
||||
balanced_awaken<true>(m_value, m_value.atomic_op([&](u32& value) -> u32
|
||||
{
|
||||
if (value > _count)
|
||||
// Subtract already signaled number from total amount of waiters
|
||||
const u32 can_sig = (value & 0xffff) - (value >> 16);
|
||||
const u32 num_sig = std::min<u32>(can_sig, _count);
|
||||
|
||||
value += num_sig << 16;
|
||||
return num_sig;
|
||||
}));
|
||||
}
|
||||
|
||||
bool notifier::imp_try_lock(u32 count)
|
||||
{
|
||||
return m_counter.atomic_op([&](u32& value)
|
||||
{
|
||||
if ((value % (max_readers + 1)) + count <= max_readers)
|
||||
{
|
||||
value -= _count;
|
||||
return _count;
|
||||
value += count;
|
||||
return true;
|
||||
}
|
||||
|
||||
return std::exchange(value, 0);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void notifier::imp_unlock(u32 count)
|
||||
{
|
||||
const u32 counter = m_counter.sub_fetch(count);
|
||||
|
||||
if (UNLIKELY(counter % (max_readers + 1)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (counter)
|
||||
{
|
||||
const u32 _old = m_counter.atomic_op([](u32& value) -> u32
|
||||
{
|
||||
if (value % (max_readers + 1))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return std::exchange(value, 0) / (max_readers + 1);
|
||||
});
|
||||
|
||||
const u32 wc = m_cond.m_value;
|
||||
|
||||
if (_old && wc)
|
||||
{
|
||||
m_cond.imp_wake(_old > wc ? wc : _old);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u32 notifier::imp_notify(u32 count)
|
||||
{
|
||||
return m_counter.atomic_op([&](u32& value) -> u32
|
||||
{
|
||||
if (const u32 add = value % (max_readers + 1))
|
||||
{
|
||||
// Mutex is locked
|
||||
const u32 result = add > count ? count : add;
|
||||
value += result * (max_readers + 1);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Mutex is unlocked
|
||||
value = 0;
|
||||
return count;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool notifier::wait(u64 usec_timeout)
|
||||
{
|
||||
const u32 _old = m_cond.m_value.fetch_add(1);
|
||||
|
||||
if (max_readers < m_counter.fetch_op([](u32& value)
|
||||
{
|
||||
if (value > max_readers)
|
||||
{
|
||||
value -= max_readers;
|
||||
}
|
||||
|
||||
value -= 1;
|
||||
}))
|
||||
{
|
||||
// Return without waiting
|
||||
m_cond.imp_wait(_old, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
const bool res = m_cond.imp_wait(_old, usec_timeout);
|
||||
|
||||
while (!try_lock_shared())
|
||||
{
|
||||
// TODO
|
||||
busy_wait();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool unique_cond::imp_wait(u64 _timeout) noexcept
|
||||
{
|
||||
// State transition: c_sig -> c_lock \ c_lock -> c_wait
|
||||
const u32 _old = m_value.fetch_sub(1);
|
||||
if (LIKELY(_old == c_sig))
|
||||
return true;
|
||||
|
||||
return balanced_wait_until(m_value, _timeout, [&](u32& value, auto... ret) -> int
|
||||
{
|
||||
if (value == c_sig)
|
||||
{
|
||||
value = c_lock;
|
||||
return +1;
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(ret))
|
||||
{
|
||||
value = c_lock;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
void unique_cond::imp_notify() noexcept
|
||||
{
|
||||
auto [old, ok] = m_value.fetch_op([](u32& v)
|
||||
{
|
||||
if (UNLIKELY(v > 0 && v < c_sig))
|
||||
{
|
||||
v = c_sig;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
for (u32 i = count; i > 0; i--)
|
||||
verify(HERE), old <= c_sig;
|
||||
|
||||
if (LIKELY(!ok || old == c_lock))
|
||||
{
|
||||
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
for (u32 i = _count; i > 0; std::this_thread::yield())
|
||||
{
|
||||
const u32 value = m_value;
|
||||
|
||||
// Constrain remaining amount with imaginary waiter count
|
||||
if (i > value)
|
||||
{
|
||||
i = value;
|
||||
}
|
||||
|
||||
if (!value || i == 0)
|
||||
{
|
||||
// Nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
if (const int res = futex((int*)&m_value.raw(), FUTEX_WAKE_PRIVATE, i > INT_MAX ? INT_MAX : i, nullptr, nullptr, 0))
|
||||
{
|
||||
verify(HERE), res >= 0 && (u32)res <= i;
|
||||
i -= res;
|
||||
}
|
||||
|
||||
if (!m_value || i == 0)
|
||||
{
|
||||
// Escape
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
balanced_awaken(m_value, 1);
|
||||
}
|
||||
|
||||
bool shared_cond::imp_wait(u32 slot, u64 _timeout) noexcept
|
||||
{
|
||||
if (slot >= 32)
|
||||
{
|
||||
// Invalid argument, assume notified
|
||||
return true;
|
||||
}
|
||||
|
||||
const u64 wait_bit = c_wait << slot;
|
||||
const u64 lock_bit = c_lock << slot;
|
||||
|
||||
// Change state from c_lock to c_wait
|
||||
const u64 old_ = m_cvx32.fetch_op([=](u64& cvx32)
|
||||
{
|
||||
if (cvx32 & wait_bit)
|
||||
{
|
||||
// c_lock -> c_wait
|
||||
cvx32 &= ~(lock_bit & ~wait_bit);
|
||||
}
|
||||
else
|
||||
{
|
||||
// c_sig -> c_lock
|
||||
cvx32 |= lock_bit;
|
||||
}
|
||||
});
|
||||
|
||||
if ((old_ & wait_bit) == 0)
|
||||
{
|
||||
// Already signaled, return without waiting
|
||||
return true;
|
||||
}
|
||||
|
||||
return balanced_wait_until(m_cvx32, _timeout, [&](u64& cvx32, auto... ret) -> int
|
||||
{
|
||||
if ((cvx32 & wait_bit) == 0)
|
||||
{
|
||||
// c_sig -> c_lock
|
||||
cvx32 |= lock_bit;
|
||||
return +1;
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(ret))
|
||||
{
|
||||
// Retire
|
||||
cvx32 |= lock_bit;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
void shared_cond::imp_notify() noexcept
|
||||
{
|
||||
auto [old, ok] = m_cvx32.fetch_op([](u64& cvx32)
|
||||
{
|
||||
if (const u64 sig_mask = cvx32 & 0xffffffff)
|
||||
{
|
||||
cvx32 &= 0xffffffffull << 32;
|
||||
cvx32 |= sig_mask << 32;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// Determine if some waiters need a syscall notification
|
||||
const u64 wait_mask = old & (~old >> 32);
|
||||
|
||||
if (UNLIKELY(!ok || !wait_mask))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
balanced_awaken<true>(m_cvx32, utils::popcnt32(wait_mask));
|
||||
}
|
||||
|
||||
void shared_cond::wait_all() noexcept
|
||||
{
|
||||
// Try to acquire waiting state without locking but only if there are other locks
|
||||
const auto [old_, result] = m_cvx32.fetch_op([](u64& cvx32) -> u64
|
||||
{
|
||||
// Check waiting alone
|
||||
if ((cvx32 & 0xffffffff) == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Combine used bits and invert to find least significant bit unused
|
||||
const u32 slot = utils::cnttz64(~((cvx32 & 0xffffffff) | (cvx32 >> 32)), true);
|
||||
|
||||
// Set waiting bit (does nothing if all slots are used)
|
||||
cvx32 |= (1ull << slot) & 0xffffffff;
|
||||
return 1ull << slot;
|
||||
});
|
||||
|
||||
if (!result)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (result > 0xffffffffu)
|
||||
{
|
||||
// All slots are used, fallback to spin wait
|
||||
while (m_cvx32 & 0xffffffff)
|
||||
{
|
||||
busy_wait();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const u64 wait_bit = result;
|
||||
const u64 lock_bit = wait_bit | (wait_bit << 32);
|
||||
|
||||
balanced_wait_until(m_cvx32, -1, [&](u64& cvx32, auto... ret) -> int
|
||||
{
|
||||
if ((cvx32 & wait_bit) == 0)
|
||||
{
|
||||
// Remove signal and unlock at once
|
||||
cvx32 &= ~lock_bit;
|
||||
return +1;
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(ret))
|
||||
{
|
||||
cvx32 &= ~lock_bit;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
bool shared_cond::wait_all(shared_cond::shared_lock& lock) noexcept
|
||||
{
|
||||
AUDIT(lock.m_this == this);
|
||||
|
||||
if (lock.m_slot >= 32)
|
||||
{
|
||||
// Invalid argument, assume notified
|
||||
return true;
|
||||
}
|
||||
|
||||
const u64 wait_bit = c_wait << lock.m_slot;
|
||||
const u64 lock_bit = c_lock << lock.m_slot;
|
||||
|
||||
// Try to acquire waiting state only if there are other locks
|
||||
const auto [old_, not_alone] = m_cvx32.fetch_op([&](u64& cvx32)
|
||||
{
|
||||
// Check locking alone
|
||||
if (((cvx32 >> 32) & cvx32) == (lock_bit >> 32))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// c_lock -> c_wait, c_sig -> unlock
|
||||
cvx32 &= ~(lock_bit & ~wait_bit);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!not_alone)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set invalid slot to acknowledge unlocking
|
||||
lock.m_slot = 33;
|
||||
}
|
||||
|
||||
if ((old_ & wait_bit) == 0)
|
||||
{
|
||||
// Already signaled, return without waiting
|
||||
return true;
|
||||
}
|
||||
|
||||
balanced_wait_until(m_cvx32, -1, [&](u64& cvx32, auto... ret) -> int
|
||||
{
|
||||
if ((cvx32 & wait_bit) == 0)
|
||||
{
|
||||
// Remove signal and unlock at once
|
||||
cvx32 &= ~lock_bit;
|
||||
return +1;
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(ret))
|
||||
{
|
||||
cvx32 &= ~lock_bit;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool shared_cond::notify_all(shared_cond::shared_lock& lock) noexcept
|
||||
{
|
||||
AUDIT(lock.m_this == this);
|
||||
|
||||
if (lock.m_slot >= 32)
|
||||
{
|
||||
// Invalid argument
|
||||
return false;
|
||||
}
|
||||
|
||||
const u64 slot_mask = c_sig << lock.m_slot;
|
||||
|
||||
auto [old, ok] = m_cvx32.fetch_op([&](u64& cvx32)
|
||||
{
|
||||
if (((cvx32 << 32) & cvx32) != slot_mask)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (const u64 sig_mask = cvx32 & 0xffffffff)
|
||||
{
|
||||
cvx32 &= (0xffffffffull << 32) & ~slot_mask;
|
||||
cvx32 |= (sig_mask << 32) & ~slot_mask;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
// Not an exclusive reader
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set invalid slot to acknowledge unlocking
|
||||
lock.m_slot = 34;
|
||||
|
||||
// Determine if some waiters need a syscall notification
|
||||
const u64 wait_mask = old & (~old >> 32);
|
||||
|
||||
if (UNLIKELY(!wait_mask))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
balanced_awaken<true>(m_cvx32, utils::popcnt32(wait_mask));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lf_queue_base::wait(u64 _timeout)
|
||||
{
|
||||
auto _old = m_head.compare_and_swap(0, 1);
|
||||
|
||||
if (_old)
|
||||
{
|
||||
verify("lf_queue concurrent wait" HERE), _old != 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
return balanced_wait_until(m_head, _timeout, [](std::uintptr_t& head, auto... ret) -> int
|
||||
{
|
||||
if (head != 1)
|
||||
{
|
||||
return +1;
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(ret))
|
||||
{
|
||||
head = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
void lf_queue_base::imp_notify()
|
||||
{
|
||||
balanced_awaken(m_head, 1);
|
||||
}
|
||||
|
||||
+235
-3
@@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Atomic.h"
|
||||
#include "util/atomic.hpp"
|
||||
#include <shared_mutex>
|
||||
#include "asm.h"
|
||||
|
||||
// Lightweight condition variable
|
||||
class cond_variable
|
||||
@@ -9,6 +11,8 @@ class cond_variable
|
||||
// Internal waiter counter
|
||||
atomic_t<u32> m_value{0};
|
||||
|
||||
friend class notifier;
|
||||
|
||||
protected:
|
||||
// Internal waiting function
|
||||
bool imp_wait(u32 _old, u64 _timeout) noexcept;
|
||||
@@ -21,7 +25,7 @@ public:
|
||||
|
||||
// Intrusive wait algorithm for lockable objects
|
||||
template <typename T>
|
||||
explicit_bool_t wait(T& object, u64 usec_timeout = -1)
|
||||
bool wait(T& object, u64 usec_timeout = -1)
|
||||
{
|
||||
const u32 _old = m_value.fetch_add(1); // Increment waiter counter
|
||||
object.unlock();
|
||||
@@ -30,6 +34,15 @@ public:
|
||||
return res;
|
||||
}
|
||||
|
||||
// Unlock all specified objects but don't lock them again
|
||||
template <typename... Locks>
|
||||
bool wait_unlock(u64 usec_timeout, Locks&&... locks)
|
||||
{
|
||||
const u32 _old = m_value.fetch_add(1); // Increment waiter counter
|
||||
(..., std::forward<Locks>(locks).unlock());
|
||||
return imp_wait(_old, usec_timeout);
|
||||
}
|
||||
|
||||
// Wake one thread
|
||||
void notify_one() noexcept
|
||||
{
|
||||
@@ -44,9 +57,228 @@ public:
|
||||
{
|
||||
if (m_value)
|
||||
{
|
||||
imp_wake(-1);
|
||||
imp_wake(65535);
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr u64 max_timeout = u64{UINT32_MAX} / 1000 * 1000000;
|
||||
};
|
||||
|
||||
// Pair of a fake shared mutex (only limited shared locking) and a condition variable. Obsolete.
|
||||
class notifier
|
||||
{
|
||||
atomic_t<u32> m_counter{0};
|
||||
cond_variable m_cond;
|
||||
|
||||
bool imp_try_lock(u32 count);
|
||||
|
||||
void imp_unlock(u32 count);
|
||||
|
||||
u32 imp_notify(u32 count);
|
||||
|
||||
public:
|
||||
constexpr notifier() = default;
|
||||
|
||||
bool try_lock()
|
||||
{
|
||||
return imp_try_lock(max_readers);
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
imp_unlock(max_readers);
|
||||
}
|
||||
|
||||
bool try_lock_shared()
|
||||
{
|
||||
return imp_try_lock(1);
|
||||
}
|
||||
|
||||
void unlock_shared()
|
||||
{
|
||||
imp_unlock(1);
|
||||
}
|
||||
|
||||
bool wait(u64 usec_timeout = -1);
|
||||
|
||||
void notify_all()
|
||||
{
|
||||
if (m_counter)
|
||||
{
|
||||
imp_notify(-1);
|
||||
}
|
||||
|
||||
// Notify after imaginary "exclusive" lock+unlock
|
||||
m_cond.notify_all();
|
||||
}
|
||||
|
||||
void notify_one()
|
||||
{
|
||||
// TODO
|
||||
if (m_counter)
|
||||
{
|
||||
if (imp_notify(1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_cond.notify_one();
|
||||
}
|
||||
|
||||
static constexpr u32 max_readers = 0x7f;
|
||||
};
|
||||
|
||||
// Condition variable fused with a pseudo-mutex which is never supposed to be locked concurrently.
|
||||
class unique_cond
|
||||
{
|
||||
enum : u32
|
||||
{
|
||||
c_wait = 1,
|
||||
c_lock = 2,
|
||||
c_sig = 3,
|
||||
};
|
||||
|
||||
atomic_t<u32> m_value{0};
|
||||
|
||||
bool imp_wait(u64 _timeout) noexcept;
|
||||
void imp_notify() noexcept;
|
||||
|
||||
public:
|
||||
constexpr unique_cond() = default;
|
||||
|
||||
void lock() noexcept
|
||||
{
|
||||
// Shouldn't be locked by more than one thread concurrently
|
||||
while (UNLIKELY(!m_value.compare_and_swap_test(0, c_lock)))
|
||||
;
|
||||
}
|
||||
|
||||
void unlock() noexcept
|
||||
{
|
||||
m_value = 0;
|
||||
}
|
||||
|
||||
bool wait(std::unique_lock<unique_cond>& lock, u64 usec_timeout = -1) noexcept
|
||||
{
|
||||
AUDIT(lock.owns_lock());
|
||||
AUDIT(lock.mutex() == this);
|
||||
return imp_wait(usec_timeout);
|
||||
}
|
||||
|
||||
void notify() noexcept
|
||||
{
|
||||
// Early exit if notification is not required
|
||||
if (LIKELY(!m_value))
|
||||
return;
|
||||
|
||||
imp_notify();
|
||||
}
|
||||
};
|
||||
|
||||
// Condition variable fused with a pseudo-mutex supporting only reader locks (up to 32 readers).
|
||||
class shared_cond
|
||||
{
|
||||
// For information, shouldn't modify
|
||||
enum : u64
|
||||
{
|
||||
// Wait bit is aligned for compatibility with 32-bit futex.
|
||||
c_wait = 1,
|
||||
c_sig = 1ull << 32,
|
||||
c_lock = 1ull << 32 | 1,
|
||||
};
|
||||
|
||||
// Split in 32-bit parts for convenient bit combining
|
||||
atomic_t<u64> m_cvx32{0};
|
||||
|
||||
class shared_lock
|
||||
{
|
||||
shared_cond* m_this;
|
||||
u32 m_slot;
|
||||
|
||||
friend class shared_cond;
|
||||
|
||||
public:
|
||||
shared_lock(shared_cond* _this) noexcept
|
||||
: m_this(_this)
|
||||
{
|
||||
// Lock and remember obtained slot index
|
||||
m_slot = m_this->m_cvx32.atomic_op([](u64& cvx32)
|
||||
{
|
||||
// Combine used bits and invert to find least significant bit unused
|
||||
const u32 slot = static_cast<u32>(utils::cnttz64(~((cvx32 & 0xffffffff) | (cvx32 >> 32)), true));
|
||||
|
||||
// Set lock bits (does nothing if all slots are used)
|
||||
const u64 bit = (1ull << slot) & 0xffffffff;
|
||||
cvx32 |= bit | (bit << 32);
|
||||
return slot;
|
||||
});
|
||||
}
|
||||
|
||||
shared_lock(const shared_lock&) = delete;
|
||||
|
||||
shared_lock(shared_lock&& rhs)
|
||||
: m_this(rhs.m_this)
|
||||
, m_slot(rhs.m_slot)
|
||||
{
|
||||
rhs.m_slot = 32;
|
||||
}
|
||||
|
||||
shared_lock& operator=(const shared_lock&) = delete;
|
||||
|
||||
~shared_lock()
|
||||
{
|
||||
// Clear the slot (does nothing if all slots are used)
|
||||
const u64 bit = (1ull << m_slot) & 0xffffffff;
|
||||
m_this->m_cvx32 &= ~(bit | (bit << 32));
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
// Check success
|
||||
return m_slot < 32;
|
||||
}
|
||||
|
||||
bool wait(u64 usec_timeout = -1) const noexcept
|
||||
{
|
||||
return m_this->wait(*this, usec_timeout);
|
||||
}
|
||||
};
|
||||
|
||||
bool imp_wait(u32 slot, u64 _timeout) noexcept;
|
||||
void imp_notify() noexcept;
|
||||
|
||||
public:
|
||||
constexpr shared_cond() = default;
|
||||
|
||||
shared_lock try_shared_lock() noexcept
|
||||
{
|
||||
return shared_lock(this);
|
||||
}
|
||||
|
||||
u32 count() const noexcept
|
||||
{
|
||||
const u64 cvx32 = m_cvx32;
|
||||
return utils::popcnt32(static_cast<u32>(cvx32 | (cvx32 >> 32)));
|
||||
}
|
||||
|
||||
bool wait(shared_lock const& lock, u64 usec_timeout = -1) noexcept
|
||||
{
|
||||
AUDIT(lock.m_this == this);
|
||||
return imp_wait(lock.m_slot, usec_timeout);
|
||||
}
|
||||
|
||||
void wait_all() noexcept;
|
||||
|
||||
bool wait_all(shared_lock& lock) noexcept;
|
||||
|
||||
void notify_all() noexcept
|
||||
{
|
||||
if (LIKELY(!m_cvx32))
|
||||
return;
|
||||
|
||||
imp_notify();
|
||||
}
|
||||
|
||||
bool notify_all(shared_lock& lock) noexcept;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
|
||||
namespace date_time
|
||||
{
|
||||
static inline tm get_time(time_t* _time)
|
||||
{
|
||||
tm buf;
|
||||
time_t t = time(_time);
|
||||
#ifdef _MSC_VER
|
||||
localtime_s(&buf, &t);
|
||||
#else
|
||||
buf = *localtime(&t);
|
||||
#endif
|
||||
return buf;
|
||||
}
|
||||
|
||||
static inline std::string current_time()
|
||||
{
|
||||
char str[80];
|
||||
tm now = get_time(0);
|
||||
strftime(str, sizeof(str), "%c", &now);
|
||||
return str;
|
||||
}
|
||||
|
||||
template<char separator = 0>
|
||||
static inline std::string current_time_narrow()
|
||||
{
|
||||
char str[80];
|
||||
tm now = get_time(0);
|
||||
|
||||
std::string parse_buf;
|
||||
|
||||
if constexpr(separator != 0)
|
||||
parse_buf = std::string("%Y") + separator + "%m" + separator + "%d" + separator + "%H" + separator + "%M" + separator + "%S";
|
||||
else
|
||||
parse_buf = "%Y%m%d%H%M%S";
|
||||
|
||||
strftime(str, sizeof(str), parse_buf.c_str(), &now);
|
||||
return str;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "dynamic_library.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -50,7 +50,7 @@ namespace utils
|
||||
|
||||
bool dynamic_library::loaded() const
|
||||
{
|
||||
return !m_handle;
|
||||
return m_handle != nullptr;
|
||||
}
|
||||
|
||||
dynamic_library::operator bool() const
|
||||
|
||||
@@ -63,18 +63,30 @@ namespace utils
|
||||
{
|
||||
}
|
||||
|
||||
// Caller
|
||||
R operator()(Args... args)
|
||||
void init()
|
||||
{
|
||||
if (!ptr)
|
||||
{
|
||||
// TODO: atomic
|
||||
ptr = reinterpret_cast<R(*)(Args...)>(get_proc_address(lib, name));
|
||||
}
|
||||
}
|
||||
|
||||
operator bool()
|
||||
{
|
||||
init();
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// Caller
|
||||
R operator()(Args... args)
|
||||
{
|
||||
init();
|
||||
|
||||
return ptr(args...);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#define DYNAMIC_IMPORT(lib, name, ...) static utils::dynamic_import<__VA_ARGS__> name(lib, #name);
|
||||
#define DYNAMIC_IMPORT(lib, name, ...) inline utils::dynamic_import<__VA_ARGS__> name(lib, #name);
|
||||
|
||||
+7
-5
@@ -4,7 +4,7 @@
|
||||
#include <deque>
|
||||
#include <list>
|
||||
|
||||
#include "Atomic.h"
|
||||
#include "util/atomic.hpp"
|
||||
|
||||
template <typename T, T Mod = T::__state_enum_max, typename Under = std::underlying_type_t<T>>
|
||||
T operator ++(T& value, int)
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
}
|
||||
|
||||
// Conditionally set state (optimized)
|
||||
explicit_bool_t state_test_and_set(T expected, T state)
|
||||
bool state_test_and_set(T expected, T state)
|
||||
{
|
||||
if (m_value == expected && m_value.compare_and_swap_test(expected, state))
|
||||
{
|
||||
@@ -83,7 +83,7 @@ public:
|
||||
}
|
||||
|
||||
// Conditionally set state (list version)
|
||||
explicit_bool_t state_test_and_set(std::initializer_list<T> expected, T state)
|
||||
bool state_test_and_set(std::initializer_list<T> expected, T state)
|
||||
{
|
||||
T _old;
|
||||
|
||||
@@ -111,9 +111,10 @@ public:
|
||||
// Unconditionally set next state
|
||||
void state_next()
|
||||
{
|
||||
T _old, state = m_value.op_fetch([&](T& value)
|
||||
T _old, state = m_value.atomic_op([&](T& value)
|
||||
{
|
||||
_old = value++;
|
||||
return value;
|
||||
});
|
||||
|
||||
(static_cast<CRT*>(this)->*transition_get(state))(_old);
|
||||
@@ -122,9 +123,10 @@ public:
|
||||
// Unconditionally set previous state
|
||||
void state_prev()
|
||||
{
|
||||
T _old, state = m_value.op_fetch([&](T& value)
|
||||
T _old, state = m_value.atomic_op([&](T& value)
|
||||
{
|
||||
_old = value--;
|
||||
return value;
|
||||
});
|
||||
|
||||
(static_cast<CRT*>(this)->*transition_get(state))(_old);
|
||||
|
||||
+36
-6
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
|
||||
@@ -673,6 +673,16 @@ struct area_base
|
||||
return{ x1, y1, x2 - x1, y2 - y1 };
|
||||
}
|
||||
|
||||
constexpr T width() const
|
||||
{
|
||||
return (x1 < x2) ? (x2 - x1) : (x1 - x2);
|
||||
}
|
||||
|
||||
constexpr T height() const
|
||||
{
|
||||
return (y1 < y2) ? (y2 - y1) : (y1 - y2);
|
||||
}
|
||||
|
||||
void flip_vertical()
|
||||
{
|
||||
T _y = y1; y1 = y2; y2 = _y;
|
||||
@@ -693,6 +703,11 @@ struct area_base
|
||||
return{ x2, y1, x1, y2 };
|
||||
}
|
||||
|
||||
constexpr bool is_flipped() const
|
||||
{
|
||||
return (x1 > x2 || y1 > y2);
|
||||
}
|
||||
|
||||
constexpr bool operator == (const area_base& rhs) const
|
||||
{
|
||||
return x1 == rhs.x1 && x2 == rhs.x2 && y1 == rhs.y1 && y2 == rhs.y2;
|
||||
@@ -826,7 +841,7 @@ struct color4_base
|
||||
T xyzw[4];
|
||||
};
|
||||
|
||||
color4_base()
|
||||
constexpr color4_base()
|
||||
: x{}
|
||||
, y{}
|
||||
, z{}
|
||||
@@ -834,7 +849,7 @@ struct color4_base
|
||||
{
|
||||
}
|
||||
|
||||
color4_base(T x, T y = {}, T z = {}, T w = {})
|
||||
constexpr color4_base(T x, T y = {}, T z = {}, T w = {})
|
||||
: x(x)
|
||||
, y(y)
|
||||
, z(z)
|
||||
@@ -842,18 +857,18 @@ struct color4_base
|
||||
{
|
||||
}
|
||||
|
||||
bool operator == (const color4_base& rhs) const
|
||||
constexpr bool operator == (const color4_base& rhs) const
|
||||
{
|
||||
return r == rhs.r && g == rhs.g && b == rhs.b && a == rhs.a;
|
||||
}
|
||||
|
||||
bool operator != (const color4_base& rhs) const
|
||||
constexpr bool operator != (const color4_base& rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
template<typename NT>
|
||||
operator color4_base<NT>() const
|
||||
constexpr operator color4_base<NT>() const
|
||||
{
|
||||
return{ (NT)x, (NT)y, (NT)z, (NT)w };
|
||||
}
|
||||
@@ -976,62 +991,77 @@ struct color1_base
|
||||
};
|
||||
|
||||
//specializations
|
||||
using positionu = position_base<unsigned int>;
|
||||
using positioni = position_base<int>;
|
||||
using positionf = position_base<float>;
|
||||
using positiond = position_base<double>;
|
||||
|
||||
using coordu = coord_base<unsigned int>;
|
||||
using coordi = coord_base<int>;
|
||||
using coordf = coord_base<float>;
|
||||
using coordd = coord_base<double>;
|
||||
|
||||
using areau = area_base<unsigned int>;
|
||||
using areai = area_base<int>;
|
||||
using areaf = area_base<float>;
|
||||
using aread = area_base<double>;
|
||||
|
||||
using position1u = position1_base<unsigned int>;
|
||||
using position1i = position1_base<int>;
|
||||
using position1f = position1_base<float>;
|
||||
using position1d = position1_base<double>;
|
||||
|
||||
using position2u = position2_base<unsigned int>;
|
||||
using position2i = position2_base<int>;
|
||||
using position2f = position2_base<float>;
|
||||
using position2d = position2_base<double>;
|
||||
|
||||
using position3u = position3_base<unsigned int>;
|
||||
using position3i = position3_base<int>;
|
||||
using position3f = position3_base<float>;
|
||||
using position3d = position3_base<double>;
|
||||
|
||||
using position4u = position4_base<unsigned int>;
|
||||
using position4i = position4_base<int>;
|
||||
using position4f = position4_base<float>;
|
||||
using position4d = position4_base<double>;
|
||||
|
||||
using size2u = size2_base<unsigned int>;
|
||||
using size2i = size2_base<int>;
|
||||
using size2f = size2_base<float>;
|
||||
using size2d = size2_base<double>;
|
||||
|
||||
using sizeu = size2u;
|
||||
using sizei = size2i;
|
||||
using sizef = size2f;
|
||||
using sized = size2d;
|
||||
|
||||
using size3u = size3_base<unsigned int>;
|
||||
using size3i = size3_base<int>;
|
||||
using size3f = size3_base<float>;
|
||||
using size3d = size3_base<double>;
|
||||
|
||||
using coord3u = coord3_base<unsigned int>;
|
||||
using coord3i = coord3_base<int>;
|
||||
using coord3f = coord3_base<float>;
|
||||
using coord3d = coord3_base<double>;
|
||||
|
||||
using color4u = color4_base<unsigned int>;
|
||||
using color4i = color4_base<int>;
|
||||
using color4f = color4_base<float>;
|
||||
using color4d = color4_base<double>;
|
||||
|
||||
using color3u = color3_base<unsigned int>;
|
||||
using color3i = color3_base<int>;
|
||||
using color3f = color3_base<float>;
|
||||
using color3d = color3_base<double>;
|
||||
|
||||
using color2u = color2_base<unsigned int>;
|
||||
using color2i = color2_base<int>;
|
||||
using color2f = color2_base<float>;
|
||||
using color2d = color2_base<double>;
|
||||
|
||||
using color1u = color1_base<unsigned int>;
|
||||
using color1i = color1_base<int>;
|
||||
using color1f = color1_base<float>;
|
||||
using color1d = color1_base<double>;
|
||||
|
||||
@@ -69,14 +69,14 @@ if defined APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH (
|
||||
rem // Otherwise, GIT_BRANCH=branch
|
||||
set GIT_BRANCH=%APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH%
|
||||
)
|
||||
|
||||
|
||||
rem // Make GIT_VERSION the last commit (shortened); Don't include commit count on non-master builds
|
||||
for /F %%I IN ('call %GIT% rev-parse --short HEAD') do set GIT_VERSION=%%I
|
||||
|
||||
for /F %%I IN ('call %GIT% rev-parse --short^=8 HEAD') do set GIT_VERSION=%%I
|
||||
|
||||
) else (
|
||||
rem // Get last commit (shortened) and concat after commit count in GIT_VERSION
|
||||
for /F %%I IN ('call %GIT% rev-parse --short HEAD') do set GIT_VERSION=%COMMIT_COUNT%-%%I
|
||||
|
||||
for /F %%I IN ('call %GIT% rev-parse --short^=8 HEAD') do set GIT_VERSION=%COMMIT_COUNT%-%%I
|
||||
|
||||
for /F %%I IN ('call %GIT% rev-parse --abbrev-ref HEAD') do set GIT_BRANCH=%%I
|
||||
)
|
||||
|
||||
|
||||
+31
-7
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Utilities/types.h"
|
||||
|
||||
namespace rpcs3
|
||||
{
|
||||
@@ -9,19 +10,42 @@ namespace rpcs3
|
||||
return static_cast<size_t>(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static size_t hash_struct(const T& value)
|
||||
template<typename T, typename U>
|
||||
static size_t hash_struct_base(const T& value)
|
||||
{
|
||||
// FNV 64-bit
|
||||
size_t result = 14695981039346656037ull;
|
||||
const unsigned char *bytes = reinterpret_cast<const unsigned char*>(&value);
|
||||
const U *bits = reinterpret_cast<const U*>(&value);
|
||||
|
||||
for (size_t n = 0; n < sizeof(T); ++n)
|
||||
for (size_t n = 0; n < (sizeof(T) / sizeof(U)); ++n)
|
||||
{
|
||||
result ^= bytes[n];
|
||||
result ^= bits[n];
|
||||
result *= 1099511628211ull;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static size_t hash_struct(const T& value)
|
||||
{
|
||||
static constexpr auto block_sz = sizeof(T);
|
||||
|
||||
if constexpr ((block_sz & 0x7) == 0)
|
||||
{
|
||||
return hash_struct_base<T, u64>(value);
|
||||
}
|
||||
|
||||
if constexpr ((block_sz & 0x3) == 0)
|
||||
{
|
||||
return hash_struct_base<T, u32>(value);
|
||||
}
|
||||
|
||||
if constexpr ((block_sz & 0x1) == 0)
|
||||
{
|
||||
return hash_struct_base<T, u16>(value);
|
||||
}
|
||||
|
||||
return hash_struct_base<T, u8>(value);
|
||||
}
|
||||
}
|
||||
|
||||
+324
-176
@@ -1,14 +1,14 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Atomic.h"
|
||||
#include "util/atomic.hpp"
|
||||
|
||||
//! Simple sizeless array base for concurrent access. Cannot shrink, only growths automatically.
|
||||
//! There is no way to know the current size. The smaller index is, the faster it's accessed.
|
||||
//!
|
||||
//!
|
||||
//! T is the type of elements. Currently, default constructor of T shall be constexpr.
|
||||
//! N is initial element count, available without any memory allocation and only stored contiguously.
|
||||
template<typename T, std::size_t N>
|
||||
template <typename T, std::size_t N>
|
||||
class lf_array
|
||||
{
|
||||
// Data (default-initialized)
|
||||
@@ -68,13 +68,13 @@ public:
|
||||
// Get current "push" position
|
||||
u32 size() const
|
||||
{
|
||||
return reinterpret_cast<const atomic_t<u32>&>(m_ctrl).load(); // Hack
|
||||
return m_ctrl.load().push;
|
||||
}
|
||||
|
||||
|
||||
// Acquire the place for one or more elements.
|
||||
u32 push_begin(u32 count = 1)
|
||||
{
|
||||
return reinterpret_cast<atomic_t<u32>&>(m_ctrl).fetch_add(count); // Hack
|
||||
return std::bit_cast<atomic_t<u64>*>(&m_ctrl)->fetch_add(count); // Hack
|
||||
}
|
||||
|
||||
// Get current "pop" position
|
||||
@@ -130,7 +130,7 @@ public:
|
||||
{
|
||||
return m_default_key_data;
|
||||
}
|
||||
|
||||
|
||||
// Calculate hash and array position
|
||||
for (std::size_t pos = Hash{}(key) % Size;; pos += Size)
|
||||
{
|
||||
@@ -146,185 +146,333 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// Fixed-size single-producer single-consumer queue
|
||||
template <typename T, std::uint32_t N>
|
||||
class lf_spsc
|
||||
// Helper type, linked list element
|
||||
template <typename T>
|
||||
class lf_queue_item final
|
||||
{
|
||||
// If N is a power of 2, m_push/m_pop can safely overflow and the algorithm is simplified
|
||||
static_assert(N && (1u << 31) % N == 0, "lf_spsc<> error: size must be power of 2");
|
||||
lf_queue_item* m_link = nullptr;
|
||||
|
||||
protected:
|
||||
volatile std::uint32_t m_push{0};
|
||||
volatile std::uint32_t m_pop{0};
|
||||
T m_data[N]{};
|
||||
T m_data;
|
||||
|
||||
public:
|
||||
constexpr lf_spsc() = default;
|
||||
template <typename U>
|
||||
friend class lf_queue_iterator;
|
||||
|
||||
// Try to push (producer only)
|
||||
template <typename T2>
|
||||
bool try_push(T2&& data)
|
||||
template <typename U>
|
||||
friend class lf_queue_slice;
|
||||
|
||||
template <typename U>
|
||||
friend class lf_queue;
|
||||
|
||||
constexpr lf_queue_item() = default;
|
||||
|
||||
template <typename... Args>
|
||||
constexpr lf_queue_item(lf_queue_item* link, Args&&... args)
|
||||
: m_link(link)
|
||||
, m_data(std::forward<Args>(args)...)
|
||||
{
|
||||
const std::uint32_t pos = m_push;
|
||||
|
||||
if (pos - m_pop >= N)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_mm_lfence();
|
||||
m_data[pos % N] = std::forward<T2>(data);
|
||||
_mm_sfence();
|
||||
m_push = pos + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try to get push pointer (producer only)
|
||||
operator T*()
|
||||
{
|
||||
const std::uint32_t pos = m_push;
|
||||
|
||||
if (pos - m_pop >= N)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
_mm_lfence();
|
||||
return m_data + (pos % N);
|
||||
}
|
||||
|
||||
// Increment push counter (producer only)
|
||||
void end_push()
|
||||
{
|
||||
const std::uint32_t pos = m_push;
|
||||
|
||||
if (pos - m_pop < N)
|
||||
{
|
||||
_mm_sfence();
|
||||
m_push = pos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe access
|
||||
T& get_push(std::size_t i)
|
||||
{
|
||||
_mm_lfence();
|
||||
return m_data[(m_push + i) % N];
|
||||
}
|
||||
|
||||
// Try to pop (consumer only)
|
||||
template <typename T2>
|
||||
bool try_pop(T2& out)
|
||||
{
|
||||
const std::uint32_t pos = m_pop;
|
||||
|
||||
if (m_push - pos <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_mm_lfence();
|
||||
out = std::move(m_data[pos % N]);
|
||||
_mm_sfence();
|
||||
m_pop = pos + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Increment pop counter (consumer only)
|
||||
void end_pop()
|
||||
{
|
||||
const std::uint32_t pos = m_pop;
|
||||
|
||||
if (m_push - pos > 0)
|
||||
{
|
||||
_mm_sfence();
|
||||
m_pop = pos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Get size (consumer only)
|
||||
std::uint32_t size() const
|
||||
{
|
||||
return m_push - m_pop;
|
||||
}
|
||||
|
||||
// Direct access (consumer only)
|
||||
T& operator [](std::size_t i)
|
||||
{
|
||||
_mm_lfence();
|
||||
return m_data[(m_pop + i) % N];
|
||||
}
|
||||
};
|
||||
|
||||
// Fixed-size multi-producer single-consumer queue
|
||||
template <typename T, std::uint32_t N>
|
||||
class lf_mpsc : lf_spsc<T, N>
|
||||
{
|
||||
protected:
|
||||
using lf_spsc<T, N>::m_push;
|
||||
using lf_spsc<T, N>::m_pop;
|
||||
using lf_spsc<T, N>::m_data;
|
||||
|
||||
enum : std::uint64_t
|
||||
{
|
||||
c_ack = 1ull << 0,
|
||||
c_rel = 1ull << 32,
|
||||
};
|
||||
|
||||
atomic_t<std::uint64_t> m_lock{0};
|
||||
|
||||
void release(std::uint64_t value)
|
||||
{
|
||||
// Push all pending elements at once when possible
|
||||
if (value && value % c_rel == value / c_rel)
|
||||
{
|
||||
_mm_sfence();
|
||||
m_push += value % c_rel;
|
||||
m_lock.compare_and_swap_test(value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr lf_mpsc() = default;
|
||||
lf_queue_item(const lf_queue_item&) = delete;
|
||||
|
||||
// Try to get push pointer
|
||||
operator T*()
|
||||
lf_queue_item& operator=(const lf_queue_item&) = delete;
|
||||
|
||||
~lf_queue_item()
|
||||
{
|
||||
const std::uint64_t old = m_lock.fetch_add(c_ack);
|
||||
const std::uint32_t pos = m_push;
|
||||
|
||||
if (old % N >= N || pos - m_pop >= N - (old % N))
|
||||
for (lf_queue_item* ptr = m_link; ptr;)
|
||||
{
|
||||
release(m_lock.sub_fetch(c_ack));
|
||||
return nullptr;
|
||||
delete std::exchange(ptr, std::exchange(ptr->m_link, nullptr));
|
||||
}
|
||||
|
||||
return m_data + ((pos + old) % N);
|
||||
}
|
||||
|
||||
// Increment push counter (producer only)
|
||||
void end_push()
|
||||
{
|
||||
release(m_lock.add_fetch(c_rel));
|
||||
}
|
||||
|
||||
// Try to push
|
||||
template <typename T2>
|
||||
bool try_push(T2&& data)
|
||||
{
|
||||
if (T* ptr = *this)
|
||||
{
|
||||
*ptr = std::forward<T2>(data);
|
||||
end_push();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enable consumer methods
|
||||
using lf_spsc<T, N>::try_pop;
|
||||
using lf_spsc<T, N>::end_pop;
|
||||
using lf_spsc<T, N>::size;
|
||||
using lf_spsc<T, N>::operator [];
|
||||
};
|
||||
|
||||
// Forward iterator: non-owning pointer to the list element in lf_queue_slice<>
|
||||
template <typename T>
|
||||
class lf_queue_iterator
|
||||
{
|
||||
lf_queue_item<T>* m_ptr = nullptr;
|
||||
|
||||
template <typename U>
|
||||
friend class lf_queue_slice;
|
||||
|
||||
public:
|
||||
constexpr lf_queue_iterator() = default;
|
||||
|
||||
bool operator ==(const lf_queue_iterator& rhs) const
|
||||
{
|
||||
return m_ptr == rhs.m_ptr;
|
||||
}
|
||||
|
||||
bool operator !=(const lf_queue_iterator& rhs) const
|
||||
{
|
||||
return m_ptr != rhs.m_ptr;
|
||||
}
|
||||
|
||||
T& operator *() const
|
||||
{
|
||||
return m_ptr->m_data;
|
||||
}
|
||||
|
||||
T* operator ->() const
|
||||
{
|
||||
return &m_ptr->m_data;
|
||||
}
|
||||
|
||||
lf_queue_iterator& operator ++()
|
||||
{
|
||||
m_ptr = m_ptr->m_link;
|
||||
return *this;
|
||||
}
|
||||
|
||||
lf_queue_iterator operator ++(int)
|
||||
{
|
||||
lf_queue_iterator result;
|
||||
result.m_ptr = m_ptr;
|
||||
m_ptr = m_ptr->m_link;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// Owning pointer to the linked list taken from the lf_queue<>
|
||||
template <typename T>
|
||||
class lf_queue_slice
|
||||
{
|
||||
lf_queue_item<T>* m_head = nullptr;
|
||||
|
||||
template <typename U>
|
||||
friend class lf_queue;
|
||||
|
||||
public:
|
||||
constexpr lf_queue_slice() = default;
|
||||
|
||||
lf_queue_slice(const lf_queue_slice&) = delete;
|
||||
|
||||
lf_queue_slice(lf_queue_slice&& r) noexcept
|
||||
: m_head(r.m_head)
|
||||
{
|
||||
r.m_head = nullptr;
|
||||
}
|
||||
|
||||
lf_queue_slice& operator =(const lf_queue_slice&) = delete;
|
||||
|
||||
lf_queue_slice& operator =(lf_queue_slice&& r) noexcept
|
||||
{
|
||||
if (this != &r)
|
||||
{
|
||||
delete m_head;
|
||||
m_head = r.m_head;
|
||||
r.m_head = nullptr;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
~lf_queue_slice()
|
||||
{
|
||||
delete m_head;
|
||||
}
|
||||
|
||||
T& operator *() const
|
||||
{
|
||||
return m_head->m_data;
|
||||
}
|
||||
|
||||
T* operator ->() const
|
||||
{
|
||||
return &m_head->m_data;
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return m_head != nullptr;
|
||||
}
|
||||
|
||||
T* get() const
|
||||
{
|
||||
return m_head ? &m_head->m_data : nullptr;
|
||||
}
|
||||
|
||||
lf_queue_iterator<T> begin() const
|
||||
{
|
||||
lf_queue_iterator<T> result;
|
||||
result.m_ptr = m_head;
|
||||
return result;
|
||||
}
|
||||
|
||||
lf_queue_iterator<T> end() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
lf_queue_slice& pop_front()
|
||||
{
|
||||
delete std::exchange(m_head, std::exchange(m_head->m_link, nullptr));
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
class lf_queue_base
|
||||
{
|
||||
protected:
|
||||
atomic_t<std::uintptr_t> m_head = 0;
|
||||
|
||||
void imp_notify();
|
||||
|
||||
public:
|
||||
// Wait for new elements pushed, no other thread shall call wait() or pop_all() simultaneously
|
||||
bool wait(u64 usec_timeout = -1);
|
||||
};
|
||||
|
||||
// Linked list-based multi-producer queue (the consumer drains the whole queue at once)
|
||||
template <typename T>
|
||||
class lf_queue : public lf_queue_base
|
||||
{
|
||||
using lf_queue_base::m_head;
|
||||
|
||||
// Extract all elements and reverse element order (FILO to FIFO)
|
||||
lf_queue_item<T>* reverse() noexcept
|
||||
{
|
||||
if (auto* head = m_head.load() ? reinterpret_cast<lf_queue_item<T>*>(m_head.exchange(0)) : nullptr)
|
||||
{
|
||||
if (auto* prev = head->m_link)
|
||||
{
|
||||
head->m_link = nullptr;
|
||||
|
||||
do
|
||||
{
|
||||
auto* pprev = prev->m_link;
|
||||
prev->m_link = head;
|
||||
head = std::exchange(prev, pprev);
|
||||
}
|
||||
while (prev);
|
||||
}
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr lf_queue() = default;
|
||||
|
||||
~lf_queue()
|
||||
{
|
||||
delete reinterpret_cast<lf_queue_item<T>*>(m_head.load());
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void push(Args&&... args)
|
||||
{
|
||||
auto _old = m_head.load();
|
||||
auto* item = new lf_queue_item<T>(_old & 1 ? nullptr : reinterpret_cast<lf_queue_item<T>*>(_old), std::forward<Args>(args)...);
|
||||
|
||||
while (!m_head.compare_exchange(_old, reinterpret_cast<std::uint64_t>(item)))
|
||||
{
|
||||
item->m_link = _old & 1 ? nullptr : reinterpret_cast<lf_queue_item<T>*>(_old);
|
||||
}
|
||||
|
||||
if (_old & 1)
|
||||
{
|
||||
lf_queue_base::imp_notify();
|
||||
}
|
||||
}
|
||||
|
||||
// Withdraw the list, supports range-for loop: for (auto&& x : y.pop_all()) ...
|
||||
lf_queue_slice<T> pop_all()
|
||||
{
|
||||
lf_queue_slice<T> result;
|
||||
result.m_head = reverse();
|
||||
return result;
|
||||
}
|
||||
|
||||
// Apply func(data) to each element, return the total length
|
||||
template <typename F>
|
||||
std::size_t apply(F func)
|
||||
{
|
||||
std::size_t count = 0;
|
||||
|
||||
for (auto slice = pop_all(); slice; slice.pop_front())
|
||||
{
|
||||
std::invoke(func, *slice);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
// Assignable lock-free thread-safe value of any type (memory-inefficient)
|
||||
template <typename T>
|
||||
class lf_value final
|
||||
{
|
||||
atomic_t<lf_value*> m_head;
|
||||
|
||||
T m_data;
|
||||
|
||||
public:
|
||||
template <typename... Args>
|
||||
explicit constexpr lf_value(Args&&... args)
|
||||
: m_head(this)
|
||||
, m_data(std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
~lf_value()
|
||||
{
|
||||
// All values are kept in the queue until the end
|
||||
for (lf_value* ptr = m_head.load(); ptr != this;)
|
||||
{
|
||||
delete std::exchange(ptr, std::exchange(ptr->m_head.raw(), ptr));
|
||||
}
|
||||
}
|
||||
|
||||
// Get current head, allows to inspect old values
|
||||
[[nodiscard]] const lf_value* head() const
|
||||
{
|
||||
return m_head.load();
|
||||
}
|
||||
|
||||
// Inspect the initial (oldest) value
|
||||
[[nodiscard]] const T& first() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
[[nodiscard]] const T& get() const
|
||||
{
|
||||
return m_head.load()->m_data;
|
||||
}
|
||||
|
||||
[[nodiscard]] operator const T&() const
|
||||
{
|
||||
return m_head.load()->m_data;
|
||||
}
|
||||
|
||||
// Construct new value in-place
|
||||
template <typename... Args>
|
||||
const T& assign(Args&&... args)
|
||||
{
|
||||
lf_value* val = new lf_value(std::forward<Args>(args)...);
|
||||
lf_value* old = m_head.load();
|
||||
|
||||
do
|
||||
{
|
||||
val->m_head = old;
|
||||
}
|
||||
while (!m_head.compare_exchange(old, val));
|
||||
|
||||
return val->m_data;
|
||||
}
|
||||
|
||||
// Copy-assign new value
|
||||
const T& operator =(const T& value)
|
||||
{
|
||||
return assign(value);
|
||||
}
|
||||
|
||||
// Move-assign new value
|
||||
const T& operator =(T&& value)
|
||||
{
|
||||
return assign(std::move(value));
|
||||
}
|
||||
};
|
||||
|
||||
+198
-274
@@ -1,334 +1,258 @@
|
||||
#include "mutex.h"
|
||||
#include "sync.h"
|
||||
|
||||
#include <climits>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
// TLS variable for tracking owned mutexes
|
||||
thread_local std::vector<shared_mutex*> g_tls_locks;
|
||||
|
||||
void shared_mutex::imp_lock_shared(s64 _old)
|
||||
void shared_mutex::imp_lock_shared(u32 val)
|
||||
{
|
||||
verify("shared_mutex overflow" HERE), _old <= c_max;
|
||||
verify("shared_mutex underflow" HERE), val < c_err;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
busy_wait();
|
||||
|
||||
const s64 value = m_value.load();
|
||||
|
||||
if (value >= c_min && m_value.compare_and_swap_test(value, value - c_min))
|
||||
if (try_lock_shared())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Acquire writer lock
|
||||
imp_wait(m_value.load());
|
||||
// Acquire writer lock and downgrade
|
||||
const u32 old = m_value.fetch_add(c_one);
|
||||
|
||||
// Convert to reader lock
|
||||
s64 value = m_value.fetch_add(c_one - c_min);
|
||||
|
||||
// Proceed exclusively
|
||||
return;
|
||||
|
||||
if (value != 0)
|
||||
if (old == 0)
|
||||
{
|
||||
imp_unlock(value);
|
||||
}
|
||||
|
||||
// Wait as a reader if necessary
|
||||
if (value + c_one - c_min < 0)
|
||||
{
|
||||
NtWaitForKeyedEvent(nullptr, (int*)&m_value + 1, false, nullptr);
|
||||
}
|
||||
#else
|
||||
// Acquire writer lock
|
||||
imp_wait(0);
|
||||
|
||||
// Convert to reader lock
|
||||
m_value += c_one - c_min;
|
||||
|
||||
// Disabled code
|
||||
while (false)
|
||||
{
|
||||
const s64 value0 = m_value.fetch_op([](s64& value)
|
||||
{
|
||||
if (value >= c_min)
|
||||
{
|
||||
value -= c_min;
|
||||
}
|
||||
});
|
||||
|
||||
if (value0 >= c_min)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Acquire writer lock
|
||||
imp_wait(value0);
|
||||
|
||||
// Convert to reader lock
|
||||
s64 value1 = m_value.fetch_add(c_one - c_min);
|
||||
|
||||
if (value1 != 0)
|
||||
{
|
||||
imp_unlock(value1);
|
||||
}
|
||||
|
||||
value1 += c_one - c_min;
|
||||
|
||||
if (value1 > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait as a reader if necessary
|
||||
while (futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAIT_BITSET_PRIVATE, int(value1 >> 32), nullptr, nullptr, INT_MIN))
|
||||
{
|
||||
value1 = m_value.load();
|
||||
|
||||
if (value1 >= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If blocked by writers, release the reader lock and try again
|
||||
const s64 value2 = m_value.fetch_op([](s64& value)
|
||||
{
|
||||
if (value < 0)
|
||||
{
|
||||
value += c_min;
|
||||
}
|
||||
});
|
||||
|
||||
if (value2 >= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
imp_unlock_shared(value2);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void shared_mutex::imp_unlock_shared(s64 _old)
|
||||
{
|
||||
verify("shared_mutex overflow" HERE), _old + c_min <= c_max;
|
||||
|
||||
// Check reader count, notify the writer if necessary
|
||||
if ((_old + c_min) % c_one == 0)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
#else
|
||||
m_value -= c_sig;
|
||||
|
||||
futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAKE_BITSET_PRIVATE, 1, nullptr, nullptr, u32(c_sig >> 32));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::imp_wait(s64)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (m_value.sub_fetch(c_one))
|
||||
{
|
||||
NtWaitForKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
}
|
||||
#else
|
||||
if (!m_value.sub_fetch(c_one))
|
||||
{
|
||||
// Return immediately if locked
|
||||
lock_downgrade();
|
||||
return;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Load new value, try to acquire c_sig
|
||||
const s64 value = m_value.fetch_op([](s64& value)
|
||||
{
|
||||
if (value <= c_one - c_sig)
|
||||
{
|
||||
value += c_sig;
|
||||
}
|
||||
});
|
||||
|
||||
if (value <= c_one - c_sig)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAIT_BITSET_PRIVATE, int(value >> 32), nullptr, nullptr, u32(c_sig >> 32));
|
||||
}
|
||||
#endif
|
||||
verify("shared_mutex overflow" HERE), (old % c_sig) + c_one < c_sig;
|
||||
imp_wait();
|
||||
lock_downgrade();
|
||||
}
|
||||
|
||||
void shared_mutex::imp_lock(s64 _old)
|
||||
void shared_mutex::imp_unlock_shared(u32 old)
|
||||
{
|
||||
verify("shared_mutex overflow" HERE), _old <= c_max;
|
||||
verify("shared_mutex underflow" HERE), old - 1 < c_err;
|
||||
|
||||
// Check reader count, notify the writer if necessary
|
||||
if ((old - 1) % c_one == 0)
|
||||
{
|
||||
imp_signal();
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::imp_lock_low(u32 val)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), val < c_err;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
busy_wait();
|
||||
|
||||
const s64 value = m_value.load();
|
||||
|
||||
if (value == c_one && m_value.compare_and_swap_test(c_one, 0))
|
||||
if (try_lock_low())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
imp_wait(m_value.load());
|
||||
// Acquire writer lock and downgrade
|
||||
const u32 old = m_value.fetch_add(c_one);
|
||||
|
||||
if (old == 0)
|
||||
{
|
||||
lock_downgrade();
|
||||
return;
|
||||
}
|
||||
|
||||
verify("shared_mutex overflow" HERE), (old % c_sig) + c_one < c_sig;
|
||||
imp_wait();
|
||||
lock_downgrade();
|
||||
}
|
||||
|
||||
void shared_mutex::imp_unlock(s64 _old)
|
||||
void shared_mutex::imp_unlock_low(u32 old)
|
||||
{
|
||||
verify("shared_mutex overflow" HERE), _old + c_one <= c_max;
|
||||
verify("shared_mutex underflow" HERE), old - 1 < c_err;
|
||||
|
||||
// 1) Notify the next writer if necessary
|
||||
// 2) Notify all readers otherwise if necessary
|
||||
#ifdef _WIN32
|
||||
if (_old + c_one <= 0)
|
||||
// Check reader count, notify the writer if necessary
|
||||
if ((old - 1) % c_vip == 0)
|
||||
{
|
||||
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
imp_signal();
|
||||
}
|
||||
else if (s64 count = -_old / c_min * 0)
|
||||
}
|
||||
|
||||
void shared_mutex::imp_lock_vip(u32 val)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), val < c_err;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
// Disabled code
|
||||
while (count--)
|
||||
busy_wait();
|
||||
|
||||
if (try_lock_vip())
|
||||
{
|
||||
NtReleaseKeyedEvent(nullptr, (int*)&m_value + 1, false, nullptr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (_old + c_one <= 0)
|
||||
{
|
||||
m_value -= c_sig;
|
||||
|
||||
futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAKE_BITSET_PRIVATE, 1, nullptr, nullptr, u32(c_sig >> 32));
|
||||
}
|
||||
else if (false)
|
||||
// Acquire writer lock and downgrade
|
||||
const u32 old = m_value.fetch_add(c_one);
|
||||
|
||||
if (old == 0)
|
||||
{
|
||||
// Disabled code
|
||||
futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAKE_BITSET_PRIVATE, INT_MAX, nullptr, nullptr, INT_MIN);
|
||||
lock_downgrade_to_vip();
|
||||
return;
|
||||
}
|
||||
|
||||
verify("shared_mutex overflow" HERE), (old % c_sig) + c_one < c_sig;
|
||||
imp_wait();
|
||||
lock_downgrade_to_vip();
|
||||
}
|
||||
|
||||
void shared_mutex::imp_unlock_vip(u32 old)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), old - 1 < c_err;
|
||||
|
||||
// Check reader count, notify the writer if necessary
|
||||
if ((old - 1) % c_one / c_vip == 0)
|
||||
{
|
||||
imp_signal();
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::imp_wait()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
const auto [old, ok] = m_value.fetch_op([](u32& value)
|
||||
{
|
||||
if (value >= c_sig)
|
||||
{
|
||||
value -= c_sig;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (ok)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
m_value.wait(old);
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::imp_signal()
|
||||
{
|
||||
m_value += c_sig;
|
||||
m_value.notify_one();
|
||||
}
|
||||
|
||||
void shared_mutex::imp_lock(u32 val)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), val < c_err;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
busy_wait();
|
||||
|
||||
if (!m_value && try_lock())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const u32 old = m_value.fetch_add(c_one);
|
||||
|
||||
if (old == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
verify("shared_mutex overflow" HERE), (old % c_sig) + c_one < c_sig;
|
||||
imp_wait();
|
||||
}
|
||||
|
||||
void shared_mutex::imp_unlock(u32 old)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), old - c_one < c_err;
|
||||
|
||||
// 1) Notify the next writer if necessary
|
||||
// 2) Notify all readers otherwise if necessary (currently indistinguishable from writers)
|
||||
if (old - c_one)
|
||||
{
|
||||
imp_signal();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void shared_mutex::imp_lock_upgrade()
|
||||
{
|
||||
// TODO
|
||||
unlock_shared();
|
||||
lock();
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
busy_wait();
|
||||
|
||||
if (try_lock_upgrade())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to writer lock
|
||||
const u32 old = m_value.fetch_add(c_one - 1);
|
||||
|
||||
verify("shared_mutex overflow" HERE), (old % c_sig) + c_one - 1 < c_sig;
|
||||
|
||||
if (old % c_one == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
imp_wait();
|
||||
}
|
||||
|
||||
void shared_mutex::imp_lock_degrade()
|
||||
void shared_mutex::imp_lock_unlock()
|
||||
{
|
||||
// TODO
|
||||
u32 _max = 1;
|
||||
|
||||
for (int i = 0; i < 30; i++)
|
||||
{
|
||||
const u32 val = m_value;
|
||||
|
||||
if (val % c_one == 0 && (val / c_one < _max || val >= c_sig))
|
||||
{
|
||||
// Return if have cought a state where:
|
||||
// 1) Mutex is free
|
||||
// 2) Total number of waiters decreased since last check
|
||||
// 3) Signal bit is set (if used on the platform)
|
||||
return;
|
||||
}
|
||||
|
||||
_max = val / c_one;
|
||||
|
||||
busy_wait(1500);
|
||||
}
|
||||
|
||||
// Lock and unlock
|
||||
if (!m_value.fetch_add(c_one))
|
||||
{
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
imp_wait();
|
||||
unlock();
|
||||
lock_shared();
|
||||
}
|
||||
|
||||
bool shared_mutex::try_lock_shared()
|
||||
bool shared_mutex::downgrade_unique_vip_lock_to_low_or_unlock()
|
||||
{
|
||||
// Conditional decrement
|
||||
return m_value.fetch_op([](s64& value) { if (value >= c_min) value -= c_min; }) >= c_min;
|
||||
}
|
||||
|
||||
bool shared_mutex::try_lock()
|
||||
{
|
||||
// Conditional decrement (TODO: obtain c_sig)
|
||||
return m_value.compare_and_swap_test(c_one, 0);
|
||||
}
|
||||
|
||||
bool shared_mutex::try_lock_upgrade()
|
||||
{
|
||||
// TODO
|
||||
return m_value.compare_and_swap_test(c_one - c_min, 0);
|
||||
}
|
||||
|
||||
bool shared_mutex::try_lock_degrade()
|
||||
{
|
||||
// TODO
|
||||
return m_value.compare_and_swap_test(0, c_one - c_min);
|
||||
}
|
||||
|
||||
safe_reader_lock::safe_reader_lock(shared_mutex& mutex)
|
||||
: m_mutex(mutex)
|
||||
, m_is_owned(false)
|
||||
{
|
||||
if (std::count(g_tls_locks.cbegin(), g_tls_locks.cend(), &m_mutex) == 0)
|
||||
return m_value.atomic_op([](u32& value)
|
||||
{
|
||||
m_is_owned = true;
|
||||
|
||||
if (m_is_owned)
|
||||
if (value % c_one / c_vip == 1)
|
||||
{
|
||||
m_mutex.lock_shared();
|
||||
g_tls_locks.emplace_back(&m_mutex);
|
||||
return;
|
||||
value -= c_vip - 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: order locks
|
||||
}
|
||||
}
|
||||
|
||||
safe_reader_lock::~safe_reader_lock()
|
||||
{
|
||||
if (m_is_owned)
|
||||
{
|
||||
m_mutex.unlock_shared();
|
||||
g_tls_locks.erase(std::remove(g_tls_locks.begin(), g_tls_locks.end(), &m_mutex), g_tls_locks.cend());
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: order locks
|
||||
}
|
||||
|
||||
safe_writer_lock::safe_writer_lock(shared_mutex& mutex)
|
||||
: m_mutex(mutex)
|
||||
, m_is_owned(false)
|
||||
, m_is_upgraded(false)
|
||||
{
|
||||
if (std::count(g_tls_locks.cbegin(), g_tls_locks.cend(), &m_mutex) == 0)
|
||||
{
|
||||
m_is_owned = true;
|
||||
|
||||
if (m_is_owned)
|
||||
{
|
||||
m_mutex.lock_shared();
|
||||
g_tls_locks.emplace_back(&m_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: order locks
|
||||
}
|
||||
|
||||
if (m_mutex.is_reading())
|
||||
{
|
||||
m_is_upgraded = true;
|
||||
m_mutex.lock_upgrade();
|
||||
}
|
||||
}
|
||||
|
||||
safe_writer_lock::~safe_writer_lock()
|
||||
{
|
||||
if (m_is_upgraded)
|
||||
{
|
||||
m_mutex.lock_degrade();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_is_owned)
|
||||
{
|
||||
m_mutex.unlock();
|
||||
g_tls_locks.erase(std::remove(g_tls_locks.begin(), g_tls_locks.end(), &m_mutex), g_tls_locks.cend());
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: order locks
|
||||
value -= c_vip;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
+211
-113
@@ -1,65 +1,171 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include "types.h"
|
||||
#include "Atomic.h"
|
||||
#include "util/atomic.hpp"
|
||||
|
||||
// Shared mutex.
|
||||
// Shared mutex with small size (u32).
|
||||
class shared_mutex final
|
||||
{
|
||||
enum : s64
|
||||
enum : u32
|
||||
{
|
||||
c_one = 1ull << 31, // Fixed-point 1.0 value (one writer)
|
||||
c_min = 0x00000001, // Fixed-point 1.0/max_readers value
|
||||
c_sig = 1ull << 62,
|
||||
c_max = c_one
|
||||
c_one = 1u << 14, // Fixed-point 1.0 value (one writer, max_readers = c_one - 1)
|
||||
c_sig = 1u << 30,
|
||||
c_err = 1u << 31,
|
||||
c_vip = 1u << 7,
|
||||
};
|
||||
|
||||
atomic_t<s64> m_value{c_one}; // Semaphore-alike counter
|
||||
|
||||
void imp_lock_shared(s64 _old);
|
||||
void imp_unlock_shared(s64 _old);
|
||||
void imp_wait(s64 _old);
|
||||
void imp_lock(s64 _old);
|
||||
void imp_unlock(s64 _old);
|
||||
atomic_t<u32> m_value{};
|
||||
|
||||
void imp_lock_shared(u32 val);
|
||||
void imp_unlock_shared(u32 old);
|
||||
void imp_lock_low(u32 val);
|
||||
void imp_unlock_low(u32 old);
|
||||
void imp_lock_vip(u32 val);
|
||||
void imp_unlock_vip(u32 old);
|
||||
void imp_wait();
|
||||
void imp_signal();
|
||||
void imp_lock(u32 val);
|
||||
void imp_unlock(u32 old);
|
||||
void imp_lock_upgrade();
|
||||
void imp_lock_degrade();
|
||||
void imp_lock_unlock();
|
||||
|
||||
public:
|
||||
constexpr shared_mutex() = default;
|
||||
|
||||
bool try_lock_shared();
|
||||
bool try_lock_shared()
|
||||
{
|
||||
const u32 value = m_value.load();
|
||||
|
||||
// Conditional increment
|
||||
return value < c_one - 1 && m_value.compare_and_swap_test(value, value + 1);
|
||||
}
|
||||
|
||||
void lock_shared()
|
||||
{
|
||||
const s64 value = m_value.load();
|
||||
const u32 value = m_value.load();
|
||||
|
||||
// Fast path: decrement if positive
|
||||
if (UNLIKELY(value < c_min || value > c_one || !m_value.compare_and_swap_test(value, value - c_min)))
|
||||
if (UNLIKELY(value >= c_one - 1 || !m_value.compare_and_swap_test(value, value + 1)))
|
||||
{
|
||||
imp_lock_shared(value);
|
||||
}
|
||||
}
|
||||
|
||||
void lock_shared_hle()
|
||||
{
|
||||
const u32 value = m_value.load();
|
||||
|
||||
if (LIKELY(value < c_one - 1))
|
||||
{
|
||||
u32 old = value;
|
||||
if (LIKELY(atomic_storage<u32>::compare_exchange_hle_acq(m_value.raw(), old, value + 1)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
imp_lock_shared(value);
|
||||
}
|
||||
|
||||
void unlock_shared()
|
||||
{
|
||||
// Unconditional increment
|
||||
const s64 value = m_value.fetch_add(c_min);
|
||||
// Unconditional decrement (can result in broken state)
|
||||
const u32 value = m_value.fetch_sub(1);
|
||||
|
||||
if (value < 0 || value > c_one - c_min)
|
||||
if (UNLIKELY(value >= c_one))
|
||||
{
|
||||
imp_unlock_shared(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock();
|
||||
void unlock_shared_hle()
|
||||
{
|
||||
const u32 value = atomic_storage<u32>::fetch_add_hle_rel(m_value.raw(), -1);
|
||||
|
||||
if (UNLIKELY(value >= c_one))
|
||||
{
|
||||
imp_unlock_shared(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock_low()
|
||||
{
|
||||
const u32 value = m_value.load();
|
||||
|
||||
// Conditional increment
|
||||
return value < c_vip - 1 && m_value.compare_and_swap_test(value, value + 1);
|
||||
}
|
||||
|
||||
void lock_low()
|
||||
{
|
||||
const u32 value = m_value.load();
|
||||
|
||||
if (UNLIKELY(value >= c_vip - 1 || !m_value.compare_and_swap_test(value, value + 1)))
|
||||
{
|
||||
imp_lock_low(value);
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_low()
|
||||
{
|
||||
// Unconditional decrement (can result in broken state)
|
||||
const u32 value = m_value.fetch_sub(1);
|
||||
|
||||
if (UNLIKELY(value >= c_one))
|
||||
{
|
||||
imp_unlock_low(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock_vip()
|
||||
{
|
||||
const u32 value = m_value.load();
|
||||
|
||||
// Conditional increment
|
||||
return value < c_one - 1 && (value % c_vip) == 0 && m_value.compare_and_swap_test(value, value + c_vip);
|
||||
}
|
||||
|
||||
void lock_vip()
|
||||
{
|
||||
const u32 value = m_value.load();
|
||||
|
||||
if (UNLIKELY(value >= c_one - 1 || (value % c_vip) || !m_value.compare_and_swap_test(value, value + c_vip)))
|
||||
{
|
||||
imp_lock_vip(value);
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_vip()
|
||||
{
|
||||
// Unconditional decrement (can result in broken state)
|
||||
const u32 value = m_value.fetch_sub(c_vip);
|
||||
|
||||
if (UNLIKELY(value >= c_one))
|
||||
{
|
||||
imp_unlock_vip(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock()
|
||||
{
|
||||
return m_value.compare_and_swap_test(0, c_one);
|
||||
}
|
||||
|
||||
void lock()
|
||||
{
|
||||
// Try to lock
|
||||
const s64 value = m_value.compare_and_swap(c_one, 0);
|
||||
const u32 value = m_value.compare_and_swap(0, c_one);
|
||||
|
||||
if (value != c_one)
|
||||
if (UNLIKELY(value))
|
||||
{
|
||||
imp_lock(value);
|
||||
}
|
||||
}
|
||||
|
||||
void lock_hle()
|
||||
{
|
||||
u32 value = 0;
|
||||
|
||||
if (UNLIKELY(!atomic_storage<u32>::compare_exchange_hle_acq(m_value.raw(), value, c_one)))
|
||||
{
|
||||
imp_lock(value);
|
||||
}
|
||||
@@ -67,69 +173,96 @@ public:
|
||||
|
||||
void unlock()
|
||||
{
|
||||
// Unconditional increment
|
||||
const s64 value = m_value.fetch_add(c_one);
|
||||
// Unconditional decrement (can result in broken state)
|
||||
const u32 value = m_value.fetch_sub(c_one);
|
||||
|
||||
if (value != 0)
|
||||
if (UNLIKELY(value != c_one))
|
||||
{
|
||||
imp_unlock(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock_upgrade();
|
||||
void unlock_hle()
|
||||
{
|
||||
const u32 value = atomic_storage<u32>::fetch_add_hle_rel(m_value.raw(), 0u - c_one);
|
||||
|
||||
if (UNLIKELY(value != c_one))
|
||||
{
|
||||
imp_unlock(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock_upgrade()
|
||||
{
|
||||
const u32 value = m_value.load();
|
||||
|
||||
// Conditional increment, try to convert a single reader into a writer, ignoring other writers
|
||||
return (value + c_one - 1) % c_one == 0 && m_value.compare_and_swap_test(value, value + c_one - 1);
|
||||
}
|
||||
|
||||
void lock_upgrade()
|
||||
{
|
||||
if (!m_value.compare_and_swap_test(c_one - c_min, 0))
|
||||
if (UNLIKELY(!try_lock_upgrade()))
|
||||
{
|
||||
imp_lock_upgrade();
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock_degrade();
|
||||
|
||||
void lock_degrade()
|
||||
void lock_downgrade()
|
||||
{
|
||||
if (!m_value.compare_and_swap_test(0, c_one - c_min))
|
||||
// Convert to reader lock (can result in broken state)
|
||||
m_value -= c_one - 1;
|
||||
}
|
||||
|
||||
void lock_downgrade_to_vip()
|
||||
{
|
||||
// Convert to vip lock (can result in broken state)
|
||||
m_value -= c_one - c_vip;
|
||||
}
|
||||
|
||||
// Optimized wait for lockability without locking, relaxed
|
||||
void lock_unlock()
|
||||
{
|
||||
if (UNLIKELY(m_value != 0))
|
||||
{
|
||||
imp_lock_degrade();
|
||||
imp_lock_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
bool is_reading() const
|
||||
// Check whether can immediately obtain an exclusive (writer) lock
|
||||
bool is_free() const
|
||||
{
|
||||
return (m_value.load() % c_one) != 0;
|
||||
return m_value.load() == 0;
|
||||
}
|
||||
|
||||
// Check whether can immediately obtain a shared (reader) lock
|
||||
bool is_lockable() const
|
||||
{
|
||||
return m_value.load() < c_one - 1;
|
||||
}
|
||||
|
||||
// Special purpose logic
|
||||
bool downgrade_unique_vip_lock_to_low_or_unlock();
|
||||
};
|
||||
|
||||
// Simplified shared (reader) lock implementation.
|
||||
// Simplified shared (reader) lock implementation. Mutually incompatible with low_lock and vip_lock.
|
||||
class reader_lock final
|
||||
{
|
||||
shared_mutex& m_mutex;
|
||||
bool m_upgraded = false;
|
||||
|
||||
void lock()
|
||||
{
|
||||
m_upgraded ? m_mutex.lock() : m_mutex.lock_shared();
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
m_upgraded ? m_mutex.unlock() : m_mutex.unlock_shared();
|
||||
}
|
||||
|
||||
friend class cond_variable;
|
||||
|
||||
public:
|
||||
reader_lock(const reader_lock&) = delete;
|
||||
|
||||
reader_lock& operator=(const reader_lock&) = delete;
|
||||
|
||||
explicit reader_lock(shared_mutex& mutex)
|
||||
: m_mutex(mutex)
|
||||
{
|
||||
lock();
|
||||
m_mutex.lock_shared();
|
||||
}
|
||||
|
||||
// One-way lock upgrade
|
||||
// One-way lock upgrade; note that the observed state could have been changed
|
||||
void upgrade()
|
||||
{
|
||||
if (!m_upgraded)
|
||||
@@ -139,93 +272,58 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Try to upgrade; if it succeeds, the observed state has NOT been changed
|
||||
bool try_upgrade()
|
||||
{
|
||||
return m_upgraded || (m_upgraded = m_mutex.try_lock_upgrade());
|
||||
}
|
||||
|
||||
~reader_lock()
|
||||
{
|
||||
unlock();
|
||||
m_upgraded ? m_mutex.unlock() : m_mutex.unlock_shared();
|
||||
}
|
||||
};
|
||||
|
||||
// Simplified exclusive (writer) lock implementation.
|
||||
class writer_lock final
|
||||
// Special shared (reader) lock, mutually exclusive with vip locks. Mutually incompatible with normal shared (reader) lock.
|
||||
class low_lock final
|
||||
{
|
||||
shared_mutex& m_mutex;
|
||||
|
||||
void lock()
|
||||
{
|
||||
m_mutex.lock();
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
||||
friend class cond_variable;
|
||||
|
||||
public:
|
||||
writer_lock(const writer_lock&) = delete;
|
||||
low_lock(const low_lock&) = delete;
|
||||
|
||||
explicit writer_lock(shared_mutex& mutex)
|
||||
low_lock& operator=(const low_lock&) = delete;
|
||||
|
||||
explicit low_lock(shared_mutex& mutex)
|
||||
: m_mutex(mutex)
|
||||
{
|
||||
lock();
|
||||
m_mutex.lock_low();
|
||||
}
|
||||
|
||||
~writer_lock()
|
||||
~low_lock()
|
||||
{
|
||||
unlock();
|
||||
m_mutex.unlock_low();
|
||||
}
|
||||
};
|
||||
|
||||
// Safe reader lock. Can be recursive above other safe locks (reader or writer).
|
||||
class safe_reader_lock final
|
||||
// Special shared (reader) lock, mutually exclusive with low locks. Mutually incompatible with normal shared (reader) lock.
|
||||
class vip_lock final
|
||||
{
|
||||
shared_mutex& m_mutex;
|
||||
bool m_is_owned;
|
||||
|
||||
void lock()
|
||||
{
|
||||
m_mutex.lock_shared();
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
m_mutex.unlock_shared();
|
||||
}
|
||||
|
||||
friend class cond_variable;
|
||||
|
||||
public:
|
||||
safe_reader_lock(const safe_reader_lock&) = delete;
|
||||
vip_lock(const vip_lock&) = delete;
|
||||
|
||||
explicit safe_reader_lock(shared_mutex& mutex);
|
||||
vip_lock& operator=(const vip_lock&) = delete;
|
||||
|
||||
~safe_reader_lock();
|
||||
};
|
||||
|
||||
// Safe writer lock. Can be recursive above other safe locks. Performs upgrade and degrade operations above existing reader lock if necessary.
|
||||
class safe_writer_lock final
|
||||
{
|
||||
shared_mutex& m_mutex;
|
||||
bool m_is_owned;
|
||||
bool m_is_upgraded;
|
||||
|
||||
void lock()
|
||||
explicit vip_lock(shared_mutex& mutex)
|
||||
: m_mutex(mutex)
|
||||
{
|
||||
m_mutex.lock();
|
||||
m_mutex.lock_vip();
|
||||
}
|
||||
|
||||
void unlock()
|
||||
~vip_lock()
|
||||
{
|
||||
m_mutex.unlock();
|
||||
m_mutex.unlock_vip();
|
||||
}
|
||||
|
||||
friend class cond_variable;
|
||||
|
||||
public:
|
||||
safe_writer_lock(const safe_writer_lock&) = delete;
|
||||
|
||||
explicit safe_writer_lock(shared_mutex& mutex);
|
||||
|
||||
~safe_writer_lock();
|
||||
};
|
||||
|
||||
+1
-5
@@ -1,10 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef PUGIXML_HEADER_ONLY
|
||||
#define PUGIXML_HEADER_ONLY 1
|
||||
#endif // !PUGIXML_HEADER_ONLY
|
||||
#include "pugixml.hpp"
|
||||
#undef PUGIXML_HEADER_ONLY
|
||||
#include <pugixml.hpp>
|
||||
|
||||
struct rXmlNode
|
||||
{
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
#ifndef MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP
|
||||
#define MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP
|
||||
|
||||
// Based on variant/recursive_wrapper.hpp from boost.
|
||||
//
|
||||
// Original license:
|
||||
//
|
||||
// Copyright (c) 2002-2003
|
||||
// Eric Friedman, Itay Maman
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See
|
||||
// accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
|
||||
//namespace mapbox {
|
||||
//namespace util {
|
||||
namespace std {
|
||||
|
||||
template <typename T>
|
||||
class recursive_wrapper
|
||||
{
|
||||
|
||||
T* p_;
|
||||
|
||||
void assign(T const& rhs)
|
||||
{
|
||||
this->get() = rhs;
|
||||
}
|
||||
|
||||
public:
|
||||
using type = T;
|
||||
|
||||
/**
|
||||
* Default constructor default initializes the internally stored value.
|
||||
* For POD types this means nothing is done and the storage is
|
||||
* uninitialized.
|
||||
*
|
||||
* @throws std::bad_alloc if there is insufficient memory for an object
|
||||
* of type T.
|
||||
* @throws any exception thrown by the default constructur of T.
|
||||
*/
|
||||
recursive_wrapper()
|
||||
: p_(new T){}
|
||||
|
||||
~recursive_wrapper() noexcept { delete p_; }
|
||||
|
||||
recursive_wrapper(recursive_wrapper const& operand)
|
||||
: p_(new T(operand.get())) {}
|
||||
|
||||
recursive_wrapper(T const& operand)
|
||||
: p_(new T(operand)) {}
|
||||
|
||||
recursive_wrapper(recursive_wrapper&& operand)
|
||||
: p_(new T(std::move(operand.get()))) {}
|
||||
|
||||
recursive_wrapper(T&& operand)
|
||||
: p_(new T(std::move(operand))) {}
|
||||
|
||||
inline recursive_wrapper& operator=(recursive_wrapper const& rhs)
|
||||
{
|
||||
assign(rhs.get());
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline recursive_wrapper& operator=(T const& rhs)
|
||||
{
|
||||
assign(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void swap(recursive_wrapper& operand) noexcept
|
||||
{
|
||||
T* temp = operand.p_;
|
||||
operand.p_ = p_;
|
||||
p_ = temp;
|
||||
}
|
||||
|
||||
recursive_wrapper& operator=(recursive_wrapper&& rhs) noexcept
|
||||
{
|
||||
swap(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
recursive_wrapper& operator=(T&& rhs)
|
||||
{
|
||||
get() = std::move(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
T& get()
|
||||
{
|
||||
assert(p_);
|
||||
return *get_pointer();
|
||||
}
|
||||
|
||||
T const& get() const
|
||||
{
|
||||
assert(p_);
|
||||
return *get_pointer();
|
||||
}
|
||||
|
||||
T* get_pointer() { return p_; }
|
||||
|
||||
const T* get_pointer() const { return p_; }
|
||||
|
||||
operator T const&() const { return this->get(); }
|
||||
|
||||
operator T&() { return this->get(); }
|
||||
|
||||
}; // class recursive_wrapper
|
||||
|
||||
template <typename T>
|
||||
inline void swap(recursive_wrapper<T>& lhs, recursive_wrapper<T>& rhs) noexcept
|
||||
{
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
}
|
||||
//} // namespace util
|
||||
//} // namespace mapbox
|
||||
|
||||
#endif // MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP
|
||||
+6
-32
@@ -1,5 +1,4 @@
|
||||
#include "sema.h"
|
||||
#include "sync.h"
|
||||
|
||||
void semaphore_base::imp_wait()
|
||||
{
|
||||
@@ -15,18 +14,10 @@ void semaphore_base::imp_wait()
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
const s32 value = m_value.fetch_sub(1);
|
||||
|
||||
if (value <= 0)
|
||||
{
|
||||
NtWaitForKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
}
|
||||
#else
|
||||
while (true)
|
||||
{
|
||||
// Try hard way
|
||||
const s32 value = m_value.op_fetch([](s32& value)
|
||||
const s32 value = m_value.atomic_op([](s32& value)
|
||||
{
|
||||
// Use sign bit to acknowledge waiter presence
|
||||
if (value && value > INT32_MIN)
|
||||
@@ -44,43 +35,26 @@ void semaphore_base::imp_wait()
|
||||
// Set sign bit
|
||||
value = INT32_MIN;
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
|
||||
if (value >= 0)
|
||||
{
|
||||
// Signal other waiter to wake up or to restore sign bit
|
||||
futex(&m_value.raw(), FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0);
|
||||
m_value.notify_one();
|
||||
return;
|
||||
}
|
||||
|
||||
futex(&m_value.raw(), FUTEX_WAIT_PRIVATE, value, nullptr, nullptr, 0);
|
||||
m_value.wait(value);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void semaphore_base::imp_post(s32 _old)
|
||||
{
|
||||
verify("semaphore_base: overflow" HERE), _old < 0;
|
||||
|
||||
#ifdef _WIN32
|
||||
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
#else
|
||||
futex(&m_value.raw(), FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool semaphore_base::try_wait()
|
||||
{
|
||||
// Conditional decrement
|
||||
const s32 value = m_value.fetch_op([](s32& value)
|
||||
{
|
||||
if (value > 0)
|
||||
{
|
||||
value -= 1;
|
||||
}
|
||||
});
|
||||
|
||||
return value > 0;
|
||||
m_value.notify_one();
|
||||
}
|
||||
|
||||
bool semaphore_base::try_post(s32 _max)
|
||||
|
||||
+12
-41
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include "types.h"
|
||||
#include "Atomic.h"
|
||||
#include "util/atomic.hpp"
|
||||
|
||||
// Lightweight semaphore helper class
|
||||
class semaphore_base
|
||||
@@ -13,8 +14,6 @@ class semaphore_base
|
||||
|
||||
void imp_post(s32 _old);
|
||||
|
||||
friend class semaphore_lock;
|
||||
|
||||
protected:
|
||||
explicit constexpr semaphore_base(s32 value)
|
||||
: m_value{value}
|
||||
@@ -33,7 +32,10 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
bool try_wait();
|
||||
bool try_wait()
|
||||
{
|
||||
return m_value.try_dec(0);
|
||||
}
|
||||
|
||||
void post(s32 _max)
|
||||
{
|
||||
@@ -73,36 +75,36 @@ class semaphore final : public semaphore_base
|
||||
public:
|
||||
// Default constructor (recommended)
|
||||
constexpr semaphore()
|
||||
: base{Def}
|
||||
: base(Def)
|
||||
{
|
||||
}
|
||||
|
||||
// Explicit value constructor (not recommended)
|
||||
explicit constexpr semaphore(s32 value)
|
||||
: base{value}
|
||||
: base(value)
|
||||
{
|
||||
}
|
||||
|
||||
// Obtain a semaphore
|
||||
void wait()
|
||||
void lock()
|
||||
{
|
||||
return base::wait();
|
||||
}
|
||||
|
||||
// Try to obtain a semaphore
|
||||
explicit_bool_t try_wait()
|
||||
bool try_lock()
|
||||
{
|
||||
return base::try_wait();
|
||||
}
|
||||
|
||||
// Return a semaphore
|
||||
void post()
|
||||
void unlock()
|
||||
{
|
||||
return base::post(Max);
|
||||
}
|
||||
|
||||
// Try to return a semaphore
|
||||
explicit_bool_t try_post()
|
||||
bool try_unlock()
|
||||
{
|
||||
return base::try_post(Max);
|
||||
}
|
||||
@@ -113,34 +115,3 @@ public:
|
||||
return Max;
|
||||
}
|
||||
};
|
||||
|
||||
class semaphore_lock
|
||||
{
|
||||
semaphore_base& m_base;
|
||||
|
||||
void lock()
|
||||
{
|
||||
m_base.wait();
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
m_base.post(INT32_MAX);
|
||||
}
|
||||
|
||||
friend class cond_variable;
|
||||
|
||||
public:
|
||||
explicit semaphore_lock(const semaphore_lock&) = delete;
|
||||
|
||||
semaphore_lock(semaphore_base& sema)
|
||||
: m_base(sema)
|
||||
{
|
||||
lock();
|
||||
}
|
||||
|
||||
~semaphore_lock()
|
||||
{
|
||||
unlock();
|
||||
}
|
||||
};
|
||||
|
||||
+183
-23
@@ -3,7 +3,7 @@
|
||||
/* For internal use. Don't include. */
|
||||
|
||||
#include "types.h"
|
||||
#include "Atomic.h"
|
||||
#include "util/atomic.hpp"
|
||||
#include "dynamic_library.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#endif
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
@@ -24,10 +25,12 @@
|
||||
#include <unordered_map>
|
||||
|
||||
#ifdef _WIN32
|
||||
DYNAMIC_IMPORT("ntdll.dll", NtSetTimerResolution, NTSTATUS(ULONG DesiredResolution, BOOLEAN SetResolution, PULONG CurrentResolution));
|
||||
DYNAMIC_IMPORT("ntdll.dll", NtWaitForKeyedEvent, NTSTATUS(HANDLE Handle, PVOID Key, BOOLEAN Alertable, PLARGE_INTEGER Timeout));
|
||||
DYNAMIC_IMPORT("ntdll.dll", NtReleaseKeyedEvent, NTSTATUS(HANDLE Handle, PVOID Key, BOOLEAN Alertable, PLARGE_INTEGER Timeout));
|
||||
DYNAMIC_IMPORT("ntdll.dll", NtDelayExecution, NTSTATUS(BOOLEAN Alertable, PLARGE_INTEGER DelayInterval));
|
||||
inline utils::dynamic_import<BOOL(volatile VOID* Address, PVOID CompareAddress, SIZE_T AddressSize, DWORD dwMilliseconds)> OptWaitOnAddress("kernel32.dll", "WaitOnAddress");
|
||||
inline utils::dynamic_import<VOID(PVOID Address)> OptWakeByAddressSingle("kernel32.dll", "WakeByAddressSingle");
|
||||
inline utils::dynamic_import<VOID(PVOID Address)> OptWakeByAddressAll("kernel32.dll", "WakeByAddressAll");
|
||||
#endif
|
||||
|
||||
#ifndef __linux__
|
||||
@@ -46,37 +49,37 @@ enum
|
||||
};
|
||||
#endif
|
||||
|
||||
inline int futex(int* uaddr, int futex_op, int val, const timespec* timeout, int* uaddr2, int val3)
|
||||
inline int futex(volatile void* uaddr, int futex_op, uint val, const timespec* timeout = nullptr, uint mask = 0)
|
||||
{
|
||||
#ifdef __linux__
|
||||
return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr, val3);
|
||||
return syscall(SYS_futex, uaddr, futex_op, static_cast<int>(val), timeout, nullptr, static_cast<int>(mask));
|
||||
#else
|
||||
static struct futex_map
|
||||
static struct futex_manager
|
||||
{
|
||||
struct waiter
|
||||
{
|
||||
int val;
|
||||
uint val;
|
||||
uint mask;
|
||||
std::condition_variable cv;
|
||||
};
|
||||
|
||||
std::mutex mutex;
|
||||
std::unordered_multimap<int*, waiter*, pointer_hash<int>> map;
|
||||
std::unordered_multimap<volatile void*, waiter*, pointer_hash<volatile void, alignof(int)>> map;
|
||||
|
||||
int operator()(int* uaddr, int futex_op, int val, const timespec* timeout, int*, uint val3)
|
||||
int operator()(volatile void* uaddr, int futex_op, uint val, const timespec* timeout, uint mask)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
std::unique_lock lock(mutex);
|
||||
|
||||
switch (futex_op)
|
||||
{
|
||||
case FUTEX_WAIT:
|
||||
case FUTEX_WAIT_PRIVATE:
|
||||
{
|
||||
val3 = -1;
|
||||
// Fallthrough
|
||||
mask = -1;
|
||||
[[fallthrough]];
|
||||
}
|
||||
case FUTEX_WAIT_BITSET:
|
||||
case FUTEX_WAIT_BITSET_PRIVATE:
|
||||
{
|
||||
if (*(volatile int*)uaddr != val)
|
||||
if (*reinterpret_cast<volatile uint*>(uaddr) != val)
|
||||
{
|
||||
errno = EAGAIN;
|
||||
return -1;
|
||||
@@ -84,7 +87,7 @@ inline int futex(int* uaddr, int futex_op, int val, const timespec* timeout, int
|
||||
|
||||
waiter rec;
|
||||
rec.val = val;
|
||||
rec.mask = val3;
|
||||
rec.mask = mask;
|
||||
const auto& ref = *map.emplace(uaddr, &rec);
|
||||
|
||||
int res = 0;
|
||||
@@ -107,25 +110,25 @@ inline int futex(int* uaddr, int futex_op, int val, const timespec* timeout, int
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
|
||||
map.erase(std::find(map.find(uaddr), map.end(), ref));
|
||||
return res;
|
||||
}
|
||||
|
||||
case FUTEX_WAKE:
|
||||
case FUTEX_WAKE_PRIVATE:
|
||||
{
|
||||
val3 = -1;
|
||||
// Fallthrough
|
||||
mask = -1;
|
||||
[[fallthrough]];
|
||||
}
|
||||
case FUTEX_WAKE_BITSET:
|
||||
case FUTEX_WAKE_BITSET_PRIVATE:
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
for (auto range = map.equal_range(uaddr); val && range.first != range.second; range.first++)
|
||||
{
|
||||
auto& entry = *range.first->second;
|
||||
|
||||
if (entry.mask & val3)
|
||||
|
||||
if (entry.mask & mask)
|
||||
{
|
||||
entry.cv.notify_one();
|
||||
entry.mask = 0;
|
||||
@@ -143,6 +146,163 @@ inline int futex(int* uaddr, int futex_op, int val, const timespec* timeout, int
|
||||
}
|
||||
} g_futex;
|
||||
|
||||
return g_futex(uaddr, futex_op, val, timeout, uaddr2, val3);
|
||||
return g_futex(uaddr, futex_op, val, timeout, mask);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T, typename Pred>
|
||||
bool balanced_wait_until(atomic_t<T>& var, u64 usec_timeout, Pred&& pred)
|
||||
{
|
||||
static_assert(sizeof(T) == 4 || sizeof(T) == 8);
|
||||
|
||||
const bool is_inf = usec_timeout > u64{UINT32_MAX / 1000} * 1000000;
|
||||
|
||||
// Optional second argument indicates that the predicate should try to retire
|
||||
auto test_pred = [&](T& _new, auto... args)
|
||||
{
|
||||
T old = var.load();
|
||||
|
||||
while (true)
|
||||
{
|
||||
_new = old;
|
||||
|
||||
// Zero indicates failure without modifying the value
|
||||
// Negative indicates failure but modifies the value
|
||||
auto ret = std::invoke(std::forward<Pred>(pred), _new, args...);
|
||||
|
||||
if (LIKELY(!ret || var.compare_exchange(old, _new)))
|
||||
{
|
||||
return ret > 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
T value;
|
||||
|
||||
#ifdef _WIN32
|
||||
if (OptWaitOnAddress)
|
||||
{
|
||||
while (!test_pred(value))
|
||||
{
|
||||
if (OptWaitOnAddress(&var, &value, sizeof(T), is_inf ? INFINITE : usec_timeout / 1000))
|
||||
{
|
||||
if (!test_pred(value, nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (GetLastError() == ERROR_TIMEOUT)
|
||||
{
|
||||
// Retire
|
||||
return test_pred(value, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LARGE_INTEGER timeout;
|
||||
timeout.QuadPart = usec_timeout * -10;
|
||||
|
||||
if (!usec_timeout || NtWaitForKeyedEvent(nullptr, &var, false, is_inf ? nullptr : &timeout))
|
||||
{
|
||||
// Timed out: retire
|
||||
if (!test_pred(value, nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Signaled in the last moment: restore balance
|
||||
NtWaitForKeyedEvent(nullptr, &var, false, nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!test_pred(value, nullptr))
|
||||
{
|
||||
// Stolen notification: restore balance
|
||||
NtReleaseKeyedEvent(nullptr, &var, false, nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
struct timespec timeout;
|
||||
timeout.tv_sec = usec_timeout / 1000000;
|
||||
timeout.tv_nsec = (usec_timeout % 1000000) * 1000;
|
||||
|
||||
char* ptr = reinterpret_cast<char*>(&var);
|
||||
|
||||
if constexpr (sizeof(T) == 8)
|
||||
{
|
||||
ptr += 4 * IS_BE_MACHINE;
|
||||
}
|
||||
|
||||
while (!test_pred(value))
|
||||
{
|
||||
if (futex(ptr, FUTEX_WAIT_PRIVATE, static_cast<u32>(value), is_inf ? nullptr : &timeout) == 0)
|
||||
{
|
||||
if (!test_pred(value, nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
switch (errno)
|
||||
{
|
||||
case EAGAIN: break;
|
||||
case ETIMEDOUT: return test_pred(value, nullptr);
|
||||
default: verify("Unknown futex error" HERE), 0;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <bool All = false, typename T>
|
||||
void balanced_awaken(atomic_t<T>& var, u32 weight)
|
||||
{
|
||||
static_assert(sizeof(T) == 4 || sizeof(T) == 8);
|
||||
|
||||
#ifdef _WIN32
|
||||
if (OptWaitOnAddress)
|
||||
{
|
||||
if (All || weight > 3)
|
||||
{
|
||||
OptWakeByAddressAll(&var);
|
||||
return;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < weight; i++)
|
||||
{
|
||||
OptWakeByAddressSingle(&var);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < weight; i++)
|
||||
{
|
||||
NtReleaseKeyedEvent(nullptr, &var, false, nullptr);
|
||||
}
|
||||
#else
|
||||
char* ptr = reinterpret_cast<char*>(&var);
|
||||
|
||||
if constexpr (sizeof(T) == 8)
|
||||
{
|
||||
ptr += 4 * IS_BE_MACHINE;
|
||||
}
|
||||
|
||||
if (All || weight)
|
||||
{
|
||||
futex(ptr, FUTEX_WAKE_PRIVATE, All ? INT_MAX : std::min<u32>(INT_MAX, weight));
|
||||
}
|
||||
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
Regular → Executable
+131
-7
@@ -1,10 +1,17 @@
|
||||
#include "sysinfo.h"
|
||||
#include "sysinfo.h"
|
||||
#include "StrFmt.h"
|
||||
#include "File.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "windows.h"
|
||||
#include "sysinfoapi.h"
|
||||
#include "subauth.h"
|
||||
#include "stringapiset.h"
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
bool utils::has_ssse3()
|
||||
@@ -13,29 +20,46 @@ bool utils::has_ssse3()
|
||||
return g_value;
|
||||
}
|
||||
|
||||
bool utils::has_sse41()
|
||||
{
|
||||
static const bool g_value = get_cpuid(0, 0)[0] >= 0x1 && get_cpuid(1, 0)[2] & 0x80000;
|
||||
return g_value;
|
||||
}
|
||||
|
||||
bool utils::has_avx()
|
||||
{
|
||||
static const bool g_value = get_cpuid(0, 0)[0] >= 0x1 && get_cpuid(1, 0)[2] & 0x10000000;
|
||||
static const bool g_value = get_cpuid(0, 0)[0] >= 0x1 && get_cpuid(1, 0)[2] & 0x10000000 && (get_cpuid(1, 0)[2] & 0x0C000000) == 0x0C000000 && (get_xgetbv(0) & 0x6) == 0x6;
|
||||
return g_value;
|
||||
}
|
||||
|
||||
bool utils::has_avx2()
|
||||
{
|
||||
static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && get_cpuid(7, 0)[1] & 0x20;
|
||||
static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && get_cpuid(7, 0)[1] & 0x20 && (get_cpuid(1, 0)[2] & 0x0C000000) == 0x0C000000 && (get_xgetbv(0) & 0x6) == 0x6;
|
||||
return g_value;
|
||||
}
|
||||
|
||||
bool utils::has_rtm()
|
||||
{
|
||||
// Check RTM and MPX extensions in order to filter out TSX on Haswell CPUs
|
||||
static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(7, 0)[1] & 0x4800) == 0x4800;
|
||||
static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(7, 0)[1] & 0x800) == 0x800;
|
||||
return g_value;
|
||||
}
|
||||
|
||||
bool utils::has_tsx_force_abort()
|
||||
{
|
||||
static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(7, 0)[3] & 0x2000) == 0x2000;
|
||||
return g_value;
|
||||
}
|
||||
|
||||
bool utils::has_mpx()
|
||||
{
|
||||
static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(7, 0)[1] & 0x4000) == 0x4000;
|
||||
return g_value;
|
||||
}
|
||||
|
||||
bool utils::has_512()
|
||||
{
|
||||
// Check AVX512F, AVX512CD, AVX512DQ, AVX512BW, AVX512VL extensions (Skylake-X level support)
|
||||
static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(7, 0)[1] & 0xd0030000) == 0xd0030000;
|
||||
static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(7, 0)[1] & 0xd0030000) == 0xd0030000 && (get_cpuid(1,0)[2] & 0x0C000000) == 0x0C000000 && (get_xgetbv(0) & 0xe6) == 0xe6;
|
||||
return g_value;
|
||||
}
|
||||
|
||||
@@ -62,8 +86,9 @@ std::string utils::get_system_info()
|
||||
brand = "Unknown CPU";
|
||||
}
|
||||
|
||||
brand.erase(0, brand.find_first_not_of(' '));
|
||||
brand.erase(brand.find_last_not_of('\0') + 1);
|
||||
brand.erase(brand.find_last_not_of(' ') + 1);
|
||||
brand.erase(0, brand.find_first_not_of(' '));
|
||||
|
||||
while (auto found = brand.find(" ") + 1)
|
||||
{
|
||||
@@ -85,6 +110,15 @@ std::string utils::get_system_info()
|
||||
|
||||
fmt::append(result, "%s | %d Threads | %.2f GiB RAM", brand, num_proc, mem_total / (1024.0f * 1024 * 1024));
|
||||
|
||||
if (const ullong tsc_freq = get_tsc_freq())
|
||||
{
|
||||
fmt::append(result, " | TSC: %.02fGHz", tsc_freq / 1000000000.);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::append(result, " | TSC: Bad");
|
||||
}
|
||||
|
||||
if (has_avx())
|
||||
{
|
||||
result += " | AVX";
|
||||
@@ -108,7 +142,97 @@ std::string utils::get_system_info()
|
||||
if (has_rtm())
|
||||
{
|
||||
result += " | TSX";
|
||||
|
||||
if (has_tsx_force_abort())
|
||||
{
|
||||
result += "-FA";
|
||||
}
|
||||
|
||||
if (!has_mpx())
|
||||
{
|
||||
result += " disabled by default";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string utils::get_firmware_version()
|
||||
{
|
||||
const std::string file_path = g_cfg.vfs.get_dev_flash() + "vsh/etc/version.txt";
|
||||
if (fs::is_file(file_path))
|
||||
{
|
||||
const fs::file version_file = fs::file(file_path);
|
||||
std::string version = version_file.to_string();
|
||||
|
||||
// Extract version
|
||||
const size_t start = version.find_first_of(':') + 1;
|
||||
const size_t end = version.find_first_of(':', start);
|
||||
version = version.substr(start, end - start);
|
||||
|
||||
// Trim version
|
||||
const size_t trim_start = version.find_first_not_of('0');
|
||||
const size_t trim_end = version.find_last_not_of('0');
|
||||
version = version.substr(trim_start, trim_end);
|
||||
|
||||
return version;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string utils::get_OS_version()
|
||||
{
|
||||
std::string output = "";
|
||||
#ifdef _WIN32
|
||||
// GetVersionEx is deprecated, RtlGetVersion is kernel-mode only and AnalyticsInfo is UWP only.
|
||||
// So we're forced to read PEB instead to get Windows version info. It's ugly but works.
|
||||
|
||||
const DWORD peb_offset = 0x60;
|
||||
const INT_PTR peb = __readgsqword(peb_offset);
|
||||
|
||||
const DWORD version_major = *reinterpret_cast<const DWORD*>(peb + 0x118);
|
||||
const DWORD version_minor = *reinterpret_cast<const DWORD*>(peb + 0x11c);
|
||||
const WORD build = *reinterpret_cast<const WORD*>(peb + 0x120);
|
||||
const UNICODE_STRING service_pack = *reinterpret_cast<const UNICODE_STRING*>(peb + 0x02E8);
|
||||
const u64 compatibility_mode = *reinterpret_cast<const u64*>(peb + 0x02C8); // Two DWORDs, major & minor version
|
||||
|
||||
const bool has_sp = service_pack.Length > 0;
|
||||
std::vector<char> holder(service_pack.Length + 1, '\0');
|
||||
if (has_sp)
|
||||
{
|
||||
WideCharToMultiByte(CP_UTF8, NULL, service_pack.Buffer, service_pack.Length,
|
||||
(LPSTR) holder.data(), static_cast<int>(holder.size()), NULL, NULL);
|
||||
}
|
||||
|
||||
fmt::append(output,
|
||||
"Operating system: Windows, Major: %lu, Minor: %lu, Build: %u, Service Pack: %s, Compatibility mode: %llu",
|
||||
version_major, version_minor, build, has_sp ? holder.data() : "none", compatibility_mode);
|
||||
#else
|
||||
struct utsname details = {};
|
||||
|
||||
if (!uname(&details))
|
||||
{
|
||||
fmt::append(output, "Operating system: POSIX, Name: %s, Release: %s, Version: %s",
|
||||
details.sysname, details.release, details.version);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::append(output, "Operating system: POSIX, Unknown version! (Error: %d)", errno);
|
||||
}
|
||||
#endif
|
||||
return output;
|
||||
}
|
||||
|
||||
ullong utils::get_tsc_freq()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
LARGE_INTEGER freq;
|
||||
if (!QueryPerformanceFrequency(&freq) || freq.QuadPart > 9'999'999)
|
||||
return 0;
|
||||
return freq.QuadPart * 1024;
|
||||
#else
|
||||
// TODO
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
Regular → Executable
+24
-19
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include <string>
|
||||
@@ -16,35 +16,40 @@ namespace utils
|
||||
return {0u+regs[0], 0u+regs[1], 0u+regs[2], 0u+regs[3]};
|
||||
}
|
||||
|
||||
inline u64 get_xgetbv(u32 xcr)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
return _xgetbv(xcr);
|
||||
#else
|
||||
u32 eax, edx;
|
||||
__asm__ volatile( "xgetbv" : "=a"(eax), "=d"(edx) : "c"(xcr));
|
||||
return eax | (u64(edx) << 32);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool has_ssse3();
|
||||
|
||||
bool has_sse41();
|
||||
|
||||
bool has_avx();
|
||||
|
||||
bool has_avx2();
|
||||
|
||||
bool has_rtm();
|
||||
|
||||
bool has_tsx_force_abort();
|
||||
|
||||
bool has_mpx();
|
||||
|
||||
bool has_512();
|
||||
|
||||
bool has_xop();
|
||||
|
||||
inline bool transaction_enter()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
const auto status = _xbegin();
|
||||
|
||||
if (status == _XBEGIN_STARTED)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(status & _XABORT_RETRY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_system_info();
|
||||
|
||||
std::string get_firmware_version();
|
||||
|
||||
std::string get_OS_version();
|
||||
|
||||
ullong get_tsc_freq();
|
||||
}
|
||||
|
||||
+1058
File diff suppressed because it is too large
Load Diff
+153
-309
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
@@ -9,41 +9,61 @@
|
||||
#include <emmintrin.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
#include <array>
|
||||
|
||||
// Assume little-endian
|
||||
#define IS_LE_MACHINE 1
|
||||
#define IS_BE_MACHINE 0
|
||||
|
||||
#ifndef __has_builtin
|
||||
#define __has_builtin(x) 0
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define ASSUME(cond) __assume(cond)
|
||||
|
||||
#define ASSUME(...) __assume(__VA_ARGS__) // MSVC __assume ignores side-effects
|
||||
#define LIKELY
|
||||
#define UNLIKELY
|
||||
#define SAFE_BUFFERS __declspec(safebuffers)
|
||||
#define NEVER_INLINE __declspec(noinline)
|
||||
#define FORCE_INLINE __forceinline
|
||||
#else
|
||||
#define ASSUME(cond) do { if (!(cond)) __builtin_unreachable(); } while (0)
|
||||
#define LIKELY(cond) __builtin_expect(!!(cond), 1)
|
||||
#define UNLIKELY(cond) __builtin_expect(!!(cond), 0)
|
||||
#define RESTRICT __restrict
|
||||
|
||||
#else // not _MSC_VER
|
||||
|
||||
#ifdef __clang__
|
||||
#if defined(__has_builtin) && __has_builtin(__builtin_assume)
|
||||
#pragma clang diagnostic ignored "-Wassume" // ignore the clang "side-effects ignored" warning
|
||||
#define ASSUME(...) __builtin_assume(!!(__VA_ARGS__)) // __builtin_assume (supported by modern clang) ignores side-effects
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef ASSUME // gcc and old clang
|
||||
#define ASSUME(...) do { if (!(__VA_ARGS__)) __builtin_unreachable(); } while (0) // note: the compiler will generate code to evaluate "cond" if the expression is opaque
|
||||
#endif
|
||||
|
||||
#define LIKELY(...) __builtin_expect(!!(__VA_ARGS__), 1)
|
||||
#define UNLIKELY(...) __builtin_expect(!!(__VA_ARGS__), 0)
|
||||
#define SAFE_BUFFERS
|
||||
#define NEVER_INLINE __attribute__((noinline))
|
||||
#define FORCE_INLINE __attribute__((always_inline)) inline
|
||||
#endif
|
||||
#define RESTRICT __restrict__
|
||||
|
||||
#endif // _MSC_VER
|
||||
|
||||
#define CHECK_SIZE(type, size) static_assert(sizeof(type) == size, "Invalid " #type " type size")
|
||||
#define CHECK_ALIGN(type, align) static_assert(alignof(type) == align, "Invalid " #type " type alignment")
|
||||
#define CHECK_MAX_SIZE(type, size) static_assert(sizeof(type) <= size, #type " type size is too big")
|
||||
#define CHECK_SIZE_ALIGN(type, size, align) CHECK_SIZE(type, size); CHECK_ALIGN(type, align)
|
||||
|
||||
// Return 32 bit sizeof() to avoid widening/narrowing conversions with size_t
|
||||
#define SIZE_32(...) static_cast<u32>(sizeof(__VA_ARGS__))
|
||||
|
||||
// Return 32 bit alignof() to avoid widening/narrowing conversions with size_t
|
||||
#define ALIGN_32(...) static_cast<u32>(alignof(__VA_ARGS__))
|
||||
// Variant pattern matching helper
|
||||
#define MATCH(arg, ...) constexpr(std::is_same_v<std::decay_t<decltype(arg)>, __VA_ARGS__>)
|
||||
|
||||
#define CONCATENATE_DETAIL(x, y) x ## y
|
||||
#define CONCATENATE(x, y) CONCATENATE_DETAIL(x, y)
|
||||
@@ -53,14 +73,36 @@
|
||||
|
||||
#define HERE "\n(in file " __FILE__ ":" STRINGIZE(__LINE__) ")"
|
||||
|
||||
// Ensure that the expression evaluates to true. Obsolete.
|
||||
//#define EXPECTS(...) do { if (!(__VA_ARGS__)) fmt::raw_error("Precondition failed: " #__VA_ARGS__ HERE); } while (0)
|
||||
//#define ENSURES(...) do { if (!(__VA_ARGS__)) fmt::raw_error("Postcondition failed: " #__VA_ARGS__ HERE); } while (0)
|
||||
|
||||
#define DECLARE(...) decltype(__VA_ARGS__) __VA_ARGS__
|
||||
|
||||
#define STR_CASE(...) case __VA_ARGS__: return #__VA_ARGS__
|
||||
|
||||
|
||||
#define ASSERT(...) do { if(!(__VA_ARGS__)) fmt::raw_error("Assertion failed: " STRINGIZE(__VA_ARGS__) HERE); } while(0)
|
||||
|
||||
#if defined(_DEBUG) || defined(_AUDIT)
|
||||
#define AUDIT(...) ASSERT(__VA_ARGS__)
|
||||
#else
|
||||
#define AUDIT(...) ((void)0)
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_lib_bit_cast) && (__cpp_lib_bit_cast >= 201806L)
|
||||
#include <bit>
|
||||
#else
|
||||
namespace std
|
||||
{
|
||||
template <class To, class From, typename = std::enable_if_t<sizeof(To) == sizeof(From)>>
|
||||
constexpr To bit_cast(const From& from) noexcept
|
||||
{
|
||||
static_assert(sizeof(To) == sizeof(From), "std::bit_cast<>: incompatible type size");
|
||||
|
||||
To result;
|
||||
std::memcpy(&result, &from, sizeof(From));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
using schar = signed char;
|
||||
using uchar = unsigned char;
|
||||
using ushort = unsigned short;
|
||||
@@ -69,7 +111,11 @@ using ulong = unsigned long;
|
||||
using ullong = unsigned long long;
|
||||
using llong = long long;
|
||||
|
||||
#if __APPLE__
|
||||
using uptr = std::uint64_t;
|
||||
#else
|
||||
using uptr = std::uintptr_t;
|
||||
#endif
|
||||
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
@@ -85,9 +131,49 @@ using steady_clock = std::conditional<
|
||||
std::chrono::high_resolution_clock::is_steady,
|
||||
std::chrono::high_resolution_clock, std::chrono::steady_clock>::type;
|
||||
|
||||
// Get integral type from type size
|
||||
template <std::size_t N>
|
||||
struct get_int_impl
|
||||
{
|
||||
};
|
||||
|
||||
template <>
|
||||
struct get_int_impl<sizeof(u8)>
|
||||
{
|
||||
using utype = u8;
|
||||
using stype = s8;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct get_int_impl<sizeof(u16)>
|
||||
{
|
||||
using utype = u16;
|
||||
using stype = s16;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct get_int_impl<sizeof(u32)>
|
||||
{
|
||||
using utype = u32;
|
||||
using stype = s32;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct get_int_impl<sizeof(u64)>
|
||||
{
|
||||
using utype = u64;
|
||||
using stype = s64;
|
||||
};
|
||||
|
||||
template <std::size_t N>
|
||||
using get_uint_t = typename get_int_impl<N>::utype;
|
||||
|
||||
template <std::size_t N>
|
||||
using get_sint_t = typename get_int_impl<N>::stype;
|
||||
|
||||
namespace gsl
|
||||
{
|
||||
enum class byte : u8;
|
||||
using std::byte;
|
||||
}
|
||||
|
||||
// Formatting helper, type-specific preprocessing for improving safety and functionality
|
||||
@@ -111,64 +197,9 @@ struct se_storage;
|
||||
template <typename T, bool Se = true, std::size_t Align = alignof(T)>
|
||||
class se_t;
|
||||
|
||||
template <typename T, std::size_t Size = sizeof(T)>
|
||||
struct atomic_storage;
|
||||
|
||||
template <typename T1, typename T2, typename = void>
|
||||
struct atomic_add;
|
||||
|
||||
template <typename T1, typename T2, typename = void>
|
||||
struct atomic_sub;
|
||||
|
||||
template <typename T1, typename T2, typename = void>
|
||||
struct atomic_and;
|
||||
|
||||
template <typename T1, typename T2, typename = void>
|
||||
struct atomic_or;
|
||||
|
||||
template <typename T1, typename T2, typename = void>
|
||||
struct atomic_xor;
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct atomic_pre_inc;
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct atomic_post_inc;
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct atomic_pre_dec;
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct atomic_post_dec;
|
||||
|
||||
template <typename T1, typename T2, typename = void>
|
||||
struct atomic_test_and_set;
|
||||
|
||||
template <typename T1, typename T2, typename = void>
|
||||
struct atomic_test_and_reset;
|
||||
|
||||
template <typename T1, typename T2, typename = void>
|
||||
struct atomic_test_and_complement;
|
||||
|
||||
template <typename T>
|
||||
class atomic_t;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
using std::void_t;
|
||||
#else
|
||||
namespace void_details
|
||||
{
|
||||
template <typename...>
|
||||
struct make_void
|
||||
{
|
||||
using type = void;
|
||||
};
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
using void_t = typename void_details::make_void<T...>::type;
|
||||
#endif
|
||||
|
||||
// Extract T::simple_type if available, remove cv qualifiers
|
||||
template <typename T, typename = void>
|
||||
struct simple_type_helper
|
||||
@@ -177,7 +208,7 @@ struct simple_type_helper
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct simple_type_helper<T, void_t<typename T::simple_type>>
|
||||
struct simple_type_helper<T, std::void_t<typename T::simple_type>>
|
||||
{
|
||||
using type = typename T::simple_type;
|
||||
};
|
||||
@@ -204,22 +235,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// Bool wrapper for restricting bool result conversions
|
||||
struct explicit_bool_t
|
||||
{
|
||||
const bool value;
|
||||
|
||||
constexpr explicit_bool_t(bool value)
|
||||
: value(value)
|
||||
{
|
||||
}
|
||||
|
||||
explicit constexpr operator bool() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef _MSC_VER
|
||||
using u128 = __uint128_t;
|
||||
using s128 = __int128_t;
|
||||
@@ -409,6 +424,9 @@ struct alignas(16) s128
|
||||
CHECK_SIZE_ALIGN(u128, 16, 16);
|
||||
CHECK_SIZE_ALIGN(s128, 16, 16);
|
||||
|
||||
using f32 = float;
|
||||
using f64 = double;
|
||||
|
||||
union alignas(2) f16
|
||||
{
|
||||
u16 _u16;
|
||||
@@ -419,30 +437,20 @@ union alignas(2) f16
|
||||
_u16 = raw;
|
||||
}
|
||||
|
||||
explicit operator float() const
|
||||
explicit operator f32() const
|
||||
{
|
||||
// See http://stackoverflow.com/a/26779139
|
||||
// The conversion doesn't handle NaN/Inf
|
||||
u32 raw = ((_u16 & 0x8000) << 16) | // Sign (just moved)
|
||||
(((_u16 & 0x7c00) + 0x1C000) << 13) | // Exponent ( exp - 15 + 127)
|
||||
((_u16 & 0x03FF) << 13); // Mantissa
|
||||
return (float&)raw;
|
||||
|
||||
return std::bit_cast<f32>(raw);
|
||||
}
|
||||
};
|
||||
|
||||
CHECK_SIZE_ALIGN(f16, 2, 2);
|
||||
|
||||
using f32 = float;
|
||||
using f64 = double;
|
||||
|
||||
struct ignore
|
||||
{
|
||||
template <typename T>
|
||||
ignore(T)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
|
||||
constexpr T align(const T& value, ullong align)
|
||||
{
|
||||
@@ -453,13 +461,11 @@ template <typename T, typename T2>
|
||||
inline u32 offset32(T T2::*const mptr)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
static_assert(sizeof(mptr) == sizeof(u32), "Invalid pointer-to-member size");
|
||||
return reinterpret_cast<const u32&>(mptr);
|
||||
return std::bit_cast<u32>(mptr);
|
||||
#elif __GNUG__
|
||||
static_assert(sizeof(mptr) == sizeof(std::size_t), "Invalid pointer-to-member size");
|
||||
return static_cast<u32>(reinterpret_cast<const std::size_t&>(mptr));
|
||||
return std::bit_cast<std::size_t>(mptr);
|
||||
#else
|
||||
static_assert(sizeof(mptr) == 0, "Invalid pointer-to-member size");
|
||||
static_assert(sizeof(mptr) == 0, "Unsupported pointer-to-member size");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -471,7 +477,7 @@ struct offset32_array
|
||||
template <typename Arg>
|
||||
static inline u32 index32(const Arg& arg)
|
||||
{
|
||||
return SIZE_32(std::remove_extent_t<T>) * static_cast<u32>(arg);
|
||||
return u32{sizeof(std::remove_extent_t<T>)} * static_cast<u32>(arg);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -481,7 +487,7 @@ struct offset32_array<std::array<T, N>>
|
||||
template <typename Arg>
|
||||
static inline u32 index32(const Arg& arg)
|
||||
{
|
||||
return SIZE_32(T) * static_cast<u32>(arg);
|
||||
return u32{sizeof(T)} * static_cast<u32>(arg);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -514,46 +520,6 @@ struct offset32_detail<T3 T4::*>
|
||||
}
|
||||
};
|
||||
|
||||
inline u32 cntlz32(u32 arg, bool nonzero = false)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
ulong res;
|
||||
return _BitScanReverse(&res, arg) || nonzero ? res ^ 31 : 32;
|
||||
#else
|
||||
return arg || nonzero ? __builtin_clzll(arg) - 32 : 32;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u64 cntlz64(u64 arg, bool nonzero = false)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
ulong res;
|
||||
return _BitScanReverse64(&res, arg) || nonzero ? res ^ 63 : 64;
|
||||
#else
|
||||
return arg || nonzero ? __builtin_clzll(arg) : 64;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u32 cnttz32(u32 arg, bool nonzero = false)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
ulong res;
|
||||
return _BitScanForward(&res, arg) || nonzero ? res : 32;
|
||||
#else
|
||||
return arg || nonzero ? __builtin_ctzll(arg) : 32;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u64 cnttz64(u64 arg, bool nonzero = false)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
ulong res;
|
||||
return _BitScanForward64(&res, arg) || nonzero ? res : 64;
|
||||
#else
|
||||
return arg || nonzero ? __builtin_ctzll(arg) : 64;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Helper function, used by ""_u16, ""_u32, ""_u64
|
||||
constexpr u8 to_u8(char c)
|
||||
{
|
||||
@@ -725,7 +691,7 @@ struct narrow_impl<From, To, std::enable_if_t<std::is_signed<From>::value && std
|
||||
|
||||
// Simple type enabled (TODO: allow for To as well)
|
||||
template <typename From, typename To>
|
||||
struct narrow_impl<From, To, void_t<typename From::simple_type>>
|
||||
struct narrow_impl<From, To, std::void_t<typename From::simple_type>>
|
||||
: narrow_impl<simple_t<From>, To>
|
||||
{
|
||||
};
|
||||
@@ -757,42 +723,6 @@ constexpr u32 size32(const T (&)[Size], const char* msg = nullptr)
|
||||
return static_cast<u32>(Size);
|
||||
}
|
||||
|
||||
template <typename T1, typename = std::enable_if_t<std::is_integral<T1>::value>>
|
||||
constexpr bool test(const T1& value)
|
||||
{
|
||||
return value != 0;
|
||||
}
|
||||
|
||||
template <typename T1, typename T2, typename = std::enable_if_t<std::is_integral<T1>::value && std::is_integral<T2>::value>>
|
||||
constexpr bool test(const T1& lhs, const T2& rhs)
|
||||
{
|
||||
return (lhs & rhs) != 0;
|
||||
}
|
||||
|
||||
template <typename T, typename T2, typename = std::enable_if_t<std::is_integral<T>::value && std::is_integral<T2>::value>>
|
||||
inline bool test_and_set(T& lhs, const T2& rhs)
|
||||
{
|
||||
const bool result = (lhs & rhs) != 0;
|
||||
lhs |= rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, typename T2, typename = std::enable_if_t<std::is_integral<T>::value && std::is_integral<T2>::value>>
|
||||
inline bool test_and_reset(T& lhs, const T2& rhs)
|
||||
{
|
||||
const bool result = (lhs & rhs) != 0;
|
||||
lhs &= ~rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, typename T2, typename = std::enable_if_t<std::is_integral<T>::value && std::is_integral<T2>::value>>
|
||||
inline bool test_and_complement(T& lhs, const T2& rhs)
|
||||
{
|
||||
const bool result = (lhs & rhs) != 0;
|
||||
lhs ^= rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Simplified hash algorithm for pointers. May be used in std::unordered_(map|set).
|
||||
template <typename T, std::size_t Align = alignof(T)>
|
||||
struct pointer_hash
|
||||
@@ -817,7 +747,7 @@ struct value_hash
|
||||
template <template <typename> class TT, std::size_t S, std::size_t A = S>
|
||||
struct alignas(A) any_pod
|
||||
{
|
||||
std::aligned_storage_t<S, A> data;
|
||||
alignas(A) std::byte data[S];
|
||||
|
||||
any_pod() = default;
|
||||
|
||||
@@ -900,35 +830,6 @@ struct cmd64 : any64
|
||||
|
||||
static_assert(sizeof(cmd64) == 8 && std::is_pod<cmd64>::value, "Incorrect cmd64 type");
|
||||
|
||||
// Allows to define integer convertible to multiple types
|
||||
template <typename T, T Value, typename T1 = void, typename... Ts>
|
||||
struct multicast : multicast<T, Value, Ts...>
|
||||
{
|
||||
constexpr multicast()
|
||||
: multicast<T, Value, Ts...>()
|
||||
{
|
||||
}
|
||||
|
||||
// Implicit conversion to desired type
|
||||
constexpr operator T1() const
|
||||
{
|
||||
return static_cast<T1>(Value);
|
||||
}
|
||||
};
|
||||
|
||||
// Recursion terminator
|
||||
template <typename T, T Value>
|
||||
struct multicast<T, Value, void>
|
||||
{
|
||||
constexpr multicast() = default;
|
||||
|
||||
// Explicit conversion to base type
|
||||
explicit constexpr operator T() const
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
};
|
||||
|
||||
// Error code type (return type), implements error reporting. Could be a template.
|
||||
struct error_code
|
||||
{
|
||||
@@ -953,26 +854,23 @@ struct error_code
|
||||
};
|
||||
|
||||
template<typename ET>
|
||||
struct is_error<ET, void_t<decltype(ET::__not_an_error)>> : std::false_type
|
||||
struct is_error<ET, std::enable_if_t<sizeof(ET::__not_an_error) != 0>> : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
// Not an error constructor
|
||||
template<typename ET, typename = decltype(ET::__not_an_error)>
|
||||
error_code(const ET& value, std::nullptr_t = nullptr)
|
||||
// Common constructor
|
||||
template<typename ET>
|
||||
error_code(const ET& value)
|
||||
: value(static_cast<s32>(value))
|
||||
{
|
||||
}
|
||||
|
||||
// Error constructor
|
||||
template<typename ET, typename = std::enable_if_t<is_error<ET>::value>>
|
||||
error_code(const ET& value)
|
||||
: value(error_report(fmt::get_type_info<fmt_unveil_t<ET>>(), fmt_unveil<ET>::get(value), nullptr, 0))
|
||||
{
|
||||
if constexpr(is_error<ET>::value)
|
||||
{
|
||||
this->value = error_report(fmt::get_type_info<fmt_unveil_t<ET>>(), fmt_unveil<ET>::get(value), nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Error constructor (2 args)
|
||||
template<typename ET, typename T2, typename = std::enable_if_t<is_error<ET>::value>>
|
||||
template<typename ET, typename T2>
|
||||
error_code(const ET& value, const T2& value2)
|
||||
: value(error_report(fmt::get_type_info<fmt_unveil_t<ET>>(), fmt_unveil<ET>::get(value), fmt::get_type_info<fmt_unveil_t<T2>>(), fmt_unveil<T2>::get(value2)))
|
||||
{
|
||||
@@ -992,93 +890,39 @@ constexpr FORCE_INLINE error_code::not_an_error not_an_error(const T& value)
|
||||
}
|
||||
|
||||
// Synchronization helper (cache-friendly busy waiting)
|
||||
inline void busy_wait(std::size_t count = 100)
|
||||
inline void busy_wait(std::size_t cycles = 3000)
|
||||
{
|
||||
while (count--) _mm_pause();
|
||||
const u64 s = __rdtsc();
|
||||
do _mm_pause(); while (__rdtsc() - s < cycles);
|
||||
}
|
||||
|
||||
// Rotate helpers
|
||||
#if defined(__GNUG__)
|
||||
|
||||
inline u8 rol8(u8 x, u8 n)
|
||||
// TODO: Remove when moving to c++20
|
||||
template <typename T>
|
||||
inline constexpr uintmax_t floor2(T value)
|
||||
{
|
||||
u8 result = x;
|
||||
__asm__("rolb %[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||
return result;
|
||||
value >>= 1;
|
||||
|
||||
for (uintmax_t i = 0;; i++, value >>= 1)
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline u8 ror8(u8 x, u8 n)
|
||||
template <typename T>
|
||||
inline constexpr uintmax_t ceil2(T value)
|
||||
{
|
||||
u8 result = x;
|
||||
__asm__("rorb %[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||
return result;
|
||||
}
|
||||
const uintmax_t ispow2 = value & (value - 1); // if power of 2 the result is 0
|
||||
|
||||
inline u16 rol16(u16 x, u16 n)
|
||||
{
|
||||
u16 result = x;
|
||||
__asm__("rolw %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||
return result;
|
||||
}
|
||||
value >>= 1;
|
||||
|
||||
inline u16 ror16(u16 x, u16 n)
|
||||
{
|
||||
u16 result = x;
|
||||
__asm__("rorw %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline u32 rol32(u32 x, u32 n)
|
||||
{
|
||||
u32 result = x;
|
||||
__asm__("roll %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline u32 ror32(u32 x, u32 n)
|
||||
{
|
||||
u32 result = x;
|
||||
__asm__("rorl %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline u64 rol64(u64 x, u64 n)
|
||||
{
|
||||
u64 result = x;
|
||||
__asm__("rolq %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline u64 ror64(u64 x, u64 n)
|
||||
{
|
||||
u64 result = x;
|
||||
__asm__("rorq %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline u64 umulh64(u64 a, u64 b)
|
||||
{
|
||||
u64 result;
|
||||
__asm__("mulq %[b]" : "=d" (result) : [a] "a" (a), [b] "rm" (b));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline s64 mulh64(s64 a, s64 b)
|
||||
{
|
||||
s64 result;
|
||||
__asm__("imulq %[b]" : "=d" (result) : [a] "a" (a), [b] "rm" (b));
|
||||
return result;
|
||||
}
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
inline u8 rol8(u8 x, u8 n) { return _rotl8(x, n); }
|
||||
inline u8 ror8(u8 x, u8 n) { return _rotr8(x, n); }
|
||||
inline u16 rol16(u16 x, u16 n) { return _rotl16(x, (u8)n); }
|
||||
inline u16 ror16(u16 x, u16 n) { return _rotr16(x, (u8)n); }
|
||||
inline u32 rol32(u32 x, u32 n) { return _rotl(x, (int)n); }
|
||||
inline u32 ror32(u32 x, u32 n) { return _rotr(x, (int)n); }
|
||||
inline u64 rol64(u64 x, u64 n) { return _rotl64(x, (int)n); }
|
||||
inline u64 ror64(u64 x, u64 n) { return _rotr64(x, (int)n); }
|
||||
inline u64 umulh64(u64 x, u64 y) { return __umulh(x, y); }
|
||||
inline s64 mulh64(s64 x, s64 y) { return __mulh(x, y); }
|
||||
#endif
|
||||
for (uintmax_t i = 0;; i++, value >>= 1)
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
return i + std::min<uintmax_t>(ispow2, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user