Compare commits
749 Commits
mainline-0
...
mainline-0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
931c52117f | ||
|
|
830b714a79 | ||
|
|
3e6e0d8f13 | ||
|
|
dee133ab3d | ||
|
|
3c22ce035b | ||
|
|
a5e184e948 | ||
|
|
0e004269a9 | ||
|
|
a58086ae0d | ||
|
|
68f718943e | ||
|
|
70db238f80 | ||
|
|
245d60bfff | ||
|
|
a352f34462 | ||
|
|
cda24b8eb1 | ||
|
|
10636d2494 | ||
|
|
783dc9e112 | ||
|
|
864762cac9 | ||
|
|
5c7c212f61 | ||
|
|
2e32ab4e0b | ||
|
|
b6736db324 | ||
|
|
d6ebb5c171 | ||
|
|
26669d9e13 | ||
|
|
1b730827dd | ||
|
|
7666c0994c | ||
|
|
d1ac25b632 | ||
|
|
903311729a | ||
|
|
ff58ad2050 | ||
|
|
0896089092 | ||
|
|
3f9eb56972 | ||
|
|
d0a760a34a | ||
|
|
4c82c08897 | ||
|
|
148fb12bf3 | ||
|
|
c5f109bc50 | ||
|
|
6e5cc977ad | ||
|
|
230e71b255 | ||
|
|
458be11f93 | ||
|
|
9d5a56a40b | ||
|
|
8893b766c3 | ||
|
|
4a01812ebe | ||
|
|
9078f4a9c7 | ||
|
|
8d00265998 | ||
|
|
32d9a83f8e | ||
|
|
160341fcf8 | ||
|
|
d6a0975e5d | ||
|
|
c9597af39d | ||
|
|
a7f9983563 | ||
|
|
ea1f656d7e | ||
|
|
481cd86722 | ||
|
|
61bf850f3d | ||
|
|
40ab2b9348 | ||
|
|
b8fc74d74d | ||
|
|
7b9c58880f | ||
|
|
1a2e7c4dbd | ||
|
|
527188391c | ||
|
|
75a60a6e22 | ||
|
|
1498a7c9a8 | ||
|
|
8b54e219c9 | ||
|
|
3a804752cb | ||
|
|
830612ef9e | ||
|
|
ea4f62615e | ||
|
|
546af64340 | ||
|
|
eba3c59a61 | ||
|
|
18175c71ed | ||
|
|
ff3c7c068b | ||
|
|
6bf80dfee0 | ||
|
|
e9446d232f | ||
|
|
4577dcd5f9 | ||
|
|
3f942c01f0 | ||
|
|
e86a7e3691 | ||
|
|
b0727c90c5 | ||
|
|
b7e6eca8b2 | ||
|
|
1d19eac415 | ||
|
|
0913aaa42a | ||
|
|
47401016bf | ||
|
|
82dd376ba2 | ||
|
|
741dc13c5a | ||
|
|
00cb631b2f | ||
|
|
756365386a | ||
|
|
b944edc85d | ||
|
|
31e6e58101 | ||
|
|
53aec1fe2d | ||
|
|
eb3afd30b1 | ||
|
|
806e2d7900 | ||
|
|
b331bb5210 | ||
|
|
8019b2b9b5 | ||
|
|
9a9e81f2e9 | ||
|
|
f30ef98761 | ||
|
|
cde532cc52 | ||
|
|
c1b81f776c | ||
|
|
b0c9752663 | ||
|
|
2c6e940493 | ||
|
|
48d040fded | ||
|
|
e5a76d728f | ||
|
|
7784b1da6d | ||
|
|
82d232db46 | ||
|
|
13b08376b7 | ||
|
|
9137f3ec68 | ||
|
|
fc43eac82a | ||
|
|
d4ebc9a120 | ||
|
|
64c3582705 | ||
|
|
9e4b2d60bc | ||
|
|
5e49b81d4d | ||
|
|
821fc4a7b6 | ||
|
|
f317b0d354 | ||
|
|
f614d7d887 | ||
|
|
9fc7f60b94 | ||
|
|
67d08f14af | ||
|
|
2489547dc5 | ||
|
|
1498ece290 | ||
|
|
f7dc77e03a | ||
|
|
8c7d89e6c7 | ||
|
|
7d9465d47a | ||
|
|
2394807b42 | ||
|
|
4bf1cf1f81 | ||
|
|
70a2065828 | ||
|
|
ec51f8de82 | ||
|
|
9f6b35e61f | ||
|
|
ded094a340 | ||
|
|
1cc009a996 | ||
|
|
9914db8daa | ||
|
|
94f660d1cb | ||
|
|
6cc769065d | ||
|
|
360ea64738 | ||
|
|
ad146eca60 | ||
|
|
99dcf7da21 | ||
|
|
ee333e063d | ||
|
|
56742c6222 | ||
|
|
7791cfd960 | ||
|
|
3bf62c7a8a | ||
|
|
3be1a565f8 | ||
|
|
a4526c4e1a | ||
|
|
5861bacafd | ||
|
|
f67cfebada | ||
|
|
aaf834ffa6 | ||
|
|
9150b8972e | ||
|
|
650734cc3e | ||
|
|
15d3376399 | ||
|
|
ef8b3623f5 | ||
|
|
b786568c5a | ||
|
|
543e212554 | ||
|
|
8d1afcb90f | ||
|
|
c8fe8247ee | ||
|
|
e24c6dab93 | ||
|
|
10738839ad | ||
|
|
3856564727 | ||
|
|
ff46ef7ea3 | ||
|
|
6ee8340a6b | ||
|
|
055194d2ab | ||
|
|
091e9e8c41 | ||
|
|
6e953f7f02 | ||
|
|
37f74d8741 | ||
|
|
f6b10fad63 | ||
|
|
0a1449e04b | ||
|
|
89a5ae92bd | ||
|
|
ca78f77827 | ||
|
|
cdd14b03e5 | ||
|
|
1470338458 | ||
|
|
1772ebeb1e | ||
|
|
1f99f5473c | ||
|
|
c0f5830323 | ||
|
|
bb966d3e33 | ||
|
|
ff186b2498 | ||
|
|
33b4930280 | ||
|
|
5a4fc4a529 | ||
|
|
97129bc742 | ||
|
|
2fb77adb9f | ||
|
|
4dbf3f4880 | ||
|
|
1e55498110 | ||
|
|
0530292b97 | ||
|
|
4782985013 | ||
|
|
eea346ba8e | ||
|
|
9a4e148f9e | ||
|
|
c0d3aef28c | ||
|
|
8ba0cac71c | ||
|
|
df41e78205 | ||
|
|
7b8fa78c65 | ||
|
|
c21ce728c2 | ||
|
|
16818e952c | ||
|
|
9f6290d207 | ||
|
|
0e125dfd43 | ||
|
|
4921ba05db | ||
|
|
ae6b3bdfbf | ||
|
|
008afa5d59 | ||
|
|
659b5f8088 | ||
|
|
9e88ad8da9 | ||
|
|
45b13c3037 | ||
|
|
ef6cc3aa1d | ||
|
|
28b822fe38 | ||
|
|
d4d39aa4c7 | ||
|
|
fb0fe3b8c3 | ||
|
|
2350b76a91 | ||
|
|
09b6f03592 | ||
|
|
72c1cb85f1 | ||
|
|
64a5548454 | ||
|
|
81a037df9d | ||
|
|
2c57f0fbd5 | ||
|
|
04e9486651 | ||
|
|
2a2ee62cfd | ||
|
|
62766b1326 | ||
|
|
5dc021d15b | ||
|
|
34c3ec2f8c | ||
|
|
45e117b043 | ||
|
|
9dc4a80b17 | ||
|
|
df0d8c45d2 | ||
|
|
b769b1be26 | ||
|
|
44c5ea3639 | ||
|
|
6b00443bc1 | ||
|
|
8959f3521f | ||
|
|
6a0143400f | ||
|
|
748551dafb | ||
|
|
19c14589d3 | ||
|
|
a8245cf2f1 | ||
|
|
2afc1060ef | ||
|
|
5882cc0502 | ||
|
|
04dcada85f | ||
|
|
b2b95e96c1 | ||
|
|
f81c783b5b | ||
|
|
cc4335a9c6 | ||
|
|
f7ac4e1eb4 | ||
|
|
1b76e7e890 | ||
|
|
80a673a27f | ||
|
|
ad48259d7e | ||
|
|
3b4da2d7fa | ||
|
|
e9bb95ae16 | ||
|
|
9477d23d70 | ||
|
|
bfd2bcb068 | ||
|
|
5942d206c2 | ||
|
|
822edff5bd | ||
|
|
3b0458a7a5 | ||
|
|
65f821850e | ||
|
|
df42100320 | ||
|
|
966896daad | ||
|
|
625a011888 | ||
|
|
12355cbf02 | ||
|
|
37ef2ee595 | ||
|
|
302a5f00e8 | ||
|
|
981d8e82d2 | ||
|
|
bda177ef40 | ||
|
|
a175ba1089 | ||
|
|
1e9b1d439f | ||
|
|
436457b6e7 | ||
|
|
2c4c7aea8a | ||
|
|
b7febb5625 | ||
|
|
0e9a6759f9 | ||
|
|
dd790abab0 | ||
|
|
46dda01151 | ||
|
|
6ff2db181f | ||
|
|
a1335d3d51 | ||
|
|
ffbde909c8 | ||
|
|
f83ef80ebd | ||
|
|
51512d01d8 | ||
|
|
d98b0f8f48 | ||
|
|
c795207fb2 | ||
|
|
5b8bc56e65 | ||
|
|
dc18a1261c | ||
|
|
dca2e2c8f1 | ||
|
|
83f8c1a25e | ||
|
|
4cd8b2f1f7 | ||
|
|
2d33b2c55a | ||
|
|
2ef4591e58 | ||
|
|
f1b58f0cd9 | ||
|
|
dd0679d710 | ||
|
|
4a67a5b917 | ||
|
|
e7c1d7bf77 | ||
|
|
bf9f737c60 | ||
|
|
fb796843df | ||
|
|
0bd8cecc94 | ||
|
|
e8401964b4 | ||
|
|
132f2006af | ||
|
|
e1ecf64701 | ||
|
|
5f4e7c77bd | ||
|
|
40acc2c079 | ||
|
|
c61b973968 | ||
|
|
0e0fc07135 | ||
|
|
fd873fd369 | ||
|
|
ca9afa3293 | ||
|
|
ff2b7cc0d3 | ||
|
|
3c8f936b31 | ||
|
|
a7fd61fcce | ||
|
|
8def504d73 | ||
|
|
c17ee0da5d | ||
|
|
c3c7603076 | ||
|
|
8be9e5b48b | ||
|
|
5f517e3e16 | ||
|
|
f8650a9580 | ||
|
|
3ff978aa4f | ||
|
|
301e2b5b7a | ||
|
|
432f045dba | ||
|
|
8f22f5470c | ||
|
|
72541af3bc | ||
|
|
fade63b58e | ||
|
|
c2b550987b | ||
|
|
e996f1ad09 | ||
|
|
f728a504aa | ||
|
|
b483f2d010 | ||
|
|
8495e1bd83 | ||
|
|
d8df9a16bd | ||
|
|
390ee10eef | ||
|
|
d583e01f54 | ||
|
|
9754a8145c | ||
|
|
5b9aedfc21 | ||
|
|
8620de6b20 | ||
|
|
89c15dd115 | ||
|
|
fe494a0ccd | ||
|
|
91084d9396 | ||
|
|
c8bf0caca0 | ||
|
|
6676687694 | ||
|
|
95fa57f007 | ||
|
|
7f37822c74 | ||
|
|
fb99446f24 | ||
|
|
cc2c3e447f | ||
|
|
28e78d81b2 | ||
|
|
185388f341 | ||
|
|
76b465f3ef | ||
|
|
af540b0057 | ||
|
|
06e0506cb3 | ||
|
|
71264ce9a7 | ||
|
|
6dc1d48fd1 | ||
|
|
3e03391a49 | ||
|
|
be8fd5490e | ||
|
|
ba2ea7eeac | ||
|
|
22be115eb2 | ||
|
|
0ec71b78fb | ||
|
|
93f7719eed | ||
|
|
4038ca2e5d | ||
|
|
e11e1dcf2d | ||
|
|
f1e278c30f | ||
|
|
980973d83e | ||
|
|
45aee996c1 | ||
|
|
a2952ac213 | ||
|
|
5e35c69f35 | ||
|
|
2c2ef9252f | ||
|
|
06cf705501 | ||
|
|
0d7de7c2db | ||
|
|
baff865d7c | ||
|
|
d9a15a935b | ||
|
|
7bd603061c | ||
|
|
c320da3f63 | ||
|
|
a4bfae1b55 | ||
|
|
2b98da2ed4 | ||
|
|
0fb19e9bef | ||
|
|
de1a316369 | ||
|
|
b15e1a3501 | ||
|
|
197b5d19bc | ||
|
|
99d2d77062 | ||
|
|
703c57a119 | ||
|
|
eb3cb54aa5 | ||
|
|
03dfc8d8e7 | ||
|
|
81c1bfafea | ||
|
|
6b2f653143 | ||
|
|
354130cd84 | ||
|
|
82f6037ec2 | ||
|
|
912dd50146 | ||
|
|
952d1ac487 | ||
|
|
b4e6d6c385 | ||
|
|
1212fa60b6 | ||
|
|
8a155c4058 | ||
|
|
92d5c63f01 | ||
|
|
f12701b303 | ||
|
|
d1309fb275 | ||
|
|
c3c43e32fc | ||
|
|
7420a717e6 | ||
|
|
4bbf173fc1 | ||
|
|
fb43b8efd2 | ||
|
|
35c3c078e3 | ||
|
|
1ae883435d | ||
|
|
8fc6e92ef1 | ||
|
|
7a3c884e39 | ||
|
|
46cd71d1c7 | ||
|
|
5e161b2531 | ||
|
|
32df83e55d | ||
|
|
05f58144c9 | ||
|
|
fe9588f4a0 | ||
|
|
25724898d0 | ||
|
|
e07540264d | ||
|
|
0f932d30f5 | ||
|
|
64a24f3344 | ||
|
|
4aae21e1e4 | ||
|
|
c190586597 | ||
|
|
7bad1974a6 | ||
|
|
d7128845c9 | ||
|
|
c68d0dc851 | ||
|
|
b4451c5e81 | ||
|
|
613b3671b7 | ||
|
|
8eea7c1176 | ||
|
|
385a4555d5 | ||
|
|
61f707d708 | ||
|
|
154a7653f9 | ||
|
|
c72571055b | ||
|
|
78be397723 | ||
|
|
aaf9e39f56 | ||
|
|
16392a23cc | ||
|
|
06cef3355e | ||
|
|
2c27127d04 | ||
|
|
bcb702fa3e | ||
|
|
21199cb965 | ||
|
|
123568ef80 | ||
|
|
aace20afc7 | ||
|
|
0d24b1a31b | ||
|
|
5b60899fbc | ||
|
|
e8d40559d5 | ||
|
|
e112d0a52f | ||
|
|
dc02b03c4a | ||
|
|
275b96a0e2 | ||
|
|
43d9f417ae | ||
|
|
4f13e270c8 | ||
|
|
2a6e6306d8 | ||
|
|
4e6aa1cfdd | ||
|
|
1ccf805367 | ||
|
|
ace8a8e86e | ||
|
|
6b354ccaee | ||
|
|
ac1e4734c2 | ||
|
|
d235cf3933 | ||
|
|
3753553b6a | ||
|
|
4801f4250d | ||
|
|
7d904fef2e | ||
|
|
3a49c1a691 | ||
|
|
beb951770a | ||
|
|
fd5776aac2 | ||
|
|
87a8925523 | ||
|
|
974d731926 | ||
|
|
7265e80c12 | ||
|
|
86592b274e | ||
|
|
71e18dddbe | ||
|
|
f64456c7e2 | ||
|
|
ec58aabb26 | ||
|
|
c90268127b | ||
|
|
bf8bd60ab3 | ||
|
|
f478a57737 | ||
|
|
235b5d27ae | ||
|
|
beaa25d777 | ||
|
|
8a5356357f | ||
|
|
62f67df6d7 | ||
|
|
55fb8e7bdd | ||
|
|
57c9da1b39 | ||
|
|
a745d87971 | ||
|
|
53d92318b8 | ||
|
|
d4f871cb6a | ||
|
|
1ff341f3dc | ||
|
|
0d47c1d527 | ||
|
|
9e109849ff | ||
|
|
904ac1daec | ||
|
|
6d30745d77 | ||
|
|
eb318ffffc | ||
|
|
0bddb794b0 | ||
|
|
5dfb8743cb | ||
|
|
8c27a74132 | ||
|
|
803ac4ca59 | ||
|
|
60121d8f28 | ||
|
|
fb41c82aaa | ||
|
|
25d607f5f6 | ||
|
|
cdbee27692 | ||
|
|
7344a7c447 | ||
|
|
f687392e6f | ||
|
|
53ea06dc17 | ||
|
|
085adfea00 | ||
|
|
11f0f7598d | ||
|
|
dce8720780 | ||
|
|
47843b4f09 | ||
|
|
25f88d99ce | ||
|
|
d1435009ed | ||
|
|
d937421422 | ||
|
|
aa4c7687ee | ||
|
|
fa5a1a4bfd | ||
|
|
53e49e5360 | ||
|
|
bcafef4b94 | ||
|
|
43cad754d5 | ||
|
|
dab7711524 | ||
|
|
f0d9ab0717 | ||
|
|
da07977db0 | ||
|
|
d5fe722a30 | ||
|
|
9764c13d6d | ||
|
|
ac2e2ebe97 | ||
|
|
157fc2d785 | ||
|
|
9106ac1e6b | ||
|
|
21b18057f7 | ||
|
|
87ff58b1d7 | ||
|
|
ae5725b709 | ||
|
|
64fbf319f1 | ||
|
|
82b7daed9c | ||
|
|
dc81a90640 | ||
|
|
5169ce9fcd | ||
|
|
59c46f9de9 | ||
|
|
12d16248dd | ||
|
|
f20e18f60d | ||
|
|
95d156a150 | ||
|
|
b3587102d1 | ||
|
|
85cfd96f62 | ||
|
|
82e0eeed21 | ||
|
|
a2a0f5318d | ||
|
|
69e82d01d5 | ||
|
|
b02464f685 | ||
|
|
c192da3f82 | ||
|
|
8d55c8c855 | ||
|
|
3f048c8646 | ||
|
|
388cf58b31 | ||
|
|
b36896b90e | ||
|
|
aa87278bf0 | ||
|
|
0383363a8f | ||
|
|
22ba437aa4 | ||
|
|
dfdac7d38a | ||
|
|
f57be2e626 | ||
|
|
7d77a3f88f | ||
|
|
c7a06908ae | ||
|
|
06f8c3dc01 | ||
|
|
d0649d0971 | ||
|
|
954341763a | ||
|
|
994a9fec4e | ||
|
|
6433b1dfd6 | ||
|
|
bea51d948d | ||
|
|
6d2f9428c5 | ||
|
|
4991620f89 | ||
|
|
916438a9de | ||
|
|
40571c073f | ||
|
|
14c825bd1c | ||
|
|
5d4715cc6a | ||
|
|
87d6588cb5 | ||
|
|
0c81b83ca9 | ||
|
|
8bc3d66354 | ||
|
|
19a8f03ad5 | ||
|
|
b377da042b | ||
|
|
28281ae250 | ||
|
|
7dbdda908c | ||
|
|
368b3ee227 | ||
|
|
1defd0847a | ||
|
|
80fece4e08 | ||
|
|
661483f313 | ||
|
|
ff3aa5d380 | ||
|
|
0dc4ab42cc | ||
|
|
453560fb3a | ||
|
|
c8a4967c9d | ||
|
|
1b9e08ab78 | ||
|
|
1e191cc837 | ||
|
|
5dbda22659 | ||
|
|
5836530a87 | ||
|
|
868c397cb6 | ||
|
|
17badbc442 | ||
|
|
d7f5e55f8e | ||
|
|
64fad8cfe9 | ||
|
|
29ccc7673f | ||
|
|
c243932b41 | ||
|
|
1279c7ce7a | ||
|
|
c3e201a829 | ||
|
|
d5984284ed | ||
|
|
10b0ab7926 | ||
|
|
82fa9f8d56 | ||
|
|
51cddcb8b8 | ||
|
|
2ddd83cdfe | ||
|
|
8b95bf041d | ||
|
|
93cb783853 | ||
|
|
d5e0923e3d | ||
|
|
d46ca5a015 | ||
|
|
46183294b2 | ||
|
|
f9653a4417 | ||
|
|
54ea3c47c8 | ||
|
|
5836786246 | ||
|
|
51a7681957 | ||
|
|
d6d1a8e02c | ||
|
|
89df483567 | ||
|
|
a5750f437d | ||
|
|
ccb439efb0 | ||
|
|
0b47f7a46b | ||
|
|
79316be18c | ||
|
|
ec100ca4db | ||
|
|
873ad1272e | ||
|
|
8cb683f3b9 | ||
|
|
5d29d2111c | ||
|
|
ac3b4f918f | ||
|
|
9b023a56a3 | ||
|
|
f3db273753 | ||
|
|
2e1b998d5e | ||
|
|
37bec068c2 | ||
|
|
df6427d30b | ||
|
|
c96930fd9d | ||
|
|
292dd642ce | ||
|
|
761206cf81 | ||
|
|
1c773c0869 | ||
|
|
69b46dd607 | ||
|
|
c918c6480f | ||
|
|
37194dd4e9 | ||
|
|
4de079b256 | ||
|
|
8941cdb7d2 | ||
|
|
dfee6321cd | ||
|
|
0195038c07 | ||
|
|
ac3ec5ed13 | ||
|
|
cdb36aef9e | ||
|
|
5e9b77129f | ||
|
|
2d47a5fd41 | ||
|
|
3802474483 | ||
|
|
d1a2b3fb18 | ||
|
|
b1657b8c6b | ||
|
|
ec8548b414 | ||
|
|
4e94d0d53a | ||
|
|
bab9cae71f | ||
|
|
6d6115475b | ||
|
|
b06d6e3646 | ||
|
|
5fe55b16a1 | ||
|
|
5329834376 | ||
|
|
52f13f2339 | ||
|
|
e94dd7e2c4 | ||
|
|
ce5fcb6bb2 | ||
|
|
20aad9e01a | ||
|
|
6f41763061 | ||
|
|
05a703e15d | ||
|
|
0e54aa17e6 | ||
|
|
2de124e223 | ||
|
|
aeb100cffe | ||
|
|
f1d633cca7 | ||
|
|
6057dc46e5 | ||
|
|
deff708cbe | ||
|
|
a9cfe06aaf | ||
|
|
009bdb3558 | ||
|
|
a44ff5ed31 | ||
|
|
00c6254129 | ||
|
|
e15039372e | ||
|
|
0eb6c6cd83 | ||
|
|
51e6f8271a | ||
|
|
6b7320add4 | ||
|
|
215cfbb757 | ||
|
|
97dd67ad1c | ||
|
|
607bb8d14b | ||
|
|
b57ba7bfb6 | ||
|
|
3415890dd5 | ||
|
|
4bd74ed4c7 | ||
|
|
f782aecf4d | ||
|
|
09fa1d6a73 | ||
|
|
45c5b084fd | ||
|
|
edcbd47800 | ||
|
|
5cd051eced | ||
|
|
12f3b13995 | ||
|
|
3ef35207c1 | ||
|
|
5d2f18fbcd | ||
|
|
3954f14c6d | ||
|
|
9ae6224f12 | ||
|
|
a58d57a60d | ||
|
|
24cabf5e2f | ||
|
|
7234f436aa | ||
|
|
4c5f5c9bf3 | ||
|
|
8a00a0ade6 | ||
|
|
43f0b42088 | ||
|
|
23aabe85e6 | ||
|
|
69af6ada2f | ||
|
|
9e7a1f1351 | ||
|
|
ce0712bf95 | ||
|
|
bcc5c4403a | ||
|
|
2dce2be138 | ||
|
|
0791082b43 | ||
|
|
5933667cb8 | ||
|
|
e31cb50405 | ||
|
|
3373149fdc | ||
|
|
0e122c13ad | ||
|
|
eea5122d1b | ||
|
|
b8fbf6969c | ||
|
|
feac654ba0 | ||
|
|
5cb1a343d1 | ||
|
|
0dc234c5ea | ||
|
|
716ae72aac | ||
|
|
d637114c17 | ||
|
|
7e5f595b31 | ||
|
|
88959b0047 | ||
|
|
dd05c7ec79 | ||
|
|
53a04d6b5d | ||
|
|
c67c25db05 | ||
|
|
1bdb756d28 | ||
|
|
d4ae0ae0e9 | ||
|
|
9b492430bb | ||
|
|
ed4d1e2ade | ||
|
|
b1b4f2337e | ||
|
|
165d8485f0 | ||
|
|
960500cfd2 | ||
|
|
8fd921557f | ||
|
|
4d3be1816c | ||
|
|
357d79fb6e | ||
|
|
d2c0c94f0b | ||
|
|
b1326d9230 | ||
|
|
bc59ca92b6 | ||
|
|
b9b7e4f915 | ||
|
|
ccce6cb3be | ||
|
|
4756cb203e | ||
|
|
8d3e06349e | ||
|
|
9e29e36a78 | ||
|
|
c10a37e5b6 | ||
|
|
7e5d0f1fe3 | ||
|
|
39d356782e | ||
|
|
d58a609ae4 | ||
|
|
493263f415 | ||
|
|
a3ccac3eb7 | ||
|
|
8dbfa4e1a4 | ||
|
|
e18ee8d681 | ||
|
|
a6e6cd5788 | ||
|
|
9dc69fa07c | ||
|
|
a1e7360273 | ||
|
|
f95602f152 | ||
|
|
c277d7d171 | ||
|
|
f6d4a289d5 | ||
|
|
f2f346e110 | ||
|
|
414a87a4f4 | ||
|
|
e6a896c4bd | ||
|
|
63419e144f | ||
|
|
2c375013dd | ||
|
|
b126267442 | ||
|
|
2a928d7492 | ||
|
|
7fbeb489d3 | ||
|
|
37d672bf08 | ||
|
|
1c8de85045 | ||
|
|
94af77aa7c | ||
|
|
677a8b208d | ||
|
|
c2f83c04cb | ||
|
|
fad38ec6e8 | ||
|
|
defa826c53 | ||
|
|
69aaad9b96 | ||
|
|
edd8208779 | ||
|
|
7cf34c3637 | ||
|
|
843ef8f2ec | ||
|
|
cf9767c608 | ||
|
|
424bffcd3f | ||
|
|
16aadcc354 | ||
|
|
395997178b | ||
|
|
5842a767a9 | ||
|
|
774d7eab64 | ||
|
|
88089c8754 | ||
|
|
1ea6bdef05 | ||
|
|
9abb23cd27 | ||
|
|
25f650e075 | ||
|
|
d39dfdf45c | ||
|
|
ece0ae2bfb | ||
|
|
7b4a213603 | ||
|
|
44f7067cab | ||
|
|
756225c8ff | ||
|
|
7bc3e80399 | ||
|
|
4b9e1b6586 | ||
|
|
b7ef581c6e | ||
|
|
24cae76d16 | ||
|
|
c2ad1243ba | ||
|
|
63fd1bb503 | ||
|
|
c0870315fd | ||
|
|
9705f651b2 | ||
|
|
9423347c1b | ||
|
|
c042a89113 | ||
|
|
7b642c7781 | ||
|
|
2ccf85a910 | ||
|
|
979b602738 | ||
|
|
e46f0e084c | ||
|
|
2c2b586d86 | ||
|
|
c9e3abe206 | ||
|
|
eab041866b | ||
|
|
bc69cc1511 | ||
|
|
24c1bb3842 | ||
|
|
a19dc3bf00 | ||
|
|
d53b79ff5c |
@@ -15,5 +15,5 @@ mv "${REV_NAME}-source.tar.xz" $RELEASE_NAME
|
||||
7z a "$REV_NAME.7z" $RELEASE_NAME
|
||||
|
||||
# move the compiled archive into the artifacts directory to be uploaded by travis releases
|
||||
mv "$ARCHIVE_NAME" artifacts/
|
||||
mv "$REV_NAME.7z" artifacts/
|
||||
mv "$ARCHIVE_NAME" "${ARTIFACTS_DIR}/"
|
||||
mv "$REV_NAME.7z" "${ARTIFACTS_DIR}/"
|
||||
|
||||
@@ -2,5 +2,6 @@
|
||||
|
||||
GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`"
|
||||
GITREV="`git show -s --format='%h'`"
|
||||
ARTIFACTS_DIR="artifacts"
|
||||
|
||||
mkdir -p artifacts
|
||||
mkdir -p "${ARTIFACTS_DIR}/"
|
||||
|
||||
@@ -1,14 +1,49 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
# Exit on error, rather than continuing with the rest of the script.
|
||||
set -e
|
||||
|
||||
cd /yuzu
|
||||
|
||||
ccache -s
|
||||
|
||||
mkdir build || true && cd build
|
||||
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON
|
||||
cmake .. -DDISPLAY_VERSION=$1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DCMAKE_INSTALL_PREFIX="/usr"
|
||||
|
||||
ninja
|
||||
make -j$(nproc)
|
||||
|
||||
ccache -s
|
||||
|
||||
ctest -VV -C Release
|
||||
|
||||
make install DESTDIR=AppDir
|
||||
rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester
|
||||
|
||||
# Download tools needed to build an AppImage
|
||||
wget -nc https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
|
||||
wget -nc https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage
|
||||
wget -nc https://github.com/darealshinji/AppImageKit-checkrt/releases/download/continuous/AppRun-patched-x86_64
|
||||
wget -nc https://github.com/darealshinji/AppImageKit-checkrt/releases/download/continuous/exec-x86_64.so
|
||||
# Set executable bit
|
||||
chmod 755 \
|
||||
AppRun-patched-x86_64 \
|
||||
exec-x86_64.so \
|
||||
linuxdeploy-x86_64.AppImage \
|
||||
linuxdeploy-plugin-qt-x86_64.AppImage
|
||||
|
||||
# Workaround for https://github.com/AppImage/AppImageKit/issues/828
|
||||
export APPIMAGE_EXTRACT_AND_RUN=1
|
||||
|
||||
mkdir -p AppDir/usr/optional
|
||||
mkdir -p AppDir/usr/optional/libstdc++
|
||||
mkdir -p AppDir/usr/optional/libgcc_s
|
||||
|
||||
# Deploy yuzu's needed dependencies
|
||||
./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt
|
||||
|
||||
# Workaround for building yuzu with GCC 10 but also trying to distribute it to Ubuntu 18.04 et al.
|
||||
# See https://github.com/darealshinji/AppImageKit-checkrt
|
||||
cp exec-x86_64.so AppDir/usr/optional/exec.so
|
||||
cp AppRun-patched-x86_64 AppDir/AppRun
|
||||
cp --dereference /usr/lib/x86_64-linux-gnu/libstdc++.so.6 AppDir/usr/optional/libstdc++/libstdc++.so.6
|
||||
cp --dereference /lib/x86_64-linux-gnu/libgcc_s.so.1 AppDir/usr/optional/libgcc_s/libgcc_s.so.1
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
. .ci/scripts/common/pre-upload.sh
|
||||
|
||||
APPIMAGE_NAME="yuzu-${GITDATE}-${GITREV}.AppImage"
|
||||
REV_NAME="yuzu-linux-${GITDATE}-${GITREV}"
|
||||
ARCHIVE_NAME="${REV_NAME}.tar.xz"
|
||||
COMPRESSION_FLAGS="-cJvf"
|
||||
@@ -17,4 +18,24 @@ mkdir "$DIR_NAME"
|
||||
cp build/bin/yuzu-cmd "$DIR_NAME"
|
||||
cp build/bin/yuzu "$DIR_NAME"
|
||||
|
||||
# Build an AppImage
|
||||
cd build
|
||||
|
||||
wget -nc https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
|
||||
chmod 755 appimagetool-x86_64.AppImage
|
||||
|
||||
if [ "${RELEASE_NAME}" = "mainline" ]; then
|
||||
# Generate update information if releasing to mainline
|
||||
./appimagetool-x86_64.AppImage -u "gh-releases-zsync|yuzu-emu|yuzu-${RELEASE_NAME}|latest|yuzu-*.AppImage.zsync" AppDir "${APPIMAGE_NAME}"
|
||||
else
|
||||
./appimagetool-x86_64.AppImage AppDir "${APPIMAGE_NAME}"
|
||||
fi
|
||||
cd ..
|
||||
|
||||
# Copy the AppImage and update info to the artifacts directory and avoid compressing it
|
||||
cp "build/${APPIMAGE_NAME}" "${ARTIFACTS_DIR}/"
|
||||
if [ -f "build/${APPIMAGE_NAME}.zsync" ]; then
|
||||
cp "build/${APPIMAGE_NAME}.zsync" "${ARTIFACTS_DIR}/"
|
||||
fi
|
||||
|
||||
. .ci/scripts/common/post-upload.sh
|
||||
|
||||
@@ -5,7 +5,7 @@ cd /yuzu
|
||||
ccache -s
|
||||
|
||||
mkdir build || true && cd build
|
||||
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON
|
||||
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON
|
||||
ninja
|
||||
|
||||
ccache -s
|
||||
@@ -42,3 +42,8 @@ done
|
||||
pip3 install pefile
|
||||
python3 .ci/scripts/windows/scan_dll.py package/*.exe "package/"
|
||||
python3 .ci/scripts/windows/scan_dll.py package/imageformats/*.dll "package/"
|
||||
|
||||
# copy FFmpeg libraries
|
||||
EXTERNALS_PATH="$(pwd)/build/externals"
|
||||
FFMPEG_DLL_PATH="$(find ${EXTERNALS_PATH} -maxdepth 1 -type d | grep ffmpeg)/bin"
|
||||
find ${FFMPEG_DLL_PATH} -type f -regex ".*\.dll" -exec cp -v {} package/ ';'
|
||||
|
||||
@@ -8,7 +8,7 @@ steps:
|
||||
displayName: 'Install vulkan-sdk'
|
||||
- script: python -m pip install --upgrade pip conan
|
||||
displayName: 'Install conan'
|
||||
- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cd ..
|
||||
- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cd ..
|
||||
displayName: 'Configure CMake'
|
||||
- task: MSBuild@1
|
||||
displayName: 'Build'
|
||||
|
||||
3
.gitmodules
vendored
@@ -37,3 +37,6 @@
|
||||
[submodule "opus"]
|
||||
path = externals/opus/opus
|
||||
url = https://github.com/xiph/opus.git
|
||||
[submodule "externals/ffmpeg"]
|
||||
path = externals/ffmpeg
|
||||
url = https://git.ffmpeg.org/ffmpeg.git
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
mkdir -p "$HOME/.ccache"
|
||||
docker run -e ENABLE_COMPATIBILITY_REPORTING --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.travis/linux/docker.sh
|
||||
docker run -e ENABLE_COMPATIBILITY_REPORTING --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/home/yuzu/.ccache yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.travis/linux/docker.sh
|
||||
|
||||
189
CMakeLists.txt
@@ -18,16 +18,20 @@ CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" ON "EN
|
||||
|
||||
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
|
||||
|
||||
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled yuzu" ON "WIN32" OFF)
|
||||
|
||||
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
|
||||
|
||||
option(YUZU_ENABLE_BOXCAT "Enable the Boxcat service, a yuzu high-level implementation of BCAT" ON)
|
||||
|
||||
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
|
||||
|
||||
option(ENABLE_VULKAN "Enables Vulkan backend" ON)
|
||||
|
||||
option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
|
||||
|
||||
if (NOT ENABLE_WEB_SERVICE)
|
||||
set(YUZU_ENABLE_BOXCAT OFF)
|
||||
endif()
|
||||
|
||||
# Default to a Release build
|
||||
get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE)
|
||||
@@ -113,6 +117,9 @@ if (NOT DEFINED ARCHITECTURE)
|
||||
endif()
|
||||
message(STATUS "Target architecture: ${ARCHITECTURE}")
|
||||
|
||||
if (UNIX)
|
||||
add_definitions(-DYUZU_UNIX=1)
|
||||
endif()
|
||||
|
||||
# Configure C++ standard
|
||||
# ===========================
|
||||
@@ -157,7 +164,6 @@ macro(yuzu_find_packages)
|
||||
# Capitalization matters here. We need the naming to match the generated paths from Conan
|
||||
set(REQUIRED_LIBS
|
||||
# Cmake Pkg Prefix Version Conan Pkg
|
||||
"Boost 1.73 boost/1.73.0"
|
||||
"Catch2 2.13 catch2/2.13.0"
|
||||
"fmt 7.1 fmt/7.1.2"
|
||||
# can't use until https://github.com/bincrafters/community/issues/1173
|
||||
@@ -165,7 +171,7 @@ macro(yuzu_find_packages)
|
||||
"lz4 1.8 lz4/1.9.2"
|
||||
"nlohmann_json 3.8 nlohmann_json/3.8.0"
|
||||
"ZLIB 1.2 zlib/1.2.11"
|
||||
"zstd 1.4 zstd/1.4.5"
|
||||
"zstd 1.4 zstd/1.4.8"
|
||||
)
|
||||
|
||||
foreach(PACKAGE ${REQUIRED_LIBS})
|
||||
@@ -192,6 +198,22 @@ macro(yuzu_find_packages)
|
||||
unset(FN_FORCE_REQUIRED)
|
||||
endmacro()
|
||||
|
||||
find_package(Boost 1.73.0 COMPONENTS context headers QUIET)
|
||||
if (Boost_FOUND)
|
||||
set(Boost_LIBRARIES Boost::boost)
|
||||
# Conditionally add Boost::context only if the active version of the Conan or system Boost package provides it
|
||||
# The old version is missing Boost::context, so we want to avoid adding in that case
|
||||
# The new version requires adding Boost::context to prevent linking issues
|
||||
#
|
||||
# This one is used by Conan on subsequent CMake configures, not the first configure.
|
||||
if (TARGET Boost::context)
|
||||
list(APPEND Boost_LIBRARIES Boost::context)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Boost 1.73.0 or newer not found, falling back to Conan")
|
||||
list(APPEND CONAN_REQUIRED_LIBS "boost/1.73.0")
|
||||
endif()
|
||||
|
||||
# Attempt to locate any packages that are required and report the missing ones in CONAN_REQUIRED_LIBS
|
||||
yuzu_find_packages()
|
||||
|
||||
@@ -223,7 +245,7 @@ if(ENABLE_QT)
|
||||
if (YUZU_USE_QT_WEB_ENGINE)
|
||||
find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets)
|
||||
endif()
|
||||
|
||||
|
||||
if (ENABLE_QT_TRANSLATION)
|
||||
find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT})
|
||||
endif()
|
||||
@@ -241,7 +263,7 @@ if(ENABLE_SDL2)
|
||||
find_package(SDL2)
|
||||
if (NOT SDL2_FOUND)
|
||||
# otherwise add this to the list of libraries to install
|
||||
list(APPEND CONAN_REQUIRED_LIBS "sdl2/2.0.12@bincrafters/stable")
|
||||
list(APPEND CONAN_REQUIRED_LIBS "sdl2/2.0.14@bincrafters/stable")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -296,6 +318,17 @@ if (CONAN_REQUIRED_LIBS)
|
||||
# this time with required, so we bail if its not found.
|
||||
yuzu_find_packages(FORCE_REQUIRED)
|
||||
|
||||
if (NOT Boost_FOUND)
|
||||
find_package(Boost 1.73.0 REQUIRED COMPONENTS context headers)
|
||||
set(Boost_LIBRARIES Boost::boost)
|
||||
# Conditionally add Boost::context only if the active version of the Conan Boost package provides it
|
||||
# The old version is missing Boost::context, so we want to avoid adding in that case
|
||||
# The new version requires adding Boost::context to prevent linking issues
|
||||
if (TARGET Boost::context)
|
||||
list(APPEND Boost_LIBRARIES Boost::context)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Due to issues with variable scopes in functions, we need to also find_package(qt5) outside of the function
|
||||
if(ENABLE_QT)
|
||||
list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}")
|
||||
@@ -353,19 +386,141 @@ if (NOT LIBUSB_FOUND)
|
||||
set(LIBUSB_LIBRARIES usb)
|
||||
endif()
|
||||
|
||||
# Use system installed ffmpeg.
|
||||
if (NOT MSVC)
|
||||
find_package(FFmpeg REQUIRED)
|
||||
else()
|
||||
set(FFMPEG_EXT_NAME "ffmpeg-4.2.1")
|
||||
set(FFMPEG_PATH "${CMAKE_BINARY_DIR}/externals/${FFMPEG_EXT_NAME}")
|
||||
download_bundled_external("ffmpeg/" ${FFMPEG_EXT_NAME} "")
|
||||
set(FFMPEG_FOUND YES)
|
||||
set(FFMPEG_INCLUDE_DIR "${FFMPEG_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE)
|
||||
set(FFMPEG_LIBRARY_DIR "${FFMPEG_PATH}/bin" CACHE PATH "Path to FFmpeg library" FORCE)
|
||||
set(FFMPEG_DLL_DIR "${FFMPEG_PATH}/bin" CACHE PATH "Path to FFmpeg dll's" FORCE)
|
||||
# List of all FFmpeg components required
|
||||
set(FFmpeg_COMPONENTS
|
||||
avcodec
|
||||
avutil
|
||||
swscale)
|
||||
|
||||
if (NOT YUZU_USE_BUNDLED_FFMPEG)
|
||||
# Use system installed FFmpeg
|
||||
find_package(FFmpeg REQUIRED COMPONENTS ${FFmpeg_COMPONENTS})
|
||||
|
||||
if (FFmpeg_FOUND)
|
||||
# Overwrite aggregate defines from FFmpeg module to avoid over-linking libraries.
|
||||
# Prevents shipping too many libraries with the AppImage.
|
||||
set(FFmpeg_LIBRARIES "")
|
||||
set(FFmpeg_INCLUDE_DIR "")
|
||||
|
||||
foreach(COMPONENT ${FFmpeg_COMPONENTS})
|
||||
set(FFmpeg_LIBRARIES ${FFmpeg_LIBRARIES} ${FFmpeg_LIBRARY_${COMPONENT}} CACHE PATH "Paths to FFmpeg libraries" FORCE)
|
||||
set(FFmpeg_INCLUDE_DIR ${FFmpeg_INCLUDE_DIR} ${FFmpeg_INCLUDE_${COMPONENT}} CACHE PATH "Path to FFmpeg headers" FORCE)
|
||||
endforeach()
|
||||
else()
|
||||
message(WARNING "FFmpeg not found, falling back to externals")
|
||||
set(YUZU_USE_BUNDLED_FFMPEG ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (YUZU_USE_BUNDLED_FFMPEG)
|
||||
if (NOT WIN32)
|
||||
# Build FFmpeg from externals
|
||||
message(STATUS "Using FFmpeg from externals")
|
||||
|
||||
# FFmpeg has source that requires one of nasm or yasm to assemble it.
|
||||
# REQUIRED throws an error if not found here during configuration rather than during compilation.
|
||||
find_program(ASSEMBLER NAMES nasm yasm REQUIRED)
|
||||
|
||||
set(FFmpeg_PREFIX ${PROJECT_SOURCE_DIR}/externals/ffmpeg)
|
||||
set(FFmpeg_BUILD_DIR ${PROJECT_BINARY_DIR}/externals/ffmpeg)
|
||||
set(FFmpeg_MAKEFILE ${FFmpeg_BUILD_DIR}/Makefile)
|
||||
make_directory(${FFmpeg_BUILD_DIR})
|
||||
|
||||
# Read version string from external
|
||||
file(READ ${FFmpeg_PREFIX}/RELEASE FFmpeg_VERSION)
|
||||
set(FFmpeg_FOUND NO)
|
||||
if (NOT FFmpeg_VERSION STREQUAL "")
|
||||
set(FFmpeg_FOUND YES)
|
||||
endif()
|
||||
|
||||
foreach(COMPONENT ${FFmpeg_COMPONENTS})
|
||||
set(FFmpeg_${COMPONENT}_PREFIX "${FFmpeg_BUILD_DIR}/lib${COMPONENT}")
|
||||
set(FFmpeg_${COMPONENT}_LIB_NAME "lib${COMPONENT}.a")
|
||||
set(FFmpeg_${COMPONENT}_LIBRARY "${FFmpeg_${COMPONENT}_PREFIX}/${FFmpeg_${COMPONENT}_LIB_NAME}")
|
||||
|
||||
set(FFmpeg_LIBRARIES
|
||||
${FFmpeg_LIBRARIES}
|
||||
${FFmpeg_${COMPONENT}_LIBRARY}
|
||||
CACHE PATH "Paths to FFmpeg libraries" FORCE)
|
||||
endforeach()
|
||||
|
||||
set(FFmpeg_INCLUDE_DIR
|
||||
${FFmpeg_PREFIX}
|
||||
CACHE PATH "Path to FFmpeg headers" FORCE)
|
||||
|
||||
# `configure` parameters builds only exactly what yuzu needs from FFmpeg
|
||||
# `--disable-{vaapi,vdpau}` is needed to avoid linking issues
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${FFmpeg_MAKEFILE}
|
||||
COMMAND
|
||||
/bin/bash ${FFmpeg_PREFIX}/configure
|
||||
--disable-avdevice
|
||||
--disable-avfilter
|
||||
--disable-avformat
|
||||
--disable-doc
|
||||
--disable-everything
|
||||
--disable-ffmpeg
|
||||
--disable-ffprobe
|
||||
--disable-network
|
||||
--disable-postproc
|
||||
--disable-swresample
|
||||
--disable-vaapi
|
||||
--disable-vdpau
|
||||
--enable-decoder=h264
|
||||
--enable-decoder=vp9
|
||||
WORKING_DIRECTORY
|
||||
${FFmpeg_BUILD_DIR}
|
||||
)
|
||||
|
||||
# Workaround for Ubuntu 18.04's older version of make not being able to call make as a child
|
||||
# with context of the jobserver. Also helps ninja users.
|
||||
execute_process(
|
||||
COMMAND
|
||||
nproc
|
||||
OUTPUT_VARIABLE
|
||||
SYSTEM_THREADS)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${FFmpeg_LIBRARIES}
|
||||
COMMAND
|
||||
make -j${SYSTEM_THREADS}
|
||||
WORKING_DIRECTORY
|
||||
${FFmpeg_BUILD_DIR}
|
||||
)
|
||||
|
||||
# ALL makes this custom target build every time
|
||||
# but it won't actually build if the DEPENDS parameter is up to date
|
||||
add_custom_target(ffmpeg-build ALL DEPENDS ${FFmpeg_LIBRARIES})
|
||||
add_custom_target(ffmpeg-configure ALL DEPENDS ${FFmpeg_MAKEFILE})
|
||||
|
||||
if (FFmpeg_FOUND)
|
||||
message(STATUS "Found FFmpeg version ${FFmpeg_VERSION}")
|
||||
|
||||
add_dependencies(ffmpeg-build ffmpeg-configure)
|
||||
else()
|
||||
message(FATAL_ERROR "FFmpeg not found")
|
||||
endif()
|
||||
else() # WIN32
|
||||
# Use yuzu FFmpeg binaries
|
||||
set(FFmpeg_EXT_NAME "ffmpeg-4.2.1")
|
||||
set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}")
|
||||
download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "")
|
||||
set(FFmpeg_FOUND YES)
|
||||
set(FFmpeg_INCLUDE_DIR "${FFmpeg_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE)
|
||||
set(FFmpeg_LIBRARY_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg library directory" FORCE)
|
||||
set(FFmpeg_DLL_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg dll's" FORCE)
|
||||
set(FFmpeg_LIBRARIES
|
||||
${FFmpeg_LIBRARY_DIR}/swscale.lib
|
||||
${FFmpeg_LIBRARY_DIR}/avcodec.lib
|
||||
${FFmpeg_LIBRARY_DIR}/avutil.lib
|
||||
CACHE PATH "Paths to FFmpeg libraries" FORCE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
unset(FFmpeg_COMPONENTS)
|
||||
|
||||
# Prefer the -pthread flag on Linux.
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
function(copy_yuzu_FFmpeg_deps target_dir)
|
||||
include(WindowsCopyFiles)
|
||||
set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
|
||||
windows_copy_files(${target_dir} ${FFMPEG_DLL_DIR} ${DLL_DEST}
|
||||
windows_copy_files(${target_dir} ${FFmpeg_DLL_DIR} ${DLL_DEST}
|
||||
avcodec-58.dll
|
||||
avutil-56.dll
|
||||
swresample-3.dll
|
||||
|
||||
@@ -30,11 +30,10 @@ If you want to contribute to the user interface translation, please check out th
|
||||
|
||||
* __Windows__: [Windows Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Windows)
|
||||
* __Linux__: [Linux Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Linux)
|
||||
* __macOS__: [macOS Build](https://github.com/yuzu-emu/yuzu/wiki/Building-for-macOS)
|
||||
|
||||
|
||||
### Support
|
||||
We happily accept monetary donations or donated games and hardware. Please see our [donations page](https://yuzu-emu.org/donate/) for more information on how you can contribute to yuzu. Any donations received will go towards things like:
|
||||
We happily accept monetary donations, or donated games and hardware. Please see our [donations page](https://yuzu-emu.org/donate/) for more information on how you can contribute to yuzu. Any donations received will go towards things like:
|
||||
* Switch consoles to explore and reverse-engineer the hardware
|
||||
* Switch games for testing, reverse-engineering, and implementing new features
|
||||
* Web hosting and infrastructure setup
|
||||
|
||||
21
dist/icons/controller/controller.qrc
vendored
@@ -1,26 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="controller">
|
||||
<file alias="dual_joycon">dual_joycon.png</file>
|
||||
<file alias="dual_joycon_dark">dual_joycon_dark.png</file>
|
||||
<file alias="dual_joycon_midnight">dual_joycon_midnight.png</file>
|
||||
<file alias="handheld">handheld.png</file>
|
||||
<file alias="handheld_dark">handheld_dark.png</file>
|
||||
<file alias="handheld_midnight">handheld_midnight.png</file>
|
||||
<file alias="pro_controller">pro_controller.png</file>
|
||||
<file alias="pro_controller_dark">pro_controller_dark.png</file>
|
||||
<file alias="pro_controller_midnight">pro_controller_midnight.png</file>
|
||||
<file alias="single_joycon_left">single_joycon_left.png</file>
|
||||
<file alias="single_joycon_left_dark">single_joycon_left_dark.png</file>
|
||||
<file alias="single_joycon_left_midnight">single_joycon_left_midnight.png</file>
|
||||
<file alias="single_joycon_right">single_joycon_right.png</file>
|
||||
<file alias="single_joycon_right_dark">single_joycon_right_dark.png</file>
|
||||
<file alias="single_joycon_right_midnight">single_joycon_right_midnight.png</file>
|
||||
<file alias="single_joycon_left_vertical">single_joycon_left_vertical.png</file>
|
||||
<file alias="single_joycon_left_vertical_dark">single_joycon_left_vertical_dark.png</file>
|
||||
<file alias="single_joycon_left_vertical_midnight">single_joycon_left_vertical_midnight.png</file>
|
||||
<file alias="single_joycon_right_vertical">single_joycon_right_vertical.png</file>
|
||||
<file alias="single_joycon_right_vertical_dark">single_joycon_right_vertical_dark.png</file>
|
||||
<file alias="single_joycon_right_vertical_midnight">single_joycon_right_vertical_midnight.png</file>
|
||||
<file alias="applet_dual_joycon">applet_dual_joycon.png</file>
|
||||
<file alias="applet_dual_joycon_dark">applet_dual_joycon_dark.png</file>
|
||||
<file alias="applet_dual_joycon_midnight">applet_dual_joycon_midnight.png</file>
|
||||
|
||||
BIN
dist/icons/controller/dual_joycon.png
vendored
|
Before Width: | Height: | Size: 36 KiB |
BIN
dist/icons/controller/dual_joycon_dark.png
vendored
|
Before Width: | Height: | Size: 35 KiB |
BIN
dist/icons/controller/dual_joycon_midnight.png
vendored
|
Before Width: | Height: | Size: 34 KiB |
BIN
dist/icons/controller/handheld.png
vendored
|
Before Width: | Height: | Size: 14 KiB |
BIN
dist/icons/controller/handheld_dark.png
vendored
|
Before Width: | Height: | Size: 13 KiB |
BIN
dist/icons/controller/handheld_midnight.png
vendored
|
Before Width: | Height: | Size: 13 KiB |
BIN
dist/icons/controller/pro_controller.png
vendored
|
Before Width: | Height: | Size: 36 KiB |
BIN
dist/icons/controller/pro_controller_dark.png
vendored
|
Before Width: | Height: | Size: 34 KiB |
BIN
dist/icons/controller/pro_controller_midnight.png
vendored
|
Before Width: | Height: | Size: 35 KiB |
BIN
dist/icons/controller/single_joycon_left.png
vendored
|
Before Width: | Height: | Size: 25 KiB |
BIN
dist/icons/controller/single_joycon_left_dark.png
vendored
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 23 KiB |
BIN
dist/icons/controller/single_joycon_right.png
vendored
|
Before Width: | Height: | Size: 28 KiB |
BIN
dist/icons/controller/single_joycon_right_dark.png
vendored
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 26 KiB |
4
externals/CMakeLists.txt
vendored
@@ -61,9 +61,7 @@ if (USE_DISCORD_PRESENCE)
|
||||
endif()
|
||||
|
||||
# Sirit
|
||||
if (ENABLE_VULKAN)
|
||||
add_subdirectory(sirit)
|
||||
endif()
|
||||
add_subdirectory(sirit)
|
||||
|
||||
# libzip
|
||||
find_package(Libzip 1.5)
|
||||
|
||||
2
externals/cubeb
vendored
2
externals/dynarmic
vendored
1
externals/ffmpeg
vendored
Submodule
261
externals/find-modules/FindFFmpeg.cmake
vendored
@@ -1,100 +1,187 @@
|
||||
# - Try to find ffmpeg libraries (libavcodec, libavformat and libavutil)
|
||||
# Once done this will define
|
||||
# FindFFmpeg
|
||||
# ----------
|
||||
#
|
||||
# FFMPEG_FOUND - system has ffmpeg or libav
|
||||
# FFMPEG_INCLUDE_DIR - the ffmpeg include directory
|
||||
# FFMPEG_LIBRARIES - Link these to use ffmpeg
|
||||
# FFMPEG_LIBAVCODEC
|
||||
# FFMPEG_LIBAVFORMAT
|
||||
# FFMPEG_LIBAVUTIL
|
||||
# Copyright 2019 Citra Emulator Project
|
||||
# Licensed under GPLv2 or any later version
|
||||
#
|
||||
# Copyright (c) 2008 Andreas Schneider <mail@cynapses.org>
|
||||
# Modified for other libraries by Lasse Kärkkäinen <tronic>
|
||||
# Modified for Hedgewars by Stepik777
|
||||
# Modified for FFmpeg-example Tuukka Pasanen 2018
|
||||
# Modified for yuzu toastUnlimted 2020
|
||||
# Find the native FFmpeg includes and libraries
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the New
|
||||
# BSD license.
|
||||
# This module defines the following variables:
|
||||
#
|
||||
# FFmpeg_INCLUDE_<component>: where to find <component>.h
|
||||
# FFmpeg_LIBRARY_<component>: where to find the <component> library
|
||||
# FFmpeg_INCLUDE_DIR: aggregate all the include paths
|
||||
# FFmpeg_LIBRARIES: aggregate all the paths to the libraries
|
||||
# FFmpeg_FOUND: True if all components have been found
|
||||
#
|
||||
# This module defines the following targets, which are prefered over variables:
|
||||
#
|
||||
# FFmpeg::<component>: Target to use <component> directly, with include path,
|
||||
# library and dependencies set up. If you are using a static build, you are
|
||||
# responsible for adding any external dependencies (such as zlib, bzlib...).
|
||||
#
|
||||
# <component> can be one of:
|
||||
# avcodec
|
||||
# avdevice
|
||||
# avfilter
|
||||
# avformat
|
||||
# avutil
|
||||
# postproc
|
||||
# swresample
|
||||
# swscale
|
||||
#
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
find_package_handle_standard_args(FFMPEG
|
||||
FOUND_VAR FFMPEG_FOUND
|
||||
REQUIRED_VARS
|
||||
FFMPEG_LIBRARY
|
||||
FFMPEG_INCLUDE_DIR
|
||||
VERSION_VAR FFMPEG_VERSION
|
||||
set(_FFmpeg_ALL_COMPONENTS
|
||||
avcodec
|
||||
avdevice
|
||||
avfilter
|
||||
avformat
|
||||
avutil
|
||||
postproc
|
||||
swresample
|
||||
swscale
|
||||
)
|
||||
|
||||
if(FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
|
||||
# in cache already
|
||||
set(FFMPEG_FOUND TRUE)
|
||||
else()
|
||||
# use pkg-config to get the directories and then use these values
|
||||
# in the FIND_PATH() and FIND_LIBRARY() calls
|
||||
find_package(PkgConfig)
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(_FFMPEG_AVCODEC libavcodec)
|
||||
pkg_check_modules(_FFMPEG_AVUTIL libavutil)
|
||||
pkg_check_modules(_FFMPEG_SWSCALE libswscale)
|
||||
set(_FFmpeg_DEPS_avcodec avutil)
|
||||
set(_FFmpeg_DEPS_avdevice avcodec avformat avutil)
|
||||
set(_FFmpeg_DEPS_avfilter avutil)
|
||||
set(_FFmpeg_DEPS_avformat avcodec avutil)
|
||||
set(_FFmpeg_DEPS_postproc avutil)
|
||||
set(_FFmpeg_DEPS_swresample avutil)
|
||||
set(_FFmpeg_DEPS_swscale avutil)
|
||||
|
||||
function(find_ffmpeg LIBNAME)
|
||||
if(DEFINED ENV{FFMPEG_DIR})
|
||||
set(FFMPEG_DIR $ENV{FFMPEG_DIR})
|
||||
endif()
|
||||
|
||||
find_path(FFMPEG_AVCODEC_INCLUDE_DIR
|
||||
NAMES libavcodec/avcodec.h
|
||||
PATHS ${_FFMPEG_AVCODEC_INCLUDE_DIRS}
|
||||
/usr/include
|
||||
/usr/local/include
|
||||
/opt/local/include
|
||||
/sw/include
|
||||
PATH_SUFFIXES ffmpeg libav)
|
||||
|
||||
find_library(FFMPEG_LIBAVCODEC
|
||||
NAMES avcodec
|
||||
PATHS ${_FFMPEG_AVCODEC_LIBRARY_DIRS}
|
||||
/usr/lib
|
||||
/usr/local/lib
|
||||
/opt/local/lib
|
||||
/sw/lib)
|
||||
|
||||
find_library(FFMPEG_LIBAVUTIL
|
||||
NAMES avutil
|
||||
PATHS ${_FFMPEG_AVUTIL_LIBRARY_DIRS}
|
||||
/usr/lib
|
||||
/usr/local/lib
|
||||
/opt/local/lib
|
||||
/sw/lib)
|
||||
|
||||
find_library(FFMPEG_LIBSWSCALE
|
||||
NAMES swscale
|
||||
PATHS ${_FFMPEG_SWSCALE_LIBRARY_DIRS}
|
||||
/usr/lib
|
||||
/usr/local/lib
|
||||
/opt/local/lib
|
||||
/sw/lib)
|
||||
|
||||
if(FFMPEG_LIBAVCODEC AND FFMPEG_LIBAVUTIL AND FFMPEG_LIBSWSCALE)
|
||||
set(FFMPEG_FOUND TRUE)
|
||||
endif()
|
||||
|
||||
if(FFMPEG_FOUND)
|
||||
set(FFMPEG_INCLUDE_DIR ${FFMPEG_AVCODEC_INCLUDE_DIR})
|
||||
set(FFMPEG_LIBRARIES
|
||||
${FFMPEG_LIBAVCODEC}
|
||||
${FFMPEG_LIBAVUTIL}
|
||||
${FFMPEG_LIBSWSCALE})
|
||||
endif()
|
||||
|
||||
if(FFMPEG_FOUND)
|
||||
if(NOT FFMPEG_FIND_QUIETLY)
|
||||
message(STATUS
|
||||
"Found FFMPEG or Libav: ${FFMPEG_LIBRARIES}, ${FFMPEG_INCLUDE_DIR}")
|
||||
endif()
|
||||
if(FFMPEG_DIR)
|
||||
list(APPEND INCLUDE_PATHS
|
||||
${FFMPEG_DIR}
|
||||
${FFMPEG_DIR}/ffmpeg
|
||||
${FFMPEG_DIR}/lib${LIBNAME}
|
||||
${FFMPEG_DIR}/include/lib${LIBNAME}
|
||||
${FFMPEG_DIR}/include/ffmpeg
|
||||
${FFMPEG_DIR}/include
|
||||
NO_DEFAULT_PATH
|
||||
NO_CMAKE_FIND_ROOT_PATH
|
||||
)
|
||||
list(APPEND LIB_PATHS
|
||||
${FFMPEG_DIR}
|
||||
${FFMPEG_DIR}/lib
|
||||
${FFMPEG_DIR}/lib${LIBNAME}
|
||||
NO_DEFAULT_PATH
|
||||
NO_CMAKE_FIND_ROOT_PATH
|
||||
)
|
||||
else()
|
||||
if(FFMPEG_FIND_REQUIRED)
|
||||
message(FATAL_ERROR
|
||||
"Could not find libavcodec or libavutil or libswscale")
|
||||
list(APPEND INCLUDE_PATHS
|
||||
/usr/local/include/ffmpeg
|
||||
/usr/local/include/lib${LIBNAME}
|
||||
/usr/include/ffmpeg
|
||||
/usr/include/lib${LIBNAME}
|
||||
/usr/include/ffmpeg/lib${LIBNAME}
|
||||
)
|
||||
|
||||
list(APPEND LIB_PATHS
|
||||
/usr/local/lib
|
||||
/usr/lib
|
||||
)
|
||||
endif()
|
||||
|
||||
find_path(FFmpeg_INCLUDE_${LIBNAME} lib${LIBNAME}/${LIBNAME}.h
|
||||
HINTS ${INCLUDE_PATHS}
|
||||
)
|
||||
|
||||
find_library(FFmpeg_LIBRARY_${LIBNAME} ${LIBNAME}
|
||||
HINTS ${LIB_PATHS}
|
||||
)
|
||||
|
||||
if(NOT FFMPEG_DIR AND (NOT FFmpeg_LIBRARY_${LIBNAME} OR NOT FFmpeg_INCLUDE_${LIBNAME}))
|
||||
# Didn't find it in the usual paths, try pkg-config
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_check_modules(FFmpeg_PKGCONFIG_${LIBNAME} QUIET lib${LIBNAME})
|
||||
|
||||
find_path(FFmpeg_INCLUDE_${LIBNAME} lib${LIBNAME}/${LIBNAME}.h
|
||||
${FFmpeg_PKGCONFIG_${LIBNAME}_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
find_library(FFmpeg_LIBRARY_${LIBNAME} ${LIBNAME}
|
||||
${FFmpeg_PKGCONFIG_${LIBNAME}_LIBRARY_DIRS}
|
||||
)
|
||||
endif()
|
||||
|
||||
if(FFmpeg_INCLUDE_${LIBNAME} AND FFmpeg_LIBRARY_${LIBNAME})
|
||||
set(FFmpeg_INCLUDE_${LIBNAME} "${FFmpeg_INCLUDE_${LIBNAME}}" PARENT_SCOPE)
|
||||
set(FFmpeg_LIBRARY_${LIBNAME} "${FFmpeg_LIBRARY_${LIBNAME}}" PARENT_SCOPE)
|
||||
|
||||
# Extract FFmpeg version from version.h
|
||||
foreach(v MAJOR MINOR MICRO)
|
||||
set(FFmpeg_${LIBNAME}_VERSION_${v} 0)
|
||||
endforeach()
|
||||
string(TOUPPER ${LIBNAME} LIBNAME_UPPER)
|
||||
file(STRINGS "${FFmpeg_INCLUDE_${LIBNAME}}/lib${LIBNAME}/version.h" _FFmpeg_VERSION_H_CONTENTS REGEX "#define LIB${LIBNAME_UPPER}_VERSION_(MAJOR|MINOR|MICRO) ")
|
||||
set(_FFmpeg_VERSION_REGEX "([0-9]+)")
|
||||
foreach(v MAJOR MINOR MICRO)
|
||||
if("${_FFmpeg_VERSION_H_CONTENTS}" MATCHES "#define LIB${LIBNAME_UPPER}_VERSION_${v}[\\t ]+${_FFmpeg_VERSION_REGEX}")
|
||||
set(FFmpeg_${LIBNAME}_VERSION_${v} "${CMAKE_MATCH_1}")
|
||||
endif()
|
||||
endforeach()
|
||||
set(FFmpeg_${LIBNAME}_VERSION "${FFmpeg_${LIBNAME}_VERSION_MAJOR}.${FFmpeg_${LIBNAME}_VERSION_MINOR}.${FFmpeg_${LIBNAME}_VERSION_MICRO}")
|
||||
set(FFmpeg_${c}_VERSION "${FFmpeg_${LIBNAME}_VERSION}" PARENT_SCOPE)
|
||||
unset(_FFmpeg_VERSION_REGEX)
|
||||
unset(_FFmpeg_VERSION_H_CONTENTS)
|
||||
|
||||
set(FFmpeg_${c}_FOUND TRUE PARENT_SCOPE)
|
||||
if(NOT FFmpeg_FIND_QUIETLY)
|
||||
message("-- Found ${LIBNAME}: ${FFmpeg_INCLUDE_${LIBNAME}} ${FFmpeg_LIBRARY_${LIBNAME}} (version: ${FFmpeg_${LIBNAME}_VERSION})")
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
foreach(c ${_FFmpeg_ALL_COMPONENTS})
|
||||
find_ffmpeg(${c})
|
||||
endforeach()
|
||||
|
||||
foreach(c ${_FFmpeg_ALL_COMPONENTS})
|
||||
if(FFmpeg_${c}_FOUND)
|
||||
list(APPEND FFmpeg_INCLUDE_DIR ${FFmpeg_INCLUDE_${c}})
|
||||
list(APPEND FFmpeg_LIBRARIES ${FFmpeg_LIBRARY_${c}})
|
||||
|
||||
add_library(FFmpeg::${c} IMPORTED UNKNOWN)
|
||||
set_target_properties(FFmpeg::${c} PROPERTIES
|
||||
IMPORTED_LOCATION ${FFmpeg_LIBRARY_${c}}
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${FFmpeg_INCLUDE_${c}}
|
||||
)
|
||||
if(_FFmpeg_DEPS_${c})
|
||||
set(deps)
|
||||
foreach(dep ${_FFmpeg_DEPS_${c}})
|
||||
list(APPEND deps FFmpeg::${dep})
|
||||
endforeach()
|
||||
|
||||
set_target_properties(FFmpeg::${c} PROPERTIES
|
||||
INTERFACE_LINK_LIBRARIES "${deps}"
|
||||
)
|
||||
unset(deps)
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(FFmpeg_INCLUDE_DIR)
|
||||
list(REMOVE_DUPLICATES FFmpeg_INCLUDE_DIR)
|
||||
endif()
|
||||
|
||||
foreach(c ${FFmpeg_FIND_COMPONENTS})
|
||||
list(APPEND _FFmpeg_REQUIRED_VARS FFmpeg_INCLUDE_${c} FFmpeg_LIBRARY_${c})
|
||||
endforeach()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(FFmpeg
|
||||
REQUIRED_VARS ${_FFmpeg_REQUIRED_VARS}
|
||||
HANDLE_COMPONENTS
|
||||
)
|
||||
|
||||
foreach(c ${_FFmpeg_ALL_COMPONENTS})
|
||||
unset(_FFmpeg_DEPS_${c})
|
||||
endforeach()
|
||||
unset(_FFmpeg_ALL_COMPONENTS)
|
||||
unset(_FFmpeg_REQUIRED_VARS)
|
||||
|
||||
@@ -45,10 +45,15 @@ if (MSVC)
|
||||
|
||||
# Warnings
|
||||
/W3
|
||||
/we4062 # enumerator 'identifier' in a switch of enum 'enumeration' is not handled
|
||||
/we4101 # 'identifier': unreferenced local variable
|
||||
/we4265 # 'class': class has virtual functions, but destructor is not virtual
|
||||
/we4388 # signed/unsigned mismatch
|
||||
/we4547 # 'operator' : operator before comma has no effect; expected operator with side-effect
|
||||
/we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
|
||||
/we4555 # Expression has no effect; expected expression with side-effect
|
||||
/we4834 # Discarding return value of function with 'nodiscard' attribute
|
||||
/we5038 # data member 'member1' will be initialized after data member 'member2'
|
||||
)
|
||||
|
||||
# /GS- - No stack buffer overflow checks
|
||||
@@ -59,10 +64,16 @@ if (MSVC)
|
||||
else()
|
||||
add_compile_options(
|
||||
-Wall
|
||||
-Werror=array-bounds
|
||||
-Werror=implicit-fallthrough
|
||||
-Werror=missing-declarations
|
||||
-Werror=missing-field-initializers
|
||||
-Werror=reorder
|
||||
-Werror=switch
|
||||
-Werror=uninitialized
|
||||
-Werror=unused-function
|
||||
-Werror=unused-result
|
||||
-Werror=unused-variable
|
||||
-Wextra
|
||||
-Wmissing-declarations
|
||||
-Wno-attributes
|
||||
@@ -121,7 +132,6 @@ add_subdirectory(tests)
|
||||
|
||||
if (ENABLE_SDL2)
|
||||
add_subdirectory(yuzu_cmd)
|
||||
add_subdirectory(yuzu_tester)
|
||||
endif()
|
||||
|
||||
if (ENABLE_QT)
|
||||
|
||||
@@ -51,6 +51,8 @@ if (NOT MSVC)
|
||||
-Werror=implicit-fallthrough
|
||||
-Werror=reorder
|
||||
-Werror=sign-compare
|
||||
-Werror=shadow
|
||||
-Werror=unused-parameter
|
||||
-Werror=unused-variable
|
||||
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
|
||||
|
||||
@@ -31,8 +31,8 @@ Filter Filter::LowPass(double cutoff, double Q) {
|
||||
|
||||
Filter::Filter() : Filter(1.0, 0.0, 0.0, 1.0, 0.0, 0.0) {}
|
||||
|
||||
Filter::Filter(double a0, double a1, double a2, double b0, double b1, double b2)
|
||||
: a1(a1 / a0), a2(a2 / a0), b0(b0 / a0), b1(b1 / a0), b2(b2 / a0) {}
|
||||
Filter::Filter(double a0_, double a1_, double a2_, double b0_, double b1_, double b2_)
|
||||
: a1(a1_ / a0_), a2(a2_ / a0_), b0(b0_ / a0_), b1(b1_ / a0_), b2(b2_ / a0_) {}
|
||||
|
||||
void Filter::Process(std::vector<s16>& signal) {
|
||||
const std::size_t num_frames = signal.size() / 2;
|
||||
@@ -69,7 +69,7 @@ CascadingFilter CascadingFilter::LowPass(double cutoff, std::size_t cascade_size
|
||||
}
|
||||
|
||||
CascadingFilter::CascadingFilter() = default;
|
||||
CascadingFilter::CascadingFilter(std::vector<Filter> filters) : filters(std::move(filters)) {}
|
||||
CascadingFilter::CascadingFilter(std::vector<Filter> filters_) : filters(std::move(filters_)) {}
|
||||
|
||||
void CascadingFilter::Process(std::vector<s16>& signal) {
|
||||
for (auto& filter : filters) {
|
||||
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
/// Passthrough filter.
|
||||
Filter();
|
||||
|
||||
Filter(double a0, double a1, double a2, double b0, double b1, double b2);
|
||||
Filter(double a0_, double a1_, double a2_, double b0_, double b1_, double b2_);
|
||||
|
||||
void Process(std::vector<s16>& signal);
|
||||
|
||||
@@ -51,7 +51,7 @@ public:
|
||||
/// Passthrough.
|
||||
CascadingFilter();
|
||||
|
||||
explicit CascadingFilter(std::vector<Filter> filters);
|
||||
explicit CascadingFilter(std::vector<Filter> filters_);
|
||||
|
||||
void Process(std::vector<s16>& signal);
|
||||
|
||||
|
||||
@@ -218,7 +218,7 @@ void Resample(s32* output, const s32* input, s32 pitch, s32& fraction, std::size
|
||||
const auto l2 = lut[lut_index + 2];
|
||||
const auto l3 = lut[lut_index + 3];
|
||||
|
||||
const auto s0 = static_cast<s32>(input[index]);
|
||||
const auto s0 = static_cast<s32>(input[index + 0]);
|
||||
const auto s1 = static_cast<s32>(input[index + 1]);
|
||||
const auto s2 = static_cast<s32>(input[index + 2]);
|
||||
const auto s3 = static_cast<s32>(input[index + 3]);
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "audio_core/info_updater.h"
|
||||
#include "audio_core/voice_context.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
@@ -71,10 +70,9 @@ namespace {
|
||||
namespace AudioCore {
|
||||
AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
|
||||
AudioCommon::AudioRendererParameter params,
|
||||
std::shared_ptr<Kernel::WritableEvent> buffer_event,
|
||||
Stream::ReleaseCallback&& release_callback,
|
||||
std::size_t instance_number)
|
||||
: worker_params{params}, buffer_event{buffer_event},
|
||||
memory_pool_info(params.effect_count + params.voice_count * 4),
|
||||
: worker_params{params}, memory_pool_info(params.effect_count + params.voice_count * 4),
|
||||
voice_context(params.voice_count), effect_context(params.effect_count), mix_context(),
|
||||
sink_context(params.sink_count), splitter_context(),
|
||||
voices(params.voice_count), memory{memory_},
|
||||
@@ -85,10 +83,9 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory
|
||||
params.num_splitter_send_channels);
|
||||
mix_context.Initialize(behavior_info, params.submix_count + 1, params.effect_count);
|
||||
audio_out = std::make_unique<AudioCore::AudioOut>();
|
||||
stream =
|
||||
audio_out->OpenStream(core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS,
|
||||
fmt::format("AudioRenderer-Instance{}", instance_number),
|
||||
[=]() { buffer_event->Signal(); });
|
||||
stream = audio_out->OpenStream(
|
||||
core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS,
|
||||
fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback));
|
||||
audio_out->StartStream(stream);
|
||||
|
||||
QueueMixedBuffer(0);
|
||||
|
||||
@@ -27,10 +27,6 @@ namespace Core::Timing {
|
||||
class CoreTiming;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class WritableEvent;
|
||||
}
|
||||
|
||||
namespace Core::Memory {
|
||||
class Memory;
|
||||
}
|
||||
@@ -44,7 +40,7 @@ class AudioRenderer {
|
||||
public:
|
||||
AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
|
||||
AudioCommon::AudioRendererParameter params,
|
||||
std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number);
|
||||
Stream::ReleaseCallback&& release_callback, std::size_t instance_number);
|
||||
~AudioRenderer();
|
||||
|
||||
[[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params,
|
||||
@@ -60,7 +56,6 @@ private:
|
||||
BehaviorInfo behavior_info{};
|
||||
|
||||
AudioCommon::AudioRendererParameter worker_params;
|
||||
std::shared_ptr<Kernel::WritableEvent> buffer_event;
|
||||
std::vector<ServerMemoryPoolInfo> memory_pool_info;
|
||||
VoiceContext voice_context;
|
||||
EffectContext effect_context;
|
||||
|
||||
@@ -18,7 +18,7 @@ class Buffer {
|
||||
public:
|
||||
using Tag = u64;
|
||||
|
||||
Buffer(Tag tag, std::vector<s16>&& samples) : tag{tag}, samples{std::move(samples)} {}
|
||||
Buffer(Tag tag_, std::vector<s16>&& samples_) : tag{tag_}, samples{std::move(samples_)} {}
|
||||
|
||||
/// Returns the raw audio data for the buffer
|
||||
std::vector<s16>& GetSamples() {
|
||||
|
||||
@@ -67,12 +67,12 @@ s32 ApplyMixDepop(s32* output, s32 first_sample, s32 delta, s32 sample_count) {
|
||||
|
||||
} // namespace
|
||||
|
||||
CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_params,
|
||||
VoiceContext& voice_context, MixContext& mix_context,
|
||||
SplitterContext& splitter_context, EffectContext& effect_context,
|
||||
Core::Memory::Memory& memory)
|
||||
: worker_params(worker_params), voice_context(voice_context), mix_context(mix_context),
|
||||
splitter_context(splitter_context), effect_context(effect_context), memory(memory),
|
||||
CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_params_,
|
||||
VoiceContext& voice_context_, MixContext& mix_context_,
|
||||
SplitterContext& splitter_context_,
|
||||
EffectContext& effect_context_, Core::Memory::Memory& memory_)
|
||||
: worker_params(worker_params_), voice_context(voice_context_), mix_context(mix_context_),
|
||||
splitter_context(splitter_context_), effect_context(effect_context_), memory(memory_),
|
||||
mix_buffer((worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT) *
|
||||
worker_params.sample_count),
|
||||
sample_buffer(MIX_BUFFER_SIZE),
|
||||
@@ -255,7 +255,8 @@ void CommandGenerator::GenerateDataSourceCommand(ServerVoiceInfo& voice_info, Vo
|
||||
|
||||
void CommandGenerator::GenerateBiquadFilterCommandForVoice(ServerVoiceInfo& voice_info,
|
||||
VoiceState& dsp_state,
|
||||
s32 mix_buffer_count, s32 channel) {
|
||||
[[maybe_unused]] s32 mix_buffer_count,
|
||||
[[maybe_unused]] s32 channel) {
|
||||
for (std::size_t i = 0; i < AudioCommon::MAX_BIQUAD_FILTERS; i++) {
|
||||
const auto& in_params = voice_info.GetInParams();
|
||||
auto& biquad_filter = in_params.biquad_filter[i];
|
||||
@@ -278,9 +279,12 @@ void CommandGenerator::GenerateBiquadFilterCommandForVoice(ServerVoiceInfo& voic
|
||||
}
|
||||
}
|
||||
|
||||
void AudioCore::CommandGenerator::GenerateBiquadFilterCommand(
|
||||
s32 mix_buffer, const BiquadFilterParameter& params, std::array<s64, 2>& state,
|
||||
std::size_t input_offset, std::size_t output_offset, s32 sample_count, s32 node_id) {
|
||||
void CommandGenerator::GenerateBiquadFilterCommand([[maybe_unused]] s32 mix_buffer_id,
|
||||
const BiquadFilterParameter& params,
|
||||
std::array<s64, 2>& state,
|
||||
std::size_t input_offset,
|
||||
std::size_t output_offset, s32 sample_count,
|
||||
s32 node_id) {
|
||||
if (dumping_frame) {
|
||||
LOG_DEBUG(Audio,
|
||||
"(DSP_TRACE) GenerateBiquadFilterCommand node_id={}, "
|
||||
@@ -379,11 +383,14 @@ void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, E
|
||||
const auto channel_count = params.channel_count;
|
||||
for (s32 i = 0; i < channel_count; i++) {
|
||||
// TODO(ogniK): Actually implement reverb
|
||||
/*
|
||||
if (params.input[i] != params.output[i]) {
|
||||
const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]);
|
||||
auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
|
||||
ApplyMix<1>(output, input, 32768, worker_params.sample_count);
|
||||
}
|
||||
}*/
|
||||
auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
|
||||
std::memset(output, 0, worker_params.sample_count * sizeof(s32));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -714,7 +721,8 @@ s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_s
|
||||
}
|
||||
|
||||
s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
|
||||
s32 sample_count, s32 channel, std::size_t mix_offset) {
|
||||
s32 sample_count, [[maybe_unused]] s32 channel,
|
||||
std::size_t mix_offset) {
|
||||
const auto& in_params = voice_info.GetInParams();
|
||||
const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
|
||||
if (wave_buffer.buffer_address == 0) {
|
||||
|
||||
@@ -25,10 +25,10 @@ using MixVolumeBuffer = std::array<float, AudioCommon::MAX_MIX_BUFFERS>;
|
||||
|
||||
class CommandGenerator {
|
||||
public:
|
||||
explicit CommandGenerator(AudioCommon::AudioRendererParameter& worker_params,
|
||||
VoiceContext& voice_context, MixContext& mix_context,
|
||||
SplitterContext& splitter_context, EffectContext& effect_context,
|
||||
Core::Memory::Memory& memory);
|
||||
explicit CommandGenerator(AudioCommon::AudioRendererParameter& worker_params_,
|
||||
VoiceContext& voice_context_, MixContext& mix_context_,
|
||||
SplitterContext& splitter_context_, EffectContext& effect_context_,
|
||||
Core::Memory::Memory& memory_);
|
||||
~CommandGenerator();
|
||||
|
||||
void ClearMixBuffers();
|
||||
|
||||
@@ -21,15 +21,16 @@ namespace AudioCore {
|
||||
|
||||
class CubebSinkStream final : public SinkStream {
|
||||
public:
|
||||
CubebSinkStream(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
|
||||
CubebSinkStream(cubeb* ctx_, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
|
||||
const std::string& name)
|
||||
: ctx{ctx}, num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate,
|
||||
num_channels} {
|
||||
: ctx{ctx_}, num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate,
|
||||
num_channels} {
|
||||
|
||||
cubeb_stream_params params{};
|
||||
params.rate = sample_rate;
|
||||
params.channels = num_channels;
|
||||
params.format = CUBEB_SAMPLE_S16NE;
|
||||
params.prefs = CUBEB_STREAM_PREF_PERSIST;
|
||||
switch (num_channels) {
|
||||
case 1:
|
||||
params.layout = CUBEB_LAYOUT_MONO;
|
||||
@@ -192,8 +193,9 @@ SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels,
|
||||
return *sink_streams.back();
|
||||
}
|
||||
|
||||
long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
|
||||
void* output_buffer, long num_frames) {
|
||||
long CubebSinkStream::DataCallback([[maybe_unused]] cubeb_stream* stream, void* user_data,
|
||||
[[maybe_unused]] const void* input_buffer, void* output_buffer,
|
||||
long num_frames) {
|
||||
auto* impl = static_cast<CubebSinkStream*>(user_data);
|
||||
auto* buffer = static_cast<u8*>(output_buffer);
|
||||
|
||||
@@ -236,7 +238,9 @@ long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const
|
||||
return num_frames;
|
||||
}
|
||||
|
||||
void CubebSinkStream::StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state) {}
|
||||
void CubebSinkStream::StateCallback([[maybe_unused]] cubeb_stream* stream,
|
||||
[[maybe_unused]] void* user_data,
|
||||
[[maybe_unused]] cubeb_state state) {}
|
||||
|
||||
std::vector<std::string> ListCubebSinkDevices() {
|
||||
std::vector<std::string> device_list;
|
||||
|
||||
@@ -12,7 +12,7 @@ bool ValidChannelCountForEffect(s32 channel_count) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
EffectContext::EffectContext(std::size_t effect_count) : effect_count(effect_count) {
|
||||
EffectContext::EffectContext(std::size_t effect_count_) : effect_count(effect_count_) {
|
||||
effects.reserve(effect_count);
|
||||
std::generate_n(std::back_inserter(effects), effect_count,
|
||||
[] { return std::make_unique<EffectStubbed>(); });
|
||||
@@ -61,13 +61,13 @@ const EffectBase* EffectContext::GetInfo(std::size_t i) const {
|
||||
return effects.at(i).get();
|
||||
}
|
||||
|
||||
EffectStubbed::EffectStubbed() : EffectBase::EffectBase(EffectType::Invalid) {}
|
||||
EffectStubbed::EffectStubbed() : EffectBase(EffectType::Invalid) {}
|
||||
EffectStubbed::~EffectStubbed() = default;
|
||||
|
||||
void EffectStubbed::Update(EffectInfo::InParams& in_params) {}
|
||||
void EffectStubbed::Update([[maybe_unused]] EffectInfo::InParams& in_params) {}
|
||||
void EffectStubbed::UpdateForCommandGeneration() {}
|
||||
|
||||
EffectBase::EffectBase(EffectType effect_type) : effect_type(effect_type) {}
|
||||
EffectBase::EffectBase(EffectType effect_type_) : effect_type(effect_type_) {}
|
||||
EffectBase::~EffectBase() = default;
|
||||
|
||||
UsageState EffectBase::GetUsage() const {
|
||||
@@ -90,32 +90,32 @@ s32 EffectBase::GetProcessingOrder() const {
|
||||
return processing_order;
|
||||
}
|
||||
|
||||
EffectI3dl2Reverb::EffectI3dl2Reverb() : EffectGeneric::EffectGeneric(EffectType::I3dl2Reverb) {}
|
||||
EffectI3dl2Reverb::EffectI3dl2Reverb() : EffectGeneric(EffectType::I3dl2Reverb) {}
|
||||
EffectI3dl2Reverb::~EffectI3dl2Reverb() = default;
|
||||
|
||||
void EffectI3dl2Reverb::Update(EffectInfo::InParams& in_params) {
|
||||
auto& internal_params = GetParams();
|
||||
auto& params = GetParams();
|
||||
const auto* reverb_params = reinterpret_cast<I3dl2ReverbParams*>(in_params.raw.data());
|
||||
if (!ValidChannelCountForEffect(reverb_params->max_channels)) {
|
||||
UNREACHABLE_MSG("Invalid reverb max channel count {}", reverb_params->max_channels);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto last_status = internal_params.status;
|
||||
const auto last_status = params.status;
|
||||
mix_id = in_params.mix_id;
|
||||
processing_order = in_params.processing_order;
|
||||
internal_params = *reverb_params;
|
||||
params = *reverb_params;
|
||||
if (!ValidChannelCountForEffect(reverb_params->channel_count)) {
|
||||
internal_params.channel_count = internal_params.max_channels;
|
||||
params.channel_count = params.max_channels;
|
||||
}
|
||||
enabled = in_params.is_enabled;
|
||||
if (last_status != ParameterStatus::Updated) {
|
||||
internal_params.status = last_status;
|
||||
params.status = last_status;
|
||||
}
|
||||
|
||||
if (in_params.is_new || skipped) {
|
||||
usage = UsageState::Initialized;
|
||||
internal_params.status = ParameterStatus::Initialized;
|
||||
params.status = ParameterStatus::Initialized;
|
||||
skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
|
||||
}
|
||||
}
|
||||
@@ -129,15 +129,15 @@ void EffectI3dl2Reverb::UpdateForCommandGeneration() {
|
||||
GetParams().status = ParameterStatus::Updated;
|
||||
}
|
||||
|
||||
EffectBiquadFilter::EffectBiquadFilter() : EffectGeneric::EffectGeneric(EffectType::BiquadFilter) {}
|
||||
EffectBiquadFilter::EffectBiquadFilter() : EffectGeneric(EffectType::BiquadFilter) {}
|
||||
EffectBiquadFilter::~EffectBiquadFilter() = default;
|
||||
|
||||
void EffectBiquadFilter::Update(EffectInfo::InParams& in_params) {
|
||||
auto& internal_params = GetParams();
|
||||
auto& params = GetParams();
|
||||
const auto* biquad_params = reinterpret_cast<BiquadFilterParams*>(in_params.raw.data());
|
||||
mix_id = in_params.mix_id;
|
||||
processing_order = in_params.processing_order;
|
||||
internal_params = *biquad_params;
|
||||
params = *biquad_params;
|
||||
enabled = in_params.is_enabled;
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ void EffectBiquadFilter::UpdateForCommandGeneration() {
|
||||
GetParams().status = ParameterStatus::Updated;
|
||||
}
|
||||
|
||||
EffectAuxInfo::EffectAuxInfo() : EffectGeneric::EffectGeneric(EffectType::Aux) {}
|
||||
EffectAuxInfo::EffectAuxInfo() : EffectGeneric(EffectType::Aux) {}
|
||||
EffectAuxInfo::~EffectAuxInfo() = default;
|
||||
|
||||
void EffectAuxInfo::Update(EffectInfo::InParams& in_params) {
|
||||
@@ -200,32 +200,32 @@ VAddr EffectAuxInfo::GetRecvBuffer() const {
|
||||
return recv_buffer;
|
||||
}
|
||||
|
||||
EffectDelay::EffectDelay() : EffectGeneric::EffectGeneric(EffectType::Delay) {}
|
||||
EffectDelay::EffectDelay() : EffectGeneric(EffectType::Delay) {}
|
||||
EffectDelay::~EffectDelay() = default;
|
||||
|
||||
void EffectDelay::Update(EffectInfo::InParams& in_params) {
|
||||
const auto* delay_params = reinterpret_cast<DelayParams*>(in_params.raw.data());
|
||||
auto& internal_params = GetParams();
|
||||
auto& params = GetParams();
|
||||
if (!ValidChannelCountForEffect(delay_params->max_channels)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto last_status = internal_params.status;
|
||||
const auto last_status = params.status;
|
||||
mix_id = in_params.mix_id;
|
||||
processing_order = in_params.processing_order;
|
||||
internal_params = *delay_params;
|
||||
params = *delay_params;
|
||||
if (!ValidChannelCountForEffect(delay_params->channels)) {
|
||||
internal_params.channels = internal_params.max_channels;
|
||||
params.channels = params.max_channels;
|
||||
}
|
||||
enabled = in_params.is_enabled;
|
||||
|
||||
if (last_status != ParameterStatus::Updated) {
|
||||
internal_params.status = last_status;
|
||||
params.status = last_status;
|
||||
}
|
||||
|
||||
if (in_params.is_new || skipped) {
|
||||
usage = UsageState::Initialized;
|
||||
internal_params.status = ParameterStatus::Initialized;
|
||||
params.status = ParameterStatus::Initialized;
|
||||
skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
|
||||
}
|
||||
}
|
||||
@@ -239,7 +239,7 @@ void EffectDelay::UpdateForCommandGeneration() {
|
||||
GetParams().status = ParameterStatus::Updated;
|
||||
}
|
||||
|
||||
EffectBufferMixer::EffectBufferMixer() : EffectGeneric::EffectGeneric(EffectType::BufferMixer) {}
|
||||
EffectBufferMixer::EffectBufferMixer() : EffectGeneric(EffectType::BufferMixer) {}
|
||||
EffectBufferMixer::~EffectBufferMixer() = default;
|
||||
|
||||
void EffectBufferMixer::Update(EffectInfo::InParams& in_params) {
|
||||
@@ -257,32 +257,32 @@ void EffectBufferMixer::UpdateForCommandGeneration() {
|
||||
}
|
||||
}
|
||||
|
||||
EffectReverb::EffectReverb() : EffectGeneric::EffectGeneric(EffectType::Reverb) {}
|
||||
EffectReverb::EffectReverb() : EffectGeneric(EffectType::Reverb) {}
|
||||
EffectReverb::~EffectReverb() = default;
|
||||
|
||||
void EffectReverb::Update(EffectInfo::InParams& in_params) {
|
||||
const auto* reverb_params = reinterpret_cast<ReverbParams*>(in_params.raw.data());
|
||||
auto& internal_params = GetParams();
|
||||
auto& params = GetParams();
|
||||
if (!ValidChannelCountForEffect(reverb_params->max_channels)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto last_status = internal_params.status;
|
||||
const auto last_status = params.status;
|
||||
mix_id = in_params.mix_id;
|
||||
processing_order = in_params.processing_order;
|
||||
internal_params = *reverb_params;
|
||||
params = *reverb_params;
|
||||
if (!ValidChannelCountForEffect(reverb_params->channels)) {
|
||||
internal_params.channels = internal_params.max_channels;
|
||||
params.channels = params.max_channels;
|
||||
}
|
||||
enabled = in_params.is_enabled;
|
||||
|
||||
if (last_status != ParameterStatus::Updated) {
|
||||
internal_params.status = last_status;
|
||||
params.status = last_status;
|
||||
}
|
||||
|
||||
if (in_params.is_new || skipped) {
|
||||
usage = UsageState::Initialized;
|
||||
internal_params.status = ParameterStatus::Initialized;
|
||||
params.status = ParameterStatus::Initialized;
|
||||
skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ struct AuxAddress {
|
||||
|
||||
class EffectBase {
|
||||
public:
|
||||
explicit EffectBase(EffectType effect_type);
|
||||
explicit EffectBase(EffectType effect_type_);
|
||||
virtual ~EffectBase();
|
||||
|
||||
virtual void Update(EffectInfo::InParams& in_params) = 0;
|
||||
@@ -206,7 +206,7 @@ protected:
|
||||
template <typename T>
|
||||
class EffectGeneric : public EffectBase {
|
||||
public:
|
||||
explicit EffectGeneric(EffectType effect_type) : EffectBase(effect_type) {}
|
||||
explicit EffectGeneric(EffectType effect_type_) : EffectBase(effect_type_) {}
|
||||
|
||||
T& GetParams() {
|
||||
return internal_params;
|
||||
@@ -306,7 +306,7 @@ private:
|
||||
|
||||
class EffectContext {
|
||||
public:
|
||||
explicit EffectContext(std::size_t effect_count);
|
||||
explicit EffectContext(std::size_t effect_count_);
|
||||
~EffectContext();
|
||||
|
||||
[[nodiscard]] std::size_t GetCount() const;
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
InfoUpdater::InfoUpdater(const std::vector<u8>& in_params, std::vector<u8>& out_params,
|
||||
BehaviorInfo& behavior_info)
|
||||
: in_params(in_params), out_params(out_params), behavior_info(behavior_info) {
|
||||
InfoUpdater::InfoUpdater(const std::vector<u8>& in_params_, std::vector<u8>& out_params_,
|
||||
BehaviorInfo& behavior_info_)
|
||||
: in_params(in_params_), out_params(out_params_), behavior_info(behavior_info_) {
|
||||
ASSERT(
|
||||
AudioCommon::CanConsumeBuffer(in_params.size(), 0, sizeof(AudioCommon::UpdateDataHeader)));
|
||||
std::memcpy(&input_header, in_params.data(), sizeof(AudioCommon::UpdateDataHeader));
|
||||
@@ -135,8 +135,8 @@ bool InfoUpdater::UpdateVoiceChannelResources(VoiceContext& voice_context) {
|
||||
}
|
||||
|
||||
bool InfoUpdater::UpdateVoices(VoiceContext& voice_context,
|
||||
std::vector<ServerMemoryPoolInfo>& memory_pool_info,
|
||||
VAddr audio_codec_dsp_addr) {
|
||||
[[maybe_unused]] std::vector<ServerMemoryPoolInfo>& memory_pool_info,
|
||||
[[maybe_unused]] VAddr audio_codec_dsp_addr) {
|
||||
const auto voice_count = voice_context.GetVoiceCount();
|
||||
std::vector<VoiceInfo::InParams> voice_in(voice_count);
|
||||
std::vector<VoiceInfo::OutParams> voice_out(voice_count);
|
||||
@@ -165,28 +165,28 @@ bool InfoUpdater::UpdateVoices(VoiceContext& voice_context,
|
||||
|
||||
// Update our voices
|
||||
for (std::size_t i = 0; i < voice_count; i++) {
|
||||
auto& in_params = voice_in[i];
|
||||
const auto channel_count = static_cast<std::size_t>(in_params.channel_count);
|
||||
auto& voice_in_params = voice_in[i];
|
||||
const auto channel_count = static_cast<std::size_t>(voice_in_params.channel_count);
|
||||
// Skip if it's not currently in use
|
||||
if (!in_params.is_in_use) {
|
||||
if (!voice_in_params.is_in_use) {
|
||||
continue;
|
||||
}
|
||||
// Voice states for each channel
|
||||
std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT> voice_states{};
|
||||
ASSERT(static_cast<std::size_t>(in_params.id) < voice_count);
|
||||
ASSERT(static_cast<std::size_t>(voice_in_params.id) < voice_count);
|
||||
|
||||
// Grab our current voice info
|
||||
auto& voice_info = voice_context.GetInfo(static_cast<std::size_t>(in_params.id));
|
||||
auto& voice_info = voice_context.GetInfo(static_cast<std::size_t>(voice_in_params.id));
|
||||
|
||||
ASSERT(channel_count <= AudioCommon::MAX_CHANNEL_COUNT);
|
||||
|
||||
// Get all our channel voice states
|
||||
for (std::size_t channel = 0; channel < channel_count; channel++) {
|
||||
voice_states[channel] =
|
||||
&voice_context.GetState(in_params.voice_channel_resource_ids[channel]);
|
||||
&voice_context.GetState(voice_in_params.voice_channel_resource_ids[channel]);
|
||||
}
|
||||
|
||||
if (in_params.is_new) {
|
||||
if (voice_in_params.is_new) {
|
||||
// Default our values for our voice
|
||||
voice_info.Initialize();
|
||||
if (channel_count == 0 || channel_count > AudioCommon::MAX_CHANNEL_COUNT) {
|
||||
@@ -200,12 +200,12 @@ bool InfoUpdater::UpdateVoices(VoiceContext& voice_context,
|
||||
}
|
||||
|
||||
// Update our voice
|
||||
voice_info.UpdateParameters(in_params, behavior_info);
|
||||
voice_info.UpdateParameters(voice_in_params, behavior_info);
|
||||
// TODO(ogniK): Handle mapping errors with behavior info based on in params response
|
||||
|
||||
// Update our wave buffers
|
||||
voice_info.UpdateWaveBuffers(in_params, voice_states, behavior_info);
|
||||
voice_info.WriteOutStatus(voice_out[i], in_params, voice_states);
|
||||
voice_info.UpdateWaveBuffers(voice_in_params, voice_states, behavior_info);
|
||||
voice_info.WriteOutStatus(voice_out[i], voice_in_params, voice_states);
|
||||
}
|
||||
|
||||
if (!AudioCommon::CanConsumeBuffer(out_params.size(), output_offset, voice_out_size)) {
|
||||
@@ -445,7 +445,7 @@ bool InfoUpdater::UpdatePerformanceBuffer() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InfoUpdater::UpdateErrorInfo(BehaviorInfo& in_behavior_info) {
|
||||
bool InfoUpdater::UpdateErrorInfo([[maybe_unused]] BehaviorInfo& in_behavior_info) {
|
||||
const auto total_beahvior_info_out = sizeof(BehaviorInfo::OutParams);
|
||||
|
||||
if (!AudioCommon::CanConsumeBuffer(out_params.size(), output_offset, total_beahvior_info_out)) {
|
||||
|
||||
@@ -21,8 +21,8 @@ class SplitterContext;
|
||||
class InfoUpdater {
|
||||
public:
|
||||
// TODO(ogniK): Pass process handle when we support it
|
||||
InfoUpdater(const std::vector<u8>& in_params, std::vector<u8>& out_params,
|
||||
BehaviorInfo& behavior_info);
|
||||
InfoUpdater(const std::vector<u8>& in_params_, std::vector<u8>& out_params_,
|
||||
BehaviorInfo& behavior_info_);
|
||||
~InfoUpdater();
|
||||
|
||||
bool UpdateBehaviorInfo(BehaviorInfo& in_behavior_info);
|
||||
|
||||
@@ -10,11 +10,10 @@ namespace AudioCore {
|
||||
|
||||
ServerMemoryPoolInfo::ServerMemoryPoolInfo() = default;
|
||||
ServerMemoryPoolInfo::~ServerMemoryPoolInfo() = default;
|
||||
bool ServerMemoryPoolInfo::Update(const ServerMemoryPoolInfo::InParams& in_params,
|
||||
ServerMemoryPoolInfo::OutParams& out_params) {
|
||||
|
||||
bool ServerMemoryPoolInfo::Update(const InParams& in_params, OutParams& out_params) {
|
||||
// Our state does not need to be changed
|
||||
if (in_params.state != ServerMemoryPoolInfo::State::RequestAttach &&
|
||||
in_params.state != ServerMemoryPoolInfo::State::RequestDetach) {
|
||||
if (in_params.state != State::RequestAttach && in_params.state != State::RequestDetach) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -32,11 +31,11 @@ bool ServerMemoryPoolInfo::Update(const ServerMemoryPoolInfo::InParams& in_param
|
||||
return false;
|
||||
}
|
||||
|
||||
if (in_params.state == ServerMemoryPoolInfo::State::RequestAttach) {
|
||||
if (in_params.state == State::RequestAttach) {
|
||||
cpu_address = in_params.address;
|
||||
size = in_params.size;
|
||||
used = true;
|
||||
out_params.state = ServerMemoryPoolInfo::State::Attached;
|
||||
out_params.state = State::Attached;
|
||||
} else {
|
||||
// Unexpected address
|
||||
if (cpu_address != in_params.address) {
|
||||
@@ -54,7 +53,7 @@ bool ServerMemoryPoolInfo::Update(const ServerMemoryPoolInfo::InParams& in_param
|
||||
cpu_address = 0;
|
||||
size = 0;
|
||||
used = false;
|
||||
out_params.state = ServerMemoryPoolInfo::State::Detached;
|
||||
out_params.state = State::Detached;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -28,19 +28,18 @@ public:
|
||||
struct InParams {
|
||||
u64_le address{};
|
||||
u64_le size{};
|
||||
ServerMemoryPoolInfo::State state{};
|
||||
State state{};
|
||||
INSERT_PADDING_WORDS(3);
|
||||
};
|
||||
static_assert(sizeof(ServerMemoryPoolInfo::InParams) == 0x20, "InParams are an invalid size");
|
||||
static_assert(sizeof(InParams) == 0x20, "InParams are an invalid size");
|
||||
|
||||
struct OutParams {
|
||||
ServerMemoryPoolInfo::State state{};
|
||||
State state{};
|
||||
INSERT_PADDING_WORDS(3);
|
||||
};
|
||||
static_assert(sizeof(ServerMemoryPoolInfo::OutParams) == 0x10, "OutParams are an invalid size");
|
||||
static_assert(sizeof(OutParams) == 0x10, "OutParams are an invalid size");
|
||||
|
||||
bool Update(const ServerMemoryPoolInfo::InParams& in_params,
|
||||
ServerMemoryPoolInfo::OutParams& out_params);
|
||||
bool Update(const InParams& in_params, OutParams& out_params);
|
||||
|
||||
private:
|
||||
// There's another entry here which is the DSP address, however since we're not talking to the
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "audio_core/sink_context.h"
|
||||
|
||||
namespace AudioCore {
|
||||
SinkContext::SinkContext(std::size_t sink_count) : sink_count(sink_count) {}
|
||||
SinkContext::SinkContext(std::size_t sink_count_) : sink_count{sink_count_} {}
|
||||
SinkContext::~SinkContext() = default;
|
||||
|
||||
std::size_t SinkContext::GetCount() const {
|
||||
|
||||
@@ -40,21 +40,21 @@ public:
|
||||
SinkSampleFormat sample_format;
|
||||
std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input;
|
||||
bool in_use;
|
||||
INSERT_UNION_PADDING_BYTES(5);
|
||||
INSERT_PADDING_BYTES_NOINIT(5);
|
||||
};
|
||||
static_assert(sizeof(SinkInfo::CircularBufferIn) == 0x28,
|
||||
static_assert(sizeof(CircularBufferIn) == 0x28,
|
||||
"SinkInfo::CircularBufferIn is in invalid size");
|
||||
|
||||
struct DeviceIn {
|
||||
std::array<u8, 255> device_name;
|
||||
INSERT_UNION_PADDING_BYTES(1);
|
||||
INSERT_PADDING_BYTES_NOINIT(1);
|
||||
s32_le input_count;
|
||||
std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input;
|
||||
INSERT_UNION_PADDING_BYTES(1);
|
||||
INSERT_PADDING_BYTES_NOINIT(1);
|
||||
bool down_matrix_enabled;
|
||||
DownmixCoefficients down_matrix_coef;
|
||||
};
|
||||
static_assert(sizeof(SinkInfo::DeviceIn) == 0x11c, "SinkInfo::DeviceIn is an invalid size");
|
||||
static_assert(sizeof(DeviceIn) == 0x11c, "SinkInfo::DeviceIn is an invalid size");
|
||||
|
||||
struct InParams {
|
||||
SinkTypes type{};
|
||||
@@ -64,16 +64,16 @@ public:
|
||||
INSERT_PADDING_WORDS(6);
|
||||
union {
|
||||
// std::array<u8, 0x120> raw{};
|
||||
SinkInfo::DeviceIn device;
|
||||
SinkInfo::CircularBufferIn circular_buffer;
|
||||
DeviceIn device;
|
||||
CircularBufferIn circular_buffer;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(SinkInfo::InParams) == 0x140, "SinkInfo::InParams are an invalid size!");
|
||||
static_assert(sizeof(InParams) == 0x140, "SinkInfo::InParams are an invalid size!");
|
||||
};
|
||||
|
||||
class SinkContext {
|
||||
public:
|
||||
explicit SinkContext(std::size_t sink_count);
|
||||
explicit SinkContext(std::size_t sink_count_);
|
||||
~SinkContext();
|
||||
|
||||
[[nodiscard]] std::size_t GetCount() const;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
ServerSplitterDestinationData::ServerSplitterDestinationData(s32 id) : id(id) {}
|
||||
ServerSplitterDestinationData::ServerSplitterDestinationData(s32 id_) : id{id_} {}
|
||||
ServerSplitterDestinationData::~ServerSplitterDestinationData() = default;
|
||||
|
||||
void ServerSplitterDestinationData::Update(SplitterInfo::InDestinationParams& header) {
|
||||
@@ -87,7 +87,7 @@ void ServerSplitterDestinationData::UpdateInternalState() {
|
||||
needs_update = false;
|
||||
}
|
||||
|
||||
ServerSplitterInfo::ServerSplitterInfo(s32 id) : id(id) {}
|
||||
ServerSplitterInfo::ServerSplitterInfo(s32 id_) : id(id_) {}
|
||||
ServerSplitterInfo::~ServerSplitterInfo() = default;
|
||||
|
||||
void ServerSplitterInfo::InitializeInfos() {
|
||||
@@ -121,7 +121,7 @@ const ServerSplitterDestinationData* ServerSplitterInfo::GetHead() const {
|
||||
}
|
||||
|
||||
ServerSplitterDestinationData* ServerSplitterInfo::GetData(std::size_t depth) {
|
||||
auto current_head = head;
|
||||
auto* current_head = head;
|
||||
for (std::size_t i = 0; i < depth; i++) {
|
||||
if (current_head == nullptr) {
|
||||
return nullptr;
|
||||
@@ -132,7 +132,7 @@ ServerSplitterDestinationData* ServerSplitterInfo::GetData(std::size_t depth) {
|
||||
}
|
||||
|
||||
const ServerSplitterDestinationData* ServerSplitterInfo::GetData(std::size_t depth) const {
|
||||
auto current_head = head;
|
||||
auto* current_head = head;
|
||||
for (std::size_t i = 0; i < depth; i++) {
|
||||
if (current_head == nullptr) {
|
||||
return nullptr;
|
||||
@@ -245,7 +245,7 @@ ServerSplitterDestinationData* SplitterContext::GetDestinationData(std::size_t i
|
||||
const ServerSplitterDestinationData* SplitterContext::GetDestinationData(std::size_t info,
|
||||
std::size_t data) const {
|
||||
ASSERT(info < info_count);
|
||||
auto& cur_info = GetInfo(info);
|
||||
const auto& cur_info = GetInfo(info);
|
||||
return cur_info.GetData(data);
|
||||
}
|
||||
|
||||
@@ -267,11 +267,11 @@ std::size_t SplitterContext::GetDataCount() const {
|
||||
return data_count;
|
||||
}
|
||||
|
||||
void SplitterContext::Setup(std::size_t _info_count, std::size_t _data_count,
|
||||
void SplitterContext::Setup(std::size_t info_count_, std::size_t data_count_,
|
||||
bool is_splitter_bug_fixed) {
|
||||
|
||||
info_count = _info_count;
|
||||
data_count = _data_count;
|
||||
info_count = info_count_;
|
||||
data_count = data_count_;
|
||||
|
||||
for (std::size_t i = 0; i < info_count; i++) {
|
||||
auto& splitter = infos.emplace_back(static_cast<s32>(i));
|
||||
@@ -364,7 +364,7 @@ bool SplitterContext::RecomposeDestination(ServerSplitterInfo& info,
|
||||
// Clear our current destinations
|
||||
auto* current_head = info.GetHead();
|
||||
while (current_head != nullptr) {
|
||||
auto next_head = current_head->GetNextDestination();
|
||||
auto* next_head = current_head->GetNextDestination();
|
||||
current_head->SetNextDestination(nullptr);
|
||||
current_head = next_head;
|
||||
}
|
||||
@@ -471,8 +471,8 @@ bool NodeStates::DepthFirstSearch(EdgeMatrix& edge_matrix) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto node_count = edge_matrix.GetNodeCount();
|
||||
for (s32 j = 0; j < static_cast<s32>(node_count); j++) {
|
||||
const auto edge_node_count = edge_matrix.GetNodeCount();
|
||||
for (s32 j = 0; j < static_cast<s32>(edge_node_count); j++) {
|
||||
// Check if our node is connected to our edge matrix
|
||||
if (!edge_matrix.Connected(current_stack_index, j)) {
|
||||
continue;
|
||||
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
NodeStates();
|
||||
~NodeStates();
|
||||
|
||||
void Initialize(std::size_t _node_count);
|
||||
void Initialize(std::size_t node_count_);
|
||||
bool Tsort(EdgeMatrix& edge_matrix);
|
||||
std::size_t GetIndexPos() const;
|
||||
const std::vector<s32>& GetIndexList() const;
|
||||
@@ -72,15 +72,15 @@ private:
|
||||
void PushTsortResult(s32 index);
|
||||
bool DepthFirstSearch(EdgeMatrix& edge_matrix);
|
||||
void ResetState();
|
||||
void UpdateState(NodeStates::State state, std::size_t i);
|
||||
NodeStates::State GetState(std::size_t i);
|
||||
void UpdateState(State state, std::size_t i);
|
||||
State GetState(std::size_t i);
|
||||
|
||||
std::size_t node_count{};
|
||||
std::vector<bool> was_node_found{};
|
||||
std::vector<bool> was_node_completed{};
|
||||
std::size_t index_pos{};
|
||||
std::vector<s32> index_list{};
|
||||
NodeStates::Stack index_stack{};
|
||||
Stack index_stack{};
|
||||
};
|
||||
|
||||
enum class SplitterMagic : u32_le {
|
||||
@@ -97,8 +97,7 @@ public:
|
||||
s32_le data_count{};
|
||||
INSERT_PADDING_WORDS(5);
|
||||
};
|
||||
static_assert(sizeof(SplitterInfo::InHeader) == 0x20,
|
||||
"SplitterInfo::InHeader is an invalid size");
|
||||
static_assert(sizeof(InHeader) == 0x20, "SplitterInfo::InHeader is an invalid size");
|
||||
|
||||
struct InInfoPrams {
|
||||
SplitterMagic magic{};
|
||||
@@ -107,8 +106,7 @@ public:
|
||||
s32_le length{};
|
||||
s32_le resource_id_base{};
|
||||
};
|
||||
static_assert(sizeof(SplitterInfo::InInfoPrams) == 0x14,
|
||||
"SplitterInfo::InInfoPrams is an invalid size");
|
||||
static_assert(sizeof(InInfoPrams) == 0x14, "SplitterInfo::InInfoPrams is an invalid size");
|
||||
|
||||
struct InDestinationParams {
|
||||
SplitterMagic magic{};
|
||||
@@ -118,13 +116,13 @@ public:
|
||||
bool in_use{};
|
||||
INSERT_PADDING_BYTES(3);
|
||||
};
|
||||
static_assert(sizeof(SplitterInfo::InDestinationParams) == 0x70,
|
||||
static_assert(sizeof(InDestinationParams) == 0x70,
|
||||
"SplitterInfo::InDestinationParams is an invalid size");
|
||||
};
|
||||
|
||||
class ServerSplitterDestinationData {
|
||||
public:
|
||||
explicit ServerSplitterDestinationData(s32 id);
|
||||
explicit ServerSplitterDestinationData(s32 id_);
|
||||
~ServerSplitterDestinationData();
|
||||
|
||||
void Update(SplitterInfo::InDestinationParams& header);
|
||||
@@ -153,7 +151,7 @@ private:
|
||||
|
||||
class ServerSplitterInfo {
|
||||
public:
|
||||
explicit ServerSplitterInfo(s32 id);
|
||||
explicit ServerSplitterInfo(s32 id_);
|
||||
~ServerSplitterInfo();
|
||||
|
||||
void InitializeInfos();
|
||||
|
||||
@@ -31,10 +31,10 @@ u32 Stream::GetNumChannels() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
Stream::Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format format,
|
||||
ReleaseCallback&& release_callback, SinkStream& sink_stream, std::string&& name_)
|
||||
: sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
|
||||
sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} {
|
||||
Stream::Stream(Core::Timing::CoreTiming& core_timing_, u32 sample_rate_, Format format_,
|
||||
ReleaseCallback&& release_callback_, SinkStream& sink_stream_, std::string&& name_)
|
||||
: sample_rate{sample_rate_}, format{format_}, release_callback{std::move(release_callback_)},
|
||||
sink_stream{sink_stream_}, core_timing{core_timing_}, name{std::move(name_)} {
|
||||
release_event =
|
||||
Core::Timing::CreateEvent(name, [this](std::uintptr_t, std::chrono::nanoseconds ns_late) {
|
||||
ReleaseActiveBuffer(ns_late);
|
||||
@@ -51,6 +51,14 @@ void Stream::Stop() {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
bool Stream::Flush() {
|
||||
const bool had_buffers = !queued_buffers.empty();
|
||||
while (!queued_buffers.empty()) {
|
||||
queued_buffers.pop();
|
||||
}
|
||||
return had_buffers;
|
||||
}
|
||||
|
||||
void Stream::SetVolume(float volume) {
|
||||
game_volume = volume;
|
||||
}
|
||||
@@ -103,7 +111,14 @@ void Stream::PlayNextBuffer(std::chrono::nanoseconds ns_late) {
|
||||
|
||||
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
|
||||
|
||||
core_timing.ScheduleEvent(GetBufferReleaseNS(*active_buffer) - ns_late, release_event, {});
|
||||
const auto buffer_release_ns = GetBufferReleaseNS(*active_buffer);
|
||||
|
||||
// If ns_late is higher than the update rate ignore the delay
|
||||
if (ns_late > buffer_release_ns) {
|
||||
ns_late = {};
|
||||
}
|
||||
|
||||
core_timing.ScheduleEvent(buffer_release_ns - ns_late, release_event, {});
|
||||
}
|
||||
|
||||
void Stream::ReleaseActiveBuffer(std::chrono::nanoseconds ns_late) {
|
||||
@@ -122,7 +137,7 @@ bool Stream::QueueBuffer(BufferPtr&& buffer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Stream::ContainsBuffer(Buffer::Tag tag) const {
|
||||
bool Stream::ContainsBuffer([[maybe_unused]] Buffer::Tag tag) const {
|
||||
UNIMPLEMENTED();
|
||||
return {};
|
||||
}
|
||||
@@ -130,7 +145,11 @@ bool Stream::ContainsBuffer(Buffer::Tag tag) const {
|
||||
std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count) {
|
||||
std::vector<Buffer::Tag> tags;
|
||||
for (std::size_t count = 0; count < max_count && !released_buffers.empty(); ++count) {
|
||||
tags.push_back(released_buffers.front()->GetTag());
|
||||
if (released_buffers.front()) {
|
||||
tags.push_back(released_buffers.front()->GetTag());
|
||||
} else {
|
||||
ASSERT_MSG(false, "Invalid tag in released_buffers!");
|
||||
}
|
||||
released_buffers.pop();
|
||||
}
|
||||
return tags;
|
||||
@@ -140,7 +159,11 @@ std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers() {
|
||||
std::vector<Buffer::Tag> tags;
|
||||
tags.reserve(released_buffers.size());
|
||||
while (!released_buffers.empty()) {
|
||||
tags.push_back(released_buffers.front()->GetTag());
|
||||
if (released_buffers.front()) {
|
||||
tags.push_back(released_buffers.front()->GetTag());
|
||||
} else {
|
||||
ASSERT_MSG(false, "Invalid tag in released_buffers!");
|
||||
}
|
||||
released_buffers.pop();
|
||||
}
|
||||
return tags;
|
||||
|
||||
@@ -44,8 +44,8 @@ public:
|
||||
/// Callback function type, used to change guest state on a buffer being released
|
||||
using ReleaseCallback = std::function<void()>;
|
||||
|
||||
Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format format,
|
||||
ReleaseCallback&& release_callback, SinkStream& sink_stream, std::string&& name_);
|
||||
Stream(Core::Timing::CoreTiming& core_timing_, u32 sample_rate_, Format format_,
|
||||
ReleaseCallback&& release_callback_, SinkStream& sink_stream_, std::string&& name_);
|
||||
|
||||
/// Plays the audio stream
|
||||
void Play();
|
||||
@@ -56,6 +56,9 @@ public:
|
||||
/// Queues a buffer into the audio stream, returns true on success
|
||||
bool QueueBuffer(BufferPtr&& buffer);
|
||||
|
||||
/// Flush audio buffers
|
||||
bool Flush();
|
||||
|
||||
/// Returns true if the audio stream contains a buffer with the specified tag
|
||||
[[nodiscard]] bool ContainsBuffer(Buffer::Tag tag) const;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
ServerVoiceChannelResource::ServerVoiceChannelResource(s32 id) : id(id) {}
|
||||
ServerVoiceChannelResource::ServerVoiceChannelResource(s32 id_) : id(id_) {}
|
||||
ServerVoiceChannelResource::~ServerVoiceChannelResource() = default;
|
||||
|
||||
bool ServerVoiceChannelResource::InUse() const {
|
||||
@@ -209,7 +209,8 @@ void ServerVoiceInfo::UpdateWaveBuffers(
|
||||
|
||||
void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer,
|
||||
const WaveBuffer& in_wave_buffer, SampleFormat sample_format,
|
||||
bool is_buffer_valid, BehaviorInfo& behavior_info) {
|
||||
bool is_buffer_valid,
|
||||
[[maybe_unused]] BehaviorInfo& behavior_info) {
|
||||
if (!is_buffer_valid && out_wavebuffer.sent_to_dsp) {
|
||||
out_wavebuffer.buffer_address = 0;
|
||||
out_wavebuffer.buffer_size = 0;
|
||||
@@ -400,7 +401,7 @@ bool ServerVoiceInfo::HasValidWaveBuffer(const VoiceState* state) const {
|
||||
return std::find(valid_wb.begin(), valid_wb.end(), true) != valid_wb.end();
|
||||
}
|
||||
|
||||
VoiceContext::VoiceContext(std::size_t voice_count) : voice_count(voice_count) {
|
||||
VoiceContext::VoiceContext(std::size_t voice_count_) : voice_count{voice_count_} {
|
||||
for (std::size_t i = 0; i < voice_count; i++) {
|
||||
voice_channel_resources.emplace_back(static_cast<s32>(i));
|
||||
sorted_voice_info.push_back(&voice_info.emplace_back());
|
||||
|
||||
@@ -86,28 +86,28 @@ struct BehaviorFlags {
|
||||
static_assert(sizeof(BehaviorFlags) == 0x4, "BehaviorFlags is an invalid size");
|
||||
|
||||
struct ADPCMContext {
|
||||
u16 header{};
|
||||
s16 yn1{};
|
||||
s16 yn2{};
|
||||
u16 header;
|
||||
s16 yn1;
|
||||
s16 yn2;
|
||||
};
|
||||
static_assert(sizeof(ADPCMContext) == 0x6, "ADPCMContext is an invalid size");
|
||||
|
||||
struct VoiceState {
|
||||
s64 played_sample_count{};
|
||||
s32 offset{};
|
||||
s32 wave_buffer_index{};
|
||||
std::array<bool, AudioCommon::MAX_WAVE_BUFFERS> is_wave_buffer_valid{};
|
||||
s32 wave_buffer_consumed{};
|
||||
std::array<s32, AudioCommon::MAX_SAMPLE_HISTORY> sample_history{};
|
||||
s32 fraction{};
|
||||
VAddr context_address{};
|
||||
Codec::ADPCM_Coeff coeff{};
|
||||
ADPCMContext context{};
|
||||
std::array<s64, 2> biquad_filter_state{};
|
||||
std::array<s32, AudioCommon::MAX_MIX_BUFFERS> previous_samples{};
|
||||
u32 external_context_size{};
|
||||
bool is_external_context_used{};
|
||||
bool voice_dropped{};
|
||||
s64 played_sample_count;
|
||||
s32 offset;
|
||||
s32 wave_buffer_index;
|
||||
std::array<bool, AudioCommon::MAX_WAVE_BUFFERS> is_wave_buffer_valid;
|
||||
s32 wave_buffer_consumed;
|
||||
std::array<s32, AudioCommon::MAX_SAMPLE_HISTORY> sample_history;
|
||||
s32 fraction;
|
||||
VAddr context_address;
|
||||
Codec::ADPCM_Coeff coeff;
|
||||
ADPCMContext context;
|
||||
std::array<s64, 2> biquad_filter_state;
|
||||
std::array<s32, AudioCommon::MAX_MIX_BUFFERS> previous_samples;
|
||||
u32 external_context_size;
|
||||
bool is_external_context_used;
|
||||
bool voice_dropped;
|
||||
};
|
||||
|
||||
class VoiceChannelResource {
|
||||
@@ -118,12 +118,12 @@ public:
|
||||
bool in_use{};
|
||||
INSERT_PADDING_BYTES(11);
|
||||
};
|
||||
static_assert(sizeof(VoiceChannelResource::InParams) == 0x70, "InParams is an invalid size");
|
||||
static_assert(sizeof(InParams) == 0x70, "InParams is an invalid size");
|
||||
};
|
||||
|
||||
class ServerVoiceChannelResource {
|
||||
public:
|
||||
explicit ServerVoiceChannelResource(s32 id);
|
||||
explicit ServerVoiceChannelResource(s32 id_);
|
||||
~ServerVoiceChannelResource();
|
||||
|
||||
bool InUse() const;
|
||||
@@ -174,7 +174,7 @@ public:
|
||||
BehaviorFlags behavior_flags{};
|
||||
INSERT_PADDING_BYTES(16);
|
||||
};
|
||||
static_assert(sizeof(VoiceInfo::InParams) == 0x170, "InParams is an invalid size");
|
||||
static_assert(sizeof(InParams) == 0x170, "InParams is an invalid size");
|
||||
|
||||
struct OutParams {
|
||||
u64_le played_sample_count{};
|
||||
@@ -182,7 +182,7 @@ public:
|
||||
u8 voice_dropped{};
|
||||
INSERT_PADDING_BYTES(3);
|
||||
};
|
||||
static_assert(sizeof(VoiceInfo::OutParams) == 0x10, "OutParams is an invalid size");
|
||||
static_assert(sizeof(OutParams) == 0x10, "OutParams is an invalid size");
|
||||
};
|
||||
|
||||
class ServerVoiceInfo {
|
||||
@@ -263,7 +263,7 @@ private:
|
||||
|
||||
class VoiceContext {
|
||||
public:
|
||||
VoiceContext(std::size_t voice_count);
|
||||
explicit VoiceContext(std::size_t voice_count_);
|
||||
~VoiceContext();
|
||||
|
||||
std::size_t GetVoiceCount() const;
|
||||
|
||||
@@ -98,16 +98,15 @@ add_library(common STATIC
|
||||
algorithm.h
|
||||
alignment.h
|
||||
assert.h
|
||||
atomic_ops.cpp
|
||||
atomic_ops.h
|
||||
detached_tasks.cpp
|
||||
detached_tasks.h
|
||||
bit_cast.h
|
||||
bit_field.h
|
||||
bit_set.h
|
||||
bit_util.h
|
||||
cityhash.cpp
|
||||
cityhash.h
|
||||
color.h
|
||||
common_funcs.h
|
||||
common_paths.h
|
||||
common_types.h
|
||||
@@ -122,6 +121,7 @@ add_library(common STATIC
|
||||
hash.h
|
||||
hex_util.cpp
|
||||
hex_util.h
|
||||
intrusive_red_black_tree.h
|
||||
logging/backend.cpp
|
||||
logging/backend.h
|
||||
logging/filter.cpp
|
||||
@@ -134,17 +134,17 @@ add_library(common STATIC
|
||||
math_util.h
|
||||
memory_detect.cpp
|
||||
memory_detect.h
|
||||
memory_hook.cpp
|
||||
memory_hook.h
|
||||
microprofile.cpp
|
||||
microprofile.h
|
||||
microprofileui.h
|
||||
misc.cpp
|
||||
multi_level_queue.h
|
||||
nvidia_flags.cpp
|
||||
nvidia_flags.h
|
||||
page_table.cpp
|
||||
page_table.h
|
||||
param_package.cpp
|
||||
param_package.h
|
||||
parent_of_member.h
|
||||
quaternion.h
|
||||
ring_buffer.h
|
||||
scm_rev.cpp
|
||||
@@ -162,11 +162,12 @@ add_library(common STATIC
|
||||
thread.cpp
|
||||
thread.h
|
||||
thread_queue_list.h
|
||||
thread_worker.cpp
|
||||
thread_worker.h
|
||||
threadsafe_queue.h
|
||||
time_zone.cpp
|
||||
time_zone.h
|
||||
timer.cpp
|
||||
timer.h
|
||||
tree.h
|
||||
uint128.cpp
|
||||
uint128.h
|
||||
uuid.cpp
|
||||
@@ -205,11 +206,12 @@ if (MSVC)
|
||||
else()
|
||||
target_compile_options(common PRIVATE
|
||||
-Werror
|
||||
|
||||
$<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>
|
||||
)
|
||||
endif()
|
||||
|
||||
create_target_directory_groups(common)
|
||||
find_package(Boost 1.71 COMPONENTS context headers REQUIRED)
|
||||
|
||||
target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile)
|
||||
target_link_libraries(common PRIVATE lz4::lz4 xbyak)
|
||||
|
||||
@@ -9,50 +9,45 @@
|
||||
namespace Common {
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr T AlignUp(T value, std::size_t size) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignUp(T value, size_t size) {
|
||||
auto mod{static_cast<T>(value % size)};
|
||||
value -= mod;
|
||||
return static_cast<T>(mod == T{0} ? value : value + size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr T AlignDown(T value, std::size_t size) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignUpLog2(T value, size_t align_log2) {
|
||||
return static_cast<T>((value + ((1ULL << align_log2) - 1)) >> align_log2 << align_log2);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignDown(T value, size_t size) {
|
||||
return static_cast<T>(value - value % size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr T AlignBits(T value, std::size_t align) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
return static_cast<T>((value + ((1ULL << align) - 1)) >> align << align);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr bool Is4KBAligned(T value) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
requires std::is_unsigned_v<T>[[nodiscard]] constexpr bool Is4KBAligned(T value) {
|
||||
return (value & 0xFFF) == 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr bool IsWordAligned(T value) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
requires std::is_unsigned_v<T>[[nodiscard]] constexpr bool IsWordAligned(T value) {
|
||||
return (value & 0b11) == 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr bool IsAligned(T value, std::size_t alignment) {
|
||||
using U = typename std::make_unsigned<T>::type;
|
||||
requires std::is_integral_v<T>[[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) {
|
||||
using U = typename std::make_unsigned_t<T>;
|
||||
const U mask = static_cast<U>(alignment - 1);
|
||||
return (value & mask) == 0;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t Align = 16>
|
||||
template <typename T, size_t Align = 16>
|
||||
class AlignmentAllocator {
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
|
||||
using propagate_on_container_copy_assignment = std::true_type;
|
||||
using propagate_on_container_move_assignment = std::true_type;
|
||||
|
||||
@@ -29,22 +29,19 @@ assert_noinline_call(const Fn& fn) {
|
||||
}
|
||||
|
||||
#define ASSERT(_a_) \
|
||||
do \
|
||||
if (!(_a_)) { \
|
||||
assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \
|
||||
} \
|
||||
while (0)
|
||||
if (!(_a_)) { \
|
||||
LOG_CRITICAL(Debug, "Assertion Failed!"); \
|
||||
}
|
||||
|
||||
#define ASSERT_MSG(_a_, ...) \
|
||||
do \
|
||||
if (!(_a_)) { \
|
||||
assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \
|
||||
} \
|
||||
while (0)
|
||||
if (!(_a_)) { \
|
||||
LOG_CRITICAL(Debug, "Assertion Failed! " __VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define UNREACHABLE() assert_noinline_call([] { LOG_CRITICAL(Debug, "Unreachable code!"); })
|
||||
#define UNREACHABLE() \
|
||||
{ LOG_CRITICAL(Debug, "Unreachable code!"); }
|
||||
#define UNREACHABLE_MSG(...) \
|
||||
assert_noinline_call([&] { LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); })
|
||||
{ LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); }
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define DEBUG_ASSERT(_a_) ASSERT(_a_)
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/atomic_ops.h"
|
||||
|
||||
#if _MSC_VER
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
#if _MSC_VER
|
||||
|
||||
bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
|
||||
const u8 result =
|
||||
_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
|
||||
return result == expected;
|
||||
}
|
||||
|
||||
bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
|
||||
const u16 result =
|
||||
_InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
|
||||
return result == expected;
|
||||
}
|
||||
|
||||
bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
|
||||
const u32 result =
|
||||
_InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
|
||||
return result == expected;
|
||||
}
|
||||
|
||||
bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
|
||||
const u64 result = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer),
|
||||
value, expected);
|
||||
return result == expected;
|
||||
}
|
||||
|
||||
bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
|
||||
return _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
|
||||
value[0],
|
||||
reinterpret_cast<__int64*>(expected.data())) != 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
|
||||
return __sync_bool_compare_and_swap(pointer, expected, value);
|
||||
}
|
||||
|
||||
bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
|
||||
return __sync_bool_compare_and_swap(pointer, expected, value);
|
||||
}
|
||||
|
||||
bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
|
||||
return __sync_bool_compare_and_swap(pointer, expected, value);
|
||||
}
|
||||
|
||||
bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
|
||||
return __sync_bool_compare_and_swap(pointer, expected, value);
|
||||
}
|
||||
|
||||
bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
|
||||
unsigned __int128 value_a;
|
||||
unsigned __int128 expected_a;
|
||||
std::memcpy(&value_a, value.data(), sizeof(u128));
|
||||
std::memcpy(&expected_a, expected.data(), sizeof(u128));
|
||||
return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace Common
|
||||
@@ -4,14 +4,75 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#if _MSC_VER
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
[[nodiscard]] bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected);
|
||||
[[nodiscard]] bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected);
|
||||
[[nodiscard]] bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected);
|
||||
[[nodiscard]] bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected);
|
||||
[[nodiscard]] bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected);
|
||||
#if _MSC_VER
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
|
||||
const u8 result =
|
||||
_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
|
||||
return result == expected;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
|
||||
const u16 result =
|
||||
_InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
|
||||
return result == expected;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
|
||||
const u32 result =
|
||||
_InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
|
||||
return result == expected;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
|
||||
const u64 result = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer),
|
||||
value, expected);
|
||||
return result == expected;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
|
||||
return _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
|
||||
value[0],
|
||||
reinterpret_cast<__int64*>(expected.data())) != 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
|
||||
return __sync_bool_compare_and_swap(pointer, expected, value);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
|
||||
return __sync_bool_compare_and_swap(pointer, expected, value);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
|
||||
return __sync_bool_compare_and_swap(pointer, expected, value);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
|
||||
return __sync_bool_compare_and_swap(pointer, expected, value);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
|
||||
unsigned __int128 value_a;
|
||||
unsigned __int128 expected_a;
|
||||
std::memcpy(&value_a, value.data(), sizeof(u128));
|
||||
std::memcpy(&expected_a, expected.data(), sizeof(u128));
|
||||
return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace Common
|
||||
|
||||
99
src/common/bit_set.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <bit>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/bit_util.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
namespace impl {
|
||||
|
||||
template <typename Storage, size_t N>
|
||||
class BitSet {
|
||||
|
||||
public:
|
||||
constexpr BitSet() = default;
|
||||
|
||||
constexpr void SetBit(size_t i) {
|
||||
this->words[i / FlagsPerWord] |= GetBitMask(i % FlagsPerWord);
|
||||
}
|
||||
|
||||
constexpr void ClearBit(size_t i) {
|
||||
this->words[i / FlagsPerWord] &= ~GetBitMask(i % FlagsPerWord);
|
||||
}
|
||||
|
||||
constexpr size_t CountLeadingZero() const {
|
||||
for (size_t i = 0; i < NumWords; i++) {
|
||||
if (this->words[i]) {
|
||||
return FlagsPerWord * i + CountLeadingZeroImpl(this->words[i]);
|
||||
}
|
||||
}
|
||||
return FlagsPerWord * NumWords;
|
||||
}
|
||||
|
||||
constexpr size_t GetNextSet(size_t n) const {
|
||||
for (size_t i = (n + 1) / FlagsPerWord; i < NumWords; i++) {
|
||||
Storage word = this->words[i];
|
||||
if (!IsAligned(n + 1, FlagsPerWord)) {
|
||||
word &= GetBitMask(n % FlagsPerWord) - 1;
|
||||
}
|
||||
if (word) {
|
||||
return FlagsPerWord * i + CountLeadingZeroImpl(word);
|
||||
}
|
||||
}
|
||||
return FlagsPerWord * NumWords;
|
||||
}
|
||||
|
||||
private:
|
||||
static_assert(std::is_unsigned_v<Storage>);
|
||||
static_assert(sizeof(Storage) <= sizeof(u64));
|
||||
|
||||
static constexpr size_t FlagsPerWord = BitSize<Storage>();
|
||||
static constexpr size_t NumWords = AlignUp(N, FlagsPerWord) / FlagsPerWord;
|
||||
|
||||
static constexpr auto CountLeadingZeroImpl(Storage word) {
|
||||
return std::countl_zero(static_cast<unsigned long long>(word)) -
|
||||
(BitSize<unsigned long long>() - FlagsPerWord);
|
||||
}
|
||||
|
||||
static constexpr Storage GetBitMask(size_t bit) {
|
||||
return Storage(1) << (FlagsPerWord - 1 - bit);
|
||||
}
|
||||
|
||||
std::array<Storage, NumWords> words{};
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
template <size_t N>
|
||||
using BitSet8 = impl::BitSet<u8, N>;
|
||||
|
||||
template <size_t N>
|
||||
using BitSet16 = impl::BitSet<u16, N>;
|
||||
|
||||
template <size_t N>
|
||||
using BitSet32 = impl::BitSet<u32, N>;
|
||||
|
||||
template <size_t N>
|
||||
using BitSet64 = impl::BitSet<u64, N>;
|
||||
|
||||
} // namespace Common
|
||||
@@ -4,13 +4,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bit>
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
@@ -21,124 +18,30 @@ template <typename T>
|
||||
return sizeof(T) * CHAR_BIT;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
[[nodiscard]] inline u32 CountLeadingZeroes32(u32 value) {
|
||||
unsigned long leading_zero = 0;
|
||||
|
||||
if (_BitScanReverse(&leading_zero, value) != 0) {
|
||||
return 31 - leading_zero;
|
||||
}
|
||||
|
||||
return 32;
|
||||
[[nodiscard]] constexpr u32 MostSignificantBit32(const u32 value) {
|
||||
return 31U - static_cast<u32>(std::countl_zero(value));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u32 CountLeadingZeroes64(u64 value) {
|
||||
unsigned long leading_zero = 0;
|
||||
|
||||
if (_BitScanReverse64(&leading_zero, value) != 0) {
|
||||
return 63 - leading_zero;
|
||||
}
|
||||
|
||||
return 64;
|
||||
}
|
||||
#else
|
||||
[[nodiscard]] inline u32 CountLeadingZeroes32(u32 value) {
|
||||
if (value == 0) {
|
||||
return 32;
|
||||
}
|
||||
|
||||
return static_cast<u32>(__builtin_clz(value));
|
||||
[[nodiscard]] constexpr u32 MostSignificantBit64(const u64 value) {
|
||||
return 63U - static_cast<u32>(std::countl_zero(value));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u32 CountLeadingZeroes64(u64 value) {
|
||||
if (value == 0) {
|
||||
return 64;
|
||||
}
|
||||
|
||||
return static_cast<u32>(__builtin_clzll(value));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
[[nodiscard]] inline u32 CountTrailingZeroes32(u32 value) {
|
||||
unsigned long trailing_zero = 0;
|
||||
|
||||
if (_BitScanForward(&trailing_zero, value) != 0) {
|
||||
return trailing_zero;
|
||||
}
|
||||
|
||||
return 32;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u32 CountTrailingZeroes64(u64 value) {
|
||||
unsigned long trailing_zero = 0;
|
||||
|
||||
if (_BitScanForward64(&trailing_zero, value) != 0) {
|
||||
return trailing_zero;
|
||||
}
|
||||
|
||||
return 64;
|
||||
}
|
||||
#else
|
||||
[[nodiscard]] inline u32 CountTrailingZeroes32(u32 value) {
|
||||
if (value == 0) {
|
||||
return 32;
|
||||
}
|
||||
|
||||
return static_cast<u32>(__builtin_ctz(value));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u32 CountTrailingZeroes64(u64 value) {
|
||||
if (value == 0) {
|
||||
return 64;
|
||||
}
|
||||
|
||||
return static_cast<u32>(__builtin_ctzll(value));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) {
|
||||
unsigned long result;
|
||||
_BitScanReverse(&result, value);
|
||||
return static_cast<u32>(result);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u32 MostSignificantBit64(const u64 value) {
|
||||
unsigned long result;
|
||||
_BitScanReverse64(&result, value);
|
||||
return static_cast<u32>(result);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) {
|
||||
return 31U - static_cast<u32>(__builtin_clz(value));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u32 MostSignificantBit64(const u64 value) {
|
||||
return 63U - static_cast<u32>(__builtin_clzll(value));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
[[nodiscard]] inline u32 Log2Floor32(const u32 value) {
|
||||
[[nodiscard]] constexpr u32 Log2Floor32(const u32 value) {
|
||||
return MostSignificantBit32(value);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u32 Log2Ceil32(const u32 value) {
|
||||
const u32 log2_f = Log2Floor32(value);
|
||||
return log2_f + ((value ^ (1U << log2_f)) != 0U);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u32 Log2Floor64(const u64 value) {
|
||||
[[nodiscard]] constexpr u32 Log2Floor64(const u64 value) {
|
||||
return MostSignificantBit64(value);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u32 Log2Ceil64(const u64 value) {
|
||||
const u64 log2_f = static_cast<u64>(Log2Floor64(value));
|
||||
return static_cast<u32>(log2_f + ((value ^ (1ULL << log2_f)) != 0ULL));
|
||||
[[nodiscard]] constexpr u32 Log2Ceil32(const u32 value) {
|
||||
const u32 log2_f = Log2Floor32(value);
|
||||
return log2_f + static_cast<u32>((value ^ (1U << log2_f)) != 0U);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr u32 Log2Ceil64(const u64 value) {
|
||||
const u64 log2_f = Log2Floor64(value);
|
||||
return static_cast<u32>(log2_f + static_cast<u64>((value ^ (1ULL << log2_f)) != 0ULL));
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -1,271 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "common/vector_math.h"
|
||||
|
||||
namespace Common::Color {
|
||||
|
||||
/// Convert a 1-bit color component to 8 bit
|
||||
[[nodiscard]] constexpr u8 Convert1To8(u8 value) {
|
||||
return value * 255;
|
||||
}
|
||||
|
||||
/// Convert a 4-bit color component to 8 bit
|
||||
[[nodiscard]] constexpr u8 Convert4To8(u8 value) {
|
||||
return (value << 4) | value;
|
||||
}
|
||||
|
||||
/// Convert a 5-bit color component to 8 bit
|
||||
[[nodiscard]] constexpr u8 Convert5To8(u8 value) {
|
||||
return (value << 3) | (value >> 2);
|
||||
}
|
||||
|
||||
/// Convert a 6-bit color component to 8 bit
|
||||
[[nodiscard]] constexpr u8 Convert6To8(u8 value) {
|
||||
return (value << 2) | (value >> 4);
|
||||
}
|
||||
|
||||
/// Convert a 8-bit color component to 1 bit
|
||||
[[nodiscard]] constexpr u8 Convert8To1(u8 value) {
|
||||
return value >> 7;
|
||||
}
|
||||
|
||||
/// Convert a 8-bit color component to 4 bit
|
||||
[[nodiscard]] constexpr u8 Convert8To4(u8 value) {
|
||||
return value >> 4;
|
||||
}
|
||||
|
||||
/// Convert a 8-bit color component to 5 bit
|
||||
[[nodiscard]] constexpr u8 Convert8To5(u8 value) {
|
||||
return value >> 3;
|
||||
}
|
||||
|
||||
/// Convert a 8-bit color component to 6 bit
|
||||
[[nodiscard]] constexpr u8 Convert8To6(u8 value) {
|
||||
return value >> 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a color stored in RGBA8 format
|
||||
* @param bytes Pointer to encoded source color
|
||||
* @return Result color decoded as Common::Vec4<u8>
|
||||
*/
|
||||
[[nodiscard]] inline Common::Vec4<u8> DecodeRGBA8(const u8* bytes) {
|
||||
return {bytes[3], bytes[2], bytes[1], bytes[0]};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a color stored in RGB8 format
|
||||
* @param bytes Pointer to encoded source color
|
||||
* @return Result color decoded as Common::Vec4<u8>
|
||||
*/
|
||||
[[nodiscard]] inline Common::Vec4<u8> DecodeRGB8(const u8* bytes) {
|
||||
return {bytes[2], bytes[1], bytes[0], 255};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a color stored in RG8 (aka HILO8) format
|
||||
* @param bytes Pointer to encoded source color
|
||||
* @return Result color decoded as Common::Vec4<u8>
|
||||
*/
|
||||
[[nodiscard]] inline Common::Vec4<u8> DecodeRG8(const u8* bytes) {
|
||||
return {bytes[1], bytes[0], 0, 255};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a color stored in RGB565 format
|
||||
* @param bytes Pointer to encoded source color
|
||||
* @return Result color decoded as Common::Vec4<u8>
|
||||
*/
|
||||
[[nodiscard]] inline Common::Vec4<u8> DecodeRGB565(const u8* bytes) {
|
||||
u16_le pixel;
|
||||
std::memcpy(&pixel, bytes, sizeof(pixel));
|
||||
return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F),
|
||||
Convert5To8(pixel & 0x1F), 255};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a color stored in RGB5A1 format
|
||||
* @param bytes Pointer to encoded source color
|
||||
* @return Result color decoded as Common::Vec4<u8>
|
||||
*/
|
||||
[[nodiscard]] inline Common::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
|
||||
u16_le pixel;
|
||||
std::memcpy(&pixel, bytes, sizeof(pixel));
|
||||
return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F),
|
||||
Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a color stored in RGBA4 format
|
||||
* @param bytes Pointer to encoded source color
|
||||
* @return Result color decoded as Common::Vec4<u8>
|
||||
*/
|
||||
[[nodiscard]] inline Common::Vec4<u8> DecodeRGBA4(const u8* bytes) {
|
||||
u16_le pixel;
|
||||
std::memcpy(&pixel, bytes, sizeof(pixel));
|
||||
return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF),
|
||||
Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a depth value stored in D16 format
|
||||
* @param bytes Pointer to encoded source value
|
||||
* @return Depth value as an u32
|
||||
*/
|
||||
[[nodiscard]] inline u32 DecodeD16(const u8* bytes) {
|
||||
u16_le data;
|
||||
std::memcpy(&data, bytes, sizeof(data));
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a depth value stored in D24 format
|
||||
* @param bytes Pointer to encoded source value
|
||||
* @return Depth value as an u32
|
||||
*/
|
||||
[[nodiscard]] inline u32 DecodeD24(const u8* bytes) {
|
||||
return (bytes[2] << 16) | (bytes[1] << 8) | bytes[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a depth value and a stencil value stored in D24S8 format
|
||||
* @param bytes Pointer to encoded source values
|
||||
* @return Resulting values stored as a Common::Vec2
|
||||
*/
|
||||
[[nodiscard]] inline Common::Vec2<u32> DecodeD24S8(const u8* bytes) {
|
||||
return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]};
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a color as RGBA8 format
|
||||
* @param color Source color to encode
|
||||
* @param bytes Destination pointer to store encoded color
|
||||
*/
|
||||
inline void EncodeRGBA8(const Common::Vec4<u8>& color, u8* bytes) {
|
||||
bytes[3] = color.r();
|
||||
bytes[2] = color.g();
|
||||
bytes[1] = color.b();
|
||||
bytes[0] = color.a();
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a color as RGB8 format
|
||||
* @param color Source color to encode
|
||||
* @param bytes Destination pointer to store encoded color
|
||||
*/
|
||||
inline void EncodeRGB8(const Common::Vec4<u8>& color, u8* bytes) {
|
||||
bytes[2] = color.r();
|
||||
bytes[1] = color.g();
|
||||
bytes[0] = color.b();
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a color as RG8 (aka HILO8) format
|
||||
* @param color Source color to encode
|
||||
* @param bytes Destination pointer to store encoded color
|
||||
*/
|
||||
inline void EncodeRG8(const Common::Vec4<u8>& color, u8* bytes) {
|
||||
bytes[1] = color.r();
|
||||
bytes[0] = color.g();
|
||||
}
|
||||
/**
|
||||
* Encode a color as RGB565 format
|
||||
* @param color Source color to encode
|
||||
* @param bytes Destination pointer to store encoded color
|
||||
*/
|
||||
inline void EncodeRGB565(const Common::Vec4<u8>& color, u8* bytes) {
|
||||
const u16_le data =
|
||||
(Convert8To5(color.r()) << 11) | (Convert8To6(color.g()) << 5) | Convert8To5(color.b());
|
||||
|
||||
std::memcpy(bytes, &data, sizeof(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a color as RGB5A1 format
|
||||
* @param color Source color to encode
|
||||
* @param bytes Destination pointer to store encoded color
|
||||
*/
|
||||
inline void EncodeRGB5A1(const Common::Vec4<u8>& color, u8* bytes) {
|
||||
const u16_le data = (Convert8To5(color.r()) << 11) | (Convert8To5(color.g()) << 6) |
|
||||
(Convert8To5(color.b()) << 1) | Convert8To1(color.a());
|
||||
|
||||
std::memcpy(bytes, &data, sizeof(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a color as RGBA4 format
|
||||
* @param color Source color to encode
|
||||
* @param bytes Destination pointer to store encoded color
|
||||
*/
|
||||
inline void EncodeRGBA4(const Common::Vec4<u8>& color, u8* bytes) {
|
||||
const u16 data = (Convert8To4(color.r()) << 12) | (Convert8To4(color.g()) << 8) |
|
||||
(Convert8To4(color.b()) << 4) | Convert8To4(color.a());
|
||||
|
||||
std::memcpy(bytes, &data, sizeof(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a 16 bit depth value as D16 format
|
||||
* @param value 16 bit source depth value to encode
|
||||
* @param bytes Pointer where to store the encoded value
|
||||
*/
|
||||
inline void EncodeD16(u32 value, u8* bytes) {
|
||||
const u16_le data = static_cast<u16>(value);
|
||||
std::memcpy(bytes, &data, sizeof(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a 24 bit depth value as D24 format
|
||||
* @param value 24 bit source depth value to encode
|
||||
* @param bytes Pointer where to store the encoded value
|
||||
*/
|
||||
inline void EncodeD24(u32 value, u8* bytes) {
|
||||
bytes[0] = value & 0xFF;
|
||||
bytes[1] = (value >> 8) & 0xFF;
|
||||
bytes[2] = (value >> 16) & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a 24 bit depth and 8 bit stencil values as D24S8 format
|
||||
* @param depth 24 bit source depth value to encode
|
||||
* @param stencil 8 bit source stencil value to encode
|
||||
* @param bytes Pointer where to store the encoded value
|
||||
*/
|
||||
inline void EncodeD24S8(u32 depth, u8 stencil, u8* bytes) {
|
||||
bytes[0] = depth & 0xFF;
|
||||
bytes[1] = (depth >> 8) & 0xFF;
|
||||
bytes[2] = (depth >> 16) & 0xFF;
|
||||
bytes[3] = stencil;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a 24 bit depth value as D24X8 format (32 bits per pixel with 8 bits unused)
|
||||
* @param depth 24 bit source depth value to encode
|
||||
* @param bytes Pointer where to store the encoded value
|
||||
* @note unused bits will not be modified
|
||||
*/
|
||||
inline void EncodeD24X8(u32 depth, u8* bytes) {
|
||||
bytes[0] = depth & 0xFF;
|
||||
bytes[1] = (depth >> 8) & 0xFF;
|
||||
bytes[2] = (depth >> 16) & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode an 8 bit stencil value as X24S8 format (32 bits per pixel with 24 bits unused)
|
||||
* @param stencil 8 bit source stencil value to encode
|
||||
* @param bytes Pointer where to store the encoded value
|
||||
* @note unused bits will not be modified
|
||||
*/
|
||||
inline void EncodeX24S8(u8 stencil, u8* bytes) {
|
||||
bytes[3] = stencil;
|
||||
}
|
||||
|
||||
} // namespace Common::Color
|
||||
@@ -24,10 +24,10 @@
|
||||
#define INSERT_PADDING_WORDS(num_words) \
|
||||
std::array<u32, num_words> CONCAT2(pad, __LINE__) {}
|
||||
|
||||
/// These are similar to the INSERT_PADDING_* macros, but are needed for padding unions. This is
|
||||
/// because unions can only be initialized by one member.
|
||||
#define INSERT_UNION_PADDING_BYTES(num_bytes) std::array<u8, num_bytes> CONCAT2(pad, __LINE__)
|
||||
#define INSERT_UNION_PADDING_WORDS(num_words) std::array<u32, num_words> CONCAT2(pad, __LINE__)
|
||||
/// These are similar to the INSERT_PADDING_* macros but do not zero-initialize the contents.
|
||||
/// This keeps the structure trivial to construct.
|
||||
#define INSERT_PADDING_BYTES_NOINIT(num_bytes) std::array<u8, num_bytes> CONCAT2(pad, __LINE__)
|
||||
#define INSERT_PADDING_WORDS_NOINIT(num_words) std::array<u32, num_words> CONCAT2(pad, __LINE__)
|
||||
|
||||
#ifndef _MSC_VER
|
||||
|
||||
@@ -93,6 +93,31 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
|
||||
return static_cast<T>(key) == 0; \
|
||||
}
|
||||
|
||||
/// Evaluates a boolean expression, and returns a result unless that expression is true.
|
||||
#define R_UNLESS(expr, res) \
|
||||
{ \
|
||||
if (!(expr)) { \
|
||||
if (res.IsError()) { \
|
||||
LOG_ERROR(Kernel, "Failed with result: {}", res.raw); \
|
||||
} \
|
||||
return res; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define R_SUCCEEDED(res) (res.IsSuccess())
|
||||
|
||||
/// Evaluates an expression that returns a result, and returns the result if it would fail.
|
||||
#define R_TRY(res_expr) \
|
||||
{ \
|
||||
const auto _tmp_r_try_rc = (res_expr); \
|
||||
if (_tmp_r_try_rc.IsError()) { \
|
||||
return _tmp_r_try_rc; \
|
||||
} \
|
||||
}
|
||||
|
||||
/// Evaluates a boolean expression, and succeeds if that expression is true.
|
||||
#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), RESULT_SUCCESS)
|
||||
|
||||
namespace Common {
|
||||
|
||||
[[nodiscard]] constexpr u32 MakeMagic(char a, char b, char c, char d) {
|
||||
|
||||
@@ -31,4 +31,8 @@ concept DerivedFrom = requires {
|
||||
std::is_convertible_v<const volatile Derived*, const volatile Base*>;
|
||||
};
|
||||
|
||||
// TODO: Replace with std::convertible_to when libc++ implements it.
|
||||
template <typename From, typename To>
|
||||
concept ConvertibleTo = std::is_convertible_v<From, To>;
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -11,16 +11,16 @@ namespace Common {
|
||||
|
||||
/// Ceiled integer division.
|
||||
template <typename N, typename D>
|
||||
requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr auto DivCeil(
|
||||
N number, D divisor) {
|
||||
return (static_cast<D>(number) + divisor - 1) / divisor;
|
||||
requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr N DivCeil(N number,
|
||||
D divisor) {
|
||||
return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor);
|
||||
}
|
||||
|
||||
/// Ceiled integer division with logarithmic divisor in base 2
|
||||
template <typename N, typename D>
|
||||
requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr auto DivCeilLog2(
|
||||
requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr N DivCeilLog2(
|
||||
N value, D alignment_log2) {
|
||||
return (static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2;
|
||||
return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -5,18 +5,20 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/fiber.h"
|
||||
#include "common/spin_lock.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <boost/context/detail/fcontext.hpp>
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
constexpr std::size_t default_stack_size = 256 * 1024; // 256kb
|
||||
constexpr std::size_t default_stack_size = 256 * 1024;
|
||||
|
||||
struct Fiber::FiberImpl {
|
||||
FiberImpl() : stack{default_stack_size}, rewind_stack{default_stack_size} {}
|
||||
|
||||
VirtualBuffer<u8> stack;
|
||||
VirtualBuffer<u8> rewind_stack;
|
||||
|
||||
SpinLock guard{};
|
||||
std::function<void(void*)> entry_point;
|
||||
std::function<void(void*)> rewind_point;
|
||||
@@ -26,17 +28,10 @@ struct Fiber::FiberImpl {
|
||||
bool is_thread_fiber{};
|
||||
bool released{};
|
||||
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
LPVOID handle = nullptr;
|
||||
LPVOID rewind_handle = nullptr;
|
||||
#else
|
||||
alignas(64) std::array<u8, default_stack_size> stack;
|
||||
alignas(64) std::array<u8, default_stack_size> rewind_stack;
|
||||
u8* stack_limit;
|
||||
u8* rewind_stack_limit;
|
||||
boost::context::detail::fcontext_t context;
|
||||
boost::context::detail::fcontext_t rewind_context;
|
||||
#endif
|
||||
u8* stack_limit{};
|
||||
u8* rewind_stack_limit{};
|
||||
boost::context::detail::fcontext_t context{};
|
||||
boost::context::detail::fcontext_t rewind_context{};
|
||||
};
|
||||
|
||||
void Fiber::SetStartParameter(void* new_parameter) {
|
||||
@@ -48,95 +43,6 @@ void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewin
|
||||
impl->rewind_parameter = rewind_param;
|
||||
}
|
||||
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
|
||||
void Fiber::Start() {
|
||||
ASSERT(impl->previous_fiber != nullptr);
|
||||
impl->previous_fiber->impl->guard.unlock();
|
||||
impl->previous_fiber.reset();
|
||||
impl->entry_point(impl->start_parameter);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void Fiber::OnRewind() {
|
||||
ASSERT(impl->handle != nullptr);
|
||||
DeleteFiber(impl->handle);
|
||||
impl->handle = impl->rewind_handle;
|
||||
impl->rewind_handle = nullptr;
|
||||
impl->rewind_point(impl->rewind_parameter);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void Fiber::FiberStartFunc(void* fiber_parameter) {
|
||||
auto* fiber = static_cast<Fiber*>(fiber_parameter);
|
||||
fiber->Start();
|
||||
}
|
||||
|
||||
void Fiber::RewindStartFunc(void* fiber_parameter) {
|
||||
auto* fiber = static_cast<Fiber*>(fiber_parameter);
|
||||
fiber->OnRewind();
|
||||
}
|
||||
|
||||
Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
|
||||
: impl{std::make_unique<FiberImpl>()} {
|
||||
impl->entry_point = std::move(entry_point_func);
|
||||
impl->start_parameter = start_parameter;
|
||||
impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this);
|
||||
}
|
||||
|
||||
Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
|
||||
|
||||
Fiber::~Fiber() {
|
||||
if (impl->released) {
|
||||
return;
|
||||
}
|
||||
// Make sure the Fiber is not being used
|
||||
const bool locked = impl->guard.try_lock();
|
||||
ASSERT_MSG(locked, "Destroying a fiber that's still running");
|
||||
if (locked) {
|
||||
impl->guard.unlock();
|
||||
}
|
||||
DeleteFiber(impl->handle);
|
||||
}
|
||||
|
||||
void Fiber::Exit() {
|
||||
ASSERT_MSG(impl->is_thread_fiber, "Exitting non main thread fiber");
|
||||
if (!impl->is_thread_fiber) {
|
||||
return;
|
||||
}
|
||||
ConvertFiberToThread();
|
||||
impl->guard.unlock();
|
||||
impl->released = true;
|
||||
}
|
||||
|
||||
void Fiber::Rewind() {
|
||||
ASSERT(impl->rewind_point);
|
||||
ASSERT(impl->rewind_handle == nullptr);
|
||||
impl->rewind_handle = CreateFiber(default_stack_size, &RewindStartFunc, this);
|
||||
SwitchToFiber(impl->rewind_handle);
|
||||
}
|
||||
|
||||
void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
|
||||
ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
|
||||
ASSERT_MSG(to != nullptr, "Next fiber is null!");
|
||||
to->impl->guard.lock();
|
||||
to->impl->previous_fiber = from;
|
||||
SwitchToFiber(to->impl->handle);
|
||||
ASSERT(from->impl->previous_fiber != nullptr);
|
||||
from->impl->previous_fiber->impl->guard.unlock();
|
||||
from->impl->previous_fiber.reset();
|
||||
}
|
||||
|
||||
std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
|
||||
std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
|
||||
fiber->impl->guard.lock();
|
||||
fiber->impl->handle = ConvertThreadToFiber(nullptr);
|
||||
fiber->impl->is_thread_fiber = true;
|
||||
return fiber;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void Fiber::Start(boost::context::detail::transfer_t& transfer) {
|
||||
ASSERT(impl->previous_fiber != nullptr);
|
||||
impl->previous_fiber->impl->context = transfer.fctx;
|
||||
@@ -229,5 +135,4 @@ std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
|
||||
return fiber;
|
||||
}
|
||||
|
||||
#endif
|
||||
} // namespace Common
|
||||
|
||||
@@ -7,11 +7,9 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#if !defined(_WIN32) && !defined(WIN32)
|
||||
namespace boost::context::detail {
|
||||
struct transfer_t;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
@@ -59,17 +57,10 @@ public:
|
||||
private:
|
||||
Fiber();
|
||||
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
void OnRewind();
|
||||
void Start();
|
||||
static void FiberStartFunc(void* fiber_parameter);
|
||||
static void RewindStartFunc(void* fiber_parameter);
|
||||
#else
|
||||
void OnRewind(boost::context::detail::transfer_t& transfer);
|
||||
void Start(boost::context::detail::transfer_t& transfer);
|
||||
static void FiberStartFunc(boost::context::detail::transfer_t transfer);
|
||||
static void RewindStartFunc(boost::context::detail::transfer_t transfer);
|
||||
#endif
|
||||
|
||||
struct FiberImpl;
|
||||
std::unique_ptr<FiberImpl> impl;
|
||||
|
||||
602
src/common/intrusive_red_black_tree.h
Normal file
@@ -0,0 +1,602 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/parent_of_member.h"
|
||||
#include "common/tree.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
namespace impl {
|
||||
|
||||
class IntrusiveRedBlackTreeImpl;
|
||||
|
||||
}
|
||||
|
||||
struct IntrusiveRedBlackTreeNode {
|
||||
public:
|
||||
using EntryType = RBEntry<IntrusiveRedBlackTreeNode>;
|
||||
|
||||
constexpr IntrusiveRedBlackTreeNode() = default;
|
||||
|
||||
void SetEntry(const EntryType& new_entry) {
|
||||
entry = new_entry;
|
||||
}
|
||||
|
||||
[[nodiscard]] EntryType& GetEntry() {
|
||||
return entry;
|
||||
}
|
||||
|
||||
[[nodiscard]] const EntryType& GetEntry() const {
|
||||
return entry;
|
||||
}
|
||||
|
||||
private:
|
||||
EntryType entry{};
|
||||
|
||||
friend class impl::IntrusiveRedBlackTreeImpl;
|
||||
|
||||
template <class, class, class>
|
||||
friend class IntrusiveRedBlackTree;
|
||||
};
|
||||
|
||||
template <class T, class Traits, class Comparator>
|
||||
class IntrusiveRedBlackTree;
|
||||
|
||||
namespace impl {
|
||||
|
||||
class IntrusiveRedBlackTreeImpl {
|
||||
private:
|
||||
template <class, class, class>
|
||||
friend class ::Common::IntrusiveRedBlackTree;
|
||||
|
||||
using RootType = RBHead<IntrusiveRedBlackTreeNode>;
|
||||
RootType root;
|
||||
|
||||
public:
|
||||
template <bool Const>
|
||||
class Iterator;
|
||||
|
||||
using value_type = IntrusiveRedBlackTreeNode;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const value_type*;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using iterator = Iterator<false>;
|
||||
using const_iterator = Iterator<true>;
|
||||
|
||||
template <bool Const>
|
||||
class Iterator {
|
||||
public:
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using value_type = typename IntrusiveRedBlackTreeImpl::value_type;
|
||||
using difference_type = typename IntrusiveRedBlackTreeImpl::difference_type;
|
||||
using pointer = std::conditional_t<Const, IntrusiveRedBlackTreeImpl::const_pointer,
|
||||
IntrusiveRedBlackTreeImpl::pointer>;
|
||||
using reference = std::conditional_t<Const, IntrusiveRedBlackTreeImpl::const_reference,
|
||||
IntrusiveRedBlackTreeImpl::reference>;
|
||||
|
||||
private:
|
||||
pointer node;
|
||||
|
||||
public:
|
||||
explicit Iterator(pointer n) : node(n) {}
|
||||
|
||||
bool operator==(const Iterator& rhs) const {
|
||||
return this->node == rhs.node;
|
||||
}
|
||||
|
||||
bool operator!=(const Iterator& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
pointer operator->() const {
|
||||
return this->node;
|
||||
}
|
||||
|
||||
reference operator*() const {
|
||||
return *this->node;
|
||||
}
|
||||
|
||||
Iterator& operator++() {
|
||||
this->node = GetNext(this->node);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator& operator--() {
|
||||
this->node = GetPrev(this->node);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator operator++(int) {
|
||||
const Iterator it{*this};
|
||||
++(*this);
|
||||
return it;
|
||||
}
|
||||
|
||||
Iterator operator--(int) {
|
||||
const Iterator it{*this};
|
||||
--(*this);
|
||||
return it;
|
||||
}
|
||||
|
||||
operator Iterator<true>() const {
|
||||
return Iterator<true>(this->node);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
// Define accessors using RB_* functions.
|
||||
bool EmptyImpl() const {
|
||||
return root.IsEmpty();
|
||||
}
|
||||
|
||||
IntrusiveRedBlackTreeNode* GetMinImpl() const {
|
||||
return RB_MIN(const_cast<RootType*>(&root));
|
||||
}
|
||||
|
||||
IntrusiveRedBlackTreeNode* GetMaxImpl() const {
|
||||
return RB_MAX(const_cast<RootType*>(&root));
|
||||
}
|
||||
|
||||
IntrusiveRedBlackTreeNode* RemoveImpl(IntrusiveRedBlackTreeNode* node) {
|
||||
return RB_REMOVE(&root, node);
|
||||
}
|
||||
|
||||
public:
|
||||
static IntrusiveRedBlackTreeNode* GetNext(IntrusiveRedBlackTreeNode* node) {
|
||||
return RB_NEXT(node);
|
||||
}
|
||||
|
||||
static IntrusiveRedBlackTreeNode* GetPrev(IntrusiveRedBlackTreeNode* node) {
|
||||
return RB_PREV(node);
|
||||
}
|
||||
|
||||
static const IntrusiveRedBlackTreeNode* GetNext(const IntrusiveRedBlackTreeNode* node) {
|
||||
return static_cast<const IntrusiveRedBlackTreeNode*>(
|
||||
GetNext(const_cast<IntrusiveRedBlackTreeNode*>(node)));
|
||||
}
|
||||
|
||||
static const IntrusiveRedBlackTreeNode* GetPrev(const IntrusiveRedBlackTreeNode* node) {
|
||||
return static_cast<const IntrusiveRedBlackTreeNode*>(
|
||||
GetPrev(const_cast<IntrusiveRedBlackTreeNode*>(node)));
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr IntrusiveRedBlackTreeImpl() {}
|
||||
|
||||
// Iterator accessors.
|
||||
iterator begin() {
|
||||
return iterator(this->GetMinImpl());
|
||||
}
|
||||
|
||||
const_iterator begin() const {
|
||||
return const_iterator(this->GetMinImpl());
|
||||
}
|
||||
|
||||
iterator end() {
|
||||
return iterator(static_cast<IntrusiveRedBlackTreeNode*>(nullptr));
|
||||
}
|
||||
|
||||
const_iterator end() const {
|
||||
return const_iterator(static_cast<const IntrusiveRedBlackTreeNode*>(nullptr));
|
||||
}
|
||||
|
||||
const_iterator cbegin() const {
|
||||
return this->begin();
|
||||
}
|
||||
|
||||
const_iterator cend() const {
|
||||
return this->end();
|
||||
}
|
||||
|
||||
iterator iterator_to(reference ref) {
|
||||
return iterator(&ref);
|
||||
}
|
||||
|
||||
const_iterator iterator_to(const_reference ref) const {
|
||||
return const_iterator(&ref);
|
||||
}
|
||||
|
||||
// Content management.
|
||||
bool empty() const {
|
||||
return this->EmptyImpl();
|
||||
}
|
||||
|
||||
reference back() {
|
||||
return *this->GetMaxImpl();
|
||||
}
|
||||
|
||||
const_reference back() const {
|
||||
return *this->GetMaxImpl();
|
||||
}
|
||||
|
||||
reference front() {
|
||||
return *this->GetMinImpl();
|
||||
}
|
||||
|
||||
const_reference front() const {
|
||||
return *this->GetMinImpl();
|
||||
}
|
||||
|
||||
iterator erase(iterator it) {
|
||||
auto cur = std::addressof(*it);
|
||||
auto next = GetNext(cur);
|
||||
this->RemoveImpl(cur);
|
||||
return iterator(next);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
template <typename T>
|
||||
concept HasLightCompareType = requires {
|
||||
{ std::is_same<typename T::LightCompareType, void>::value }
|
||||
->std::convertible_to<bool>;
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
|
||||
template <typename T, typename Default>
|
||||
consteval auto* GetLightCompareType() {
|
||||
if constexpr (HasLightCompareType<T>) {
|
||||
return static_cast<typename T::LightCompareType*>(nullptr);
|
||||
} else {
|
||||
return static_cast<Default*>(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace impl
|
||||
|
||||
template <typename T, typename Default>
|
||||
using LightCompareType = std::remove_pointer_t<decltype(impl::GetLightCompareType<T, Default>())>;
|
||||
|
||||
template <class T, class Traits, class Comparator>
|
||||
class IntrusiveRedBlackTree {
|
||||
|
||||
public:
|
||||
using ImplType = impl::IntrusiveRedBlackTreeImpl;
|
||||
|
||||
private:
|
||||
ImplType impl{};
|
||||
|
||||
public:
|
||||
template <bool Const>
|
||||
class Iterator;
|
||||
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using iterator = Iterator<false>;
|
||||
using const_iterator = Iterator<true>;
|
||||
|
||||
using light_value_type = LightCompareType<Comparator, value_type>;
|
||||
using const_light_pointer = const light_value_type*;
|
||||
using const_light_reference = const light_value_type&;
|
||||
|
||||
template <bool Const>
|
||||
class Iterator {
|
||||
public:
|
||||
friend class IntrusiveRedBlackTree<T, Traits, Comparator>;
|
||||
|
||||
using ImplIterator =
|
||||
std::conditional_t<Const, ImplType::const_iterator, ImplType::iterator>;
|
||||
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using value_type = typename IntrusiveRedBlackTree::value_type;
|
||||
using difference_type = typename IntrusiveRedBlackTree::difference_type;
|
||||
using pointer = std::conditional_t<Const, IntrusiveRedBlackTree::const_pointer,
|
||||
IntrusiveRedBlackTree::pointer>;
|
||||
using reference = std::conditional_t<Const, IntrusiveRedBlackTree::const_reference,
|
||||
IntrusiveRedBlackTree::reference>;
|
||||
|
||||
private:
|
||||
ImplIterator iterator;
|
||||
|
||||
private:
|
||||
explicit Iterator(ImplIterator it) : iterator(it) {}
|
||||
|
||||
explicit Iterator(typename std::conditional<Const, ImplType::const_iterator,
|
||||
ImplType::iterator>::type::pointer ptr)
|
||||
: iterator(ptr) {}
|
||||
|
||||
ImplIterator GetImplIterator() const {
|
||||
return this->iterator;
|
||||
}
|
||||
|
||||
public:
|
||||
bool operator==(const Iterator& rhs) const {
|
||||
return this->iterator == rhs.iterator;
|
||||
}
|
||||
|
||||
bool operator!=(const Iterator& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
pointer operator->() const {
|
||||
return Traits::GetParent(std::addressof(*this->iterator));
|
||||
}
|
||||
|
||||
reference operator*() const {
|
||||
return *Traits::GetParent(std::addressof(*this->iterator));
|
||||
}
|
||||
|
||||
Iterator& operator++() {
|
||||
++this->iterator;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator& operator--() {
|
||||
--this->iterator;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator operator++(int) {
|
||||
const Iterator it{*this};
|
||||
++this->iterator;
|
||||
return it;
|
||||
}
|
||||
|
||||
Iterator operator--(int) {
|
||||
const Iterator it{*this};
|
||||
--this->iterator;
|
||||
return it;
|
||||
}
|
||||
|
||||
operator Iterator<true>() const {
|
||||
return Iterator<true>(this->iterator);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
static int CompareImpl(const IntrusiveRedBlackTreeNode* lhs,
|
||||
const IntrusiveRedBlackTreeNode* rhs) {
|
||||
return Comparator::Compare(*Traits::GetParent(lhs), *Traits::GetParent(rhs));
|
||||
}
|
||||
|
||||
static int LightCompareImpl(const void* elm, const IntrusiveRedBlackTreeNode* rhs) {
|
||||
return Comparator::Compare(*static_cast<const_light_pointer>(elm), *Traits::GetParent(rhs));
|
||||
}
|
||||
|
||||
// Define accessors using RB_* functions.
|
||||
IntrusiveRedBlackTreeNode* InsertImpl(IntrusiveRedBlackTreeNode* node) {
|
||||
return RB_INSERT(&impl.root, node, CompareImpl);
|
||||
}
|
||||
|
||||
IntrusiveRedBlackTreeNode* FindImpl(const IntrusiveRedBlackTreeNode* node) const {
|
||||
return RB_FIND(const_cast<ImplType::RootType*>(&impl.root),
|
||||
const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
|
||||
}
|
||||
|
||||
IntrusiveRedBlackTreeNode* NFindImpl(const IntrusiveRedBlackTreeNode* node) const {
|
||||
return RB_NFIND(const_cast<ImplType::RootType*>(&impl.root),
|
||||
const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
|
||||
}
|
||||
|
||||
IntrusiveRedBlackTreeNode* FindLightImpl(const_light_pointer lelm) const {
|
||||
return RB_FIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root),
|
||||
static_cast<const void*>(lelm), LightCompareImpl);
|
||||
}
|
||||
|
||||
IntrusiveRedBlackTreeNode* NFindLightImpl(const_light_pointer lelm) const {
|
||||
return RB_NFIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root),
|
||||
static_cast<const void*>(lelm), LightCompareImpl);
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr IntrusiveRedBlackTree() = default;
|
||||
|
||||
// Iterator accessors.
|
||||
iterator begin() {
|
||||
return iterator(this->impl.begin());
|
||||
}
|
||||
|
||||
const_iterator begin() const {
|
||||
return const_iterator(this->impl.begin());
|
||||
}
|
||||
|
||||
iterator end() {
|
||||
return iterator(this->impl.end());
|
||||
}
|
||||
|
||||
const_iterator end() const {
|
||||
return const_iterator(this->impl.end());
|
||||
}
|
||||
|
||||
const_iterator cbegin() const {
|
||||
return this->begin();
|
||||
}
|
||||
|
||||
const_iterator cend() const {
|
||||
return this->end();
|
||||
}
|
||||
|
||||
iterator iterator_to(reference ref) {
|
||||
return iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref))));
|
||||
}
|
||||
|
||||
const_iterator iterator_to(const_reference ref) const {
|
||||
return const_iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref))));
|
||||
}
|
||||
|
||||
// Content management.
|
||||
bool empty() const {
|
||||
return this->impl.empty();
|
||||
}
|
||||
|
||||
reference back() {
|
||||
return *Traits::GetParent(std::addressof(this->impl.back()));
|
||||
}
|
||||
|
||||
const_reference back() const {
|
||||
return *Traits::GetParent(std::addressof(this->impl.back()));
|
||||
}
|
||||
|
||||
reference front() {
|
||||
return *Traits::GetParent(std::addressof(this->impl.front()));
|
||||
}
|
||||
|
||||
const_reference front() const {
|
||||
return *Traits::GetParent(std::addressof(this->impl.front()));
|
||||
}
|
||||
|
||||
iterator erase(iterator it) {
|
||||
return iterator(this->impl.erase(it.GetImplIterator()));
|
||||
}
|
||||
|
||||
iterator insert(reference ref) {
|
||||
ImplType::pointer node = Traits::GetNode(std::addressof(ref));
|
||||
this->InsertImpl(node);
|
||||
return iterator(node);
|
||||
}
|
||||
|
||||
iterator find(const_reference ref) const {
|
||||
return iterator(this->FindImpl(Traits::GetNode(std::addressof(ref))));
|
||||
}
|
||||
|
||||
iterator nfind(const_reference ref) const {
|
||||
return iterator(this->NFindImpl(Traits::GetNode(std::addressof(ref))));
|
||||
}
|
||||
|
||||
iterator find_light(const_light_reference ref) const {
|
||||
return iterator(this->FindLightImpl(std::addressof(ref)));
|
||||
}
|
||||
|
||||
iterator nfind_light(const_light_reference ref) const {
|
||||
return iterator(this->NFindLightImpl(std::addressof(ref)));
|
||||
}
|
||||
};
|
||||
|
||||
template <auto T, class Derived = impl::GetParentType<T>>
|
||||
class IntrusiveRedBlackTreeMemberTraits;
|
||||
|
||||
template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived>
|
||||
class IntrusiveRedBlackTreeMemberTraits<Member, Derived> {
|
||||
public:
|
||||
template <class Comparator>
|
||||
using TreeType = IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeMemberTraits, Comparator>;
|
||||
using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl;
|
||||
|
||||
private:
|
||||
template <class, class, class>
|
||||
friend class IntrusiveRedBlackTree;
|
||||
|
||||
friend class impl::IntrusiveRedBlackTreeImpl;
|
||||
|
||||
static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) {
|
||||
return std::addressof(parent->*Member);
|
||||
}
|
||||
|
||||
static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) {
|
||||
return std::addressof(parent->*Member);
|
||||
}
|
||||
|
||||
static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
|
||||
return GetParentPointer<Member, Derived>(node);
|
||||
}
|
||||
|
||||
static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) {
|
||||
return GetParentPointer<Member, Derived>(node);
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr TypedStorage<Derived> DerivedStorage = {};
|
||||
static_assert(GetParent(GetNode(GetPointer(DerivedStorage))) == GetPointer(DerivedStorage));
|
||||
};
|
||||
|
||||
template <auto T, class Derived = impl::GetParentType<T>>
|
||||
class IntrusiveRedBlackTreeMemberTraitsDeferredAssert;
|
||||
|
||||
template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived>
|
||||
class IntrusiveRedBlackTreeMemberTraitsDeferredAssert<Member, Derived> {
|
||||
public:
|
||||
template <class Comparator>
|
||||
using TreeType =
|
||||
IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeMemberTraitsDeferredAssert, Comparator>;
|
||||
using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl;
|
||||
|
||||
static constexpr bool IsValid() {
|
||||
TypedStorage<Derived> DerivedStorage = {};
|
||||
return GetParent(GetNode(GetPointer(DerivedStorage))) == GetPointer(DerivedStorage);
|
||||
}
|
||||
|
||||
private:
|
||||
template <class, class, class>
|
||||
friend class IntrusiveRedBlackTree;
|
||||
|
||||
friend class impl::IntrusiveRedBlackTreeImpl;
|
||||
|
||||
static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) {
|
||||
return std::addressof(parent->*Member);
|
||||
}
|
||||
|
||||
static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) {
|
||||
return std::addressof(parent->*Member);
|
||||
}
|
||||
|
||||
static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
|
||||
return GetParentPointer<Member, Derived>(node);
|
||||
}
|
||||
|
||||
static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) {
|
||||
return GetParentPointer<Member, Derived>(node);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Derived>
|
||||
class IntrusiveRedBlackTreeBaseNode : public IntrusiveRedBlackTreeNode {
|
||||
public:
|
||||
constexpr Derived* GetPrev() {
|
||||
return static_cast<Derived*>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this));
|
||||
}
|
||||
constexpr const Derived* GetPrev() const {
|
||||
return static_cast<const Derived*>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this));
|
||||
}
|
||||
|
||||
constexpr Derived* GetNext() {
|
||||
return static_cast<Derived*>(impl::IntrusiveRedBlackTreeImpl::GetNext(this));
|
||||
}
|
||||
constexpr const Derived* GetNext() const {
|
||||
return static_cast<const Derived*>(impl::IntrusiveRedBlackTreeImpl::GetNext(this));
|
||||
}
|
||||
};
|
||||
|
||||
template <class Derived>
|
||||
class IntrusiveRedBlackTreeBaseTraits {
|
||||
public:
|
||||
template <class Comparator>
|
||||
using TreeType = IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeBaseTraits, Comparator>;
|
||||
using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl;
|
||||
|
||||
private:
|
||||
template <class, class, class>
|
||||
friend class IntrusiveRedBlackTree;
|
||||
|
||||
friend class impl::IntrusiveRedBlackTreeImpl;
|
||||
|
||||
static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) {
|
||||
return static_cast<IntrusiveRedBlackTreeNode*>(parent);
|
||||
}
|
||||
|
||||
static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) {
|
||||
return static_cast<const IntrusiveRedBlackTreeNode*>(parent);
|
||||
}
|
||||
|
||||
static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
|
||||
return static_cast<Derived*>(node);
|
||||
}
|
||||
|
||||
static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) {
|
||||
return static_cast<const Derived*>(node);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -145,10 +145,18 @@ void ColorConsoleBackend::Write(const Entry& entry) {
|
||||
PrintColoredMessage(entry);
|
||||
}
|
||||
|
||||
// _SH_DENYWR allows read only access to the file for other programs.
|
||||
// It is #defined to 0 on other platforms
|
||||
FileBackend::FileBackend(const std::string& filename)
|
||||
: file(filename, "w", _SH_DENYWR), bytes_written(0) {}
|
||||
FileBackend::FileBackend(const std::string& filename) : bytes_written(0) {
|
||||
if (Common::FS::Exists(filename + ".old.txt")) {
|
||||
Common::FS::Delete(filename + ".old.txt");
|
||||
}
|
||||
if (Common::FS::Exists(filename)) {
|
||||
Common::FS::Rename(filename, filename + ".old.txt");
|
||||
}
|
||||
|
||||
// _SH_DENYWR allows read only access to the file for other programs.
|
||||
// It is #defined to 0 on other platforms
|
||||
file = Common::FS::IOFile(filename, "w", _SH_DENYWR);
|
||||
}
|
||||
|
||||
void FileBackend::Write(const Entry& entry) {
|
||||
// prevent logs from going over the maximum size (in case its spamming and the user doesn't
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright 2018 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/memory_hook.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
MemoryHook::~MemoryHook() = default;
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,47 +0,0 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
/**
|
||||
* Memory hooks have two purposes:
|
||||
* 1. To allow reads and writes to a region of memory to be intercepted. This is used to implement
|
||||
* texture forwarding and memory breakpoints for debugging.
|
||||
* 2. To allow for the implementation of MMIO devices.
|
||||
*
|
||||
* A hook may be mapped to multiple regions of memory.
|
||||
*
|
||||
* If a std::nullopt or false is returned from a function, the read/write request is passed through
|
||||
* to the underlying memory region.
|
||||
*/
|
||||
class MemoryHook {
|
||||
public:
|
||||
virtual ~MemoryHook();
|
||||
|
||||
virtual std::optional<bool> IsValidAddress(VAddr addr) = 0;
|
||||
|
||||
virtual std::optional<u8> Read8(VAddr addr) = 0;
|
||||
virtual std::optional<u16> Read16(VAddr addr) = 0;
|
||||
virtual std::optional<u32> Read32(VAddr addr) = 0;
|
||||
virtual std::optional<u64> Read64(VAddr addr) = 0;
|
||||
|
||||
virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) = 0;
|
||||
|
||||
virtual bool Write8(VAddr addr, u8 data) = 0;
|
||||
virtual bool Write16(VAddr addr, u16 data) = 0;
|
||||
virtual bool Write32(VAddr addr, u32 data) = 0;
|
||||
virtual bool Write64(VAddr addr, u64 data) = 0;
|
||||
|
||||
virtual bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) = 0;
|
||||
};
|
||||
|
||||
using MemoryHookPointer = std::shared_ptr<MemoryHook>;
|
||||
} // namespace Common
|
||||
@@ -1,345 +0,0 @@
|
||||
// Copyright 2019 TuxSH
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <iterator>
|
||||
#include <list>
|
||||
#include <utility>
|
||||
|
||||
#include "common/bit_util.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
/**
|
||||
* A MultiLevelQueue is a type of priority queue which has the following characteristics:
|
||||
* - iteratable through each of its elements.
|
||||
* - back can be obtained.
|
||||
* - O(1) add, lookup (both front and back)
|
||||
* - discrete priorities and a max of 64 priorities (limited domain)
|
||||
* This type of priority queue is normaly used for managing threads within an scheduler
|
||||
*/
|
||||
template <typename T, std::size_t Depth>
|
||||
class MultiLevelQueue {
|
||||
public:
|
||||
using value_type = T;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const value_type*;
|
||||
|
||||
using difference_type = typename std::pointer_traits<pointer>::difference_type;
|
||||
using size_type = std::size_t;
|
||||
|
||||
template <bool is_constant>
|
||||
class iterator_impl {
|
||||
public:
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using value_type = T;
|
||||
using pointer = std::conditional_t<is_constant, T*, const T*>;
|
||||
using reference = std::conditional_t<is_constant, const T&, T&>;
|
||||
using difference_type = typename std::pointer_traits<pointer>::difference_type;
|
||||
|
||||
friend bool operator==(const iterator_impl& lhs, const iterator_impl& rhs) {
|
||||
if (lhs.IsEnd() && rhs.IsEnd())
|
||||
return true;
|
||||
return std::tie(lhs.current_priority, lhs.it) == std::tie(rhs.current_priority, rhs.it);
|
||||
}
|
||||
|
||||
friend bool operator!=(const iterator_impl& lhs, const iterator_impl& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
reference operator*() const {
|
||||
return *it;
|
||||
}
|
||||
|
||||
pointer operator->() const {
|
||||
return it.operator->();
|
||||
}
|
||||
|
||||
iterator_impl& operator++() {
|
||||
if (IsEnd()) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
++it;
|
||||
|
||||
if (it == GetEndItForPrio()) {
|
||||
u64 prios = mlq.used_priorities;
|
||||
prios &= ~((1ULL << (current_priority + 1)) - 1);
|
||||
if (prios == 0) {
|
||||
current_priority = static_cast<u32>(mlq.depth());
|
||||
} else {
|
||||
current_priority = CountTrailingZeroes64(prios);
|
||||
it = GetBeginItForPrio();
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator_impl& operator--() {
|
||||
if (IsEnd()) {
|
||||
if (mlq.used_priorities != 0) {
|
||||
current_priority = 63 - CountLeadingZeroes64(mlq.used_priorities);
|
||||
it = GetEndItForPrio();
|
||||
--it;
|
||||
}
|
||||
} else if (it == GetBeginItForPrio()) {
|
||||
u64 prios = mlq.used_priorities;
|
||||
prios &= (1ULL << current_priority) - 1;
|
||||
if (prios != 0) {
|
||||
current_priority = CountTrailingZeroes64(prios);
|
||||
it = GetEndItForPrio();
|
||||
--it;
|
||||
}
|
||||
} else {
|
||||
--it;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator_impl operator++(int) {
|
||||
const iterator_impl v{*this};
|
||||
++(*this);
|
||||
return v;
|
||||
}
|
||||
|
||||
iterator_impl operator--(int) {
|
||||
const iterator_impl v{*this};
|
||||
--(*this);
|
||||
return v;
|
||||
}
|
||||
|
||||
// allow implicit const->non-const
|
||||
iterator_impl(const iterator_impl<false>& other)
|
||||
: mlq(other.mlq), it(other.it), current_priority(other.current_priority) {}
|
||||
|
||||
iterator_impl(const iterator_impl<true>& other)
|
||||
: mlq(other.mlq), it(other.it), current_priority(other.current_priority) {}
|
||||
|
||||
iterator_impl& operator=(const iterator_impl<false>& other) {
|
||||
mlq = other.mlq;
|
||||
it = other.it;
|
||||
current_priority = other.current_priority;
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend class iterator_impl<true>;
|
||||
iterator_impl() = default;
|
||||
|
||||
private:
|
||||
friend class MultiLevelQueue;
|
||||
using container_ref =
|
||||
std::conditional_t<is_constant, const MultiLevelQueue&, MultiLevelQueue&>;
|
||||
using list_iterator = std::conditional_t<is_constant, typename std::list<T>::const_iterator,
|
||||
typename std::list<T>::iterator>;
|
||||
|
||||
explicit iterator_impl(container_ref mlq, list_iterator it, u32 current_priority)
|
||||
: mlq(mlq), it(it), current_priority(current_priority) {}
|
||||
explicit iterator_impl(container_ref mlq, u32 current_priority)
|
||||
: mlq(mlq), it(), current_priority(current_priority) {}
|
||||
|
||||
bool IsEnd() const {
|
||||
return current_priority == mlq.depth();
|
||||
}
|
||||
|
||||
list_iterator GetBeginItForPrio() const {
|
||||
return mlq.levels[current_priority].begin();
|
||||
}
|
||||
|
||||
list_iterator GetEndItForPrio() const {
|
||||
return mlq.levels[current_priority].end();
|
||||
}
|
||||
|
||||
container_ref mlq;
|
||||
list_iterator it;
|
||||
u32 current_priority;
|
||||
};
|
||||
|
||||
using iterator = iterator_impl<false>;
|
||||
using const_iterator = iterator_impl<true>;
|
||||
|
||||
void add(const T& element, u32 priority, bool send_back = true) {
|
||||
if (send_back)
|
||||
levels[priority].push_back(element);
|
||||
else
|
||||
levels[priority].push_front(element);
|
||||
used_priorities |= 1ULL << priority;
|
||||
}
|
||||
|
||||
void remove(const T& element, u32 priority) {
|
||||
auto it = ListIterateTo(levels[priority], element);
|
||||
if (it == levels[priority].end())
|
||||
return;
|
||||
levels[priority].erase(it);
|
||||
if (levels[priority].empty()) {
|
||||
used_priorities &= ~(1ULL << priority);
|
||||
}
|
||||
}
|
||||
|
||||
void adjust(const T& element, u32 old_priority, u32 new_priority, bool adjust_front = false) {
|
||||
remove(element, old_priority);
|
||||
add(element, new_priority, !adjust_front);
|
||||
}
|
||||
void adjust(const_iterator it, u32 old_priority, u32 new_priority, bool adjust_front = false) {
|
||||
adjust(*it, old_priority, new_priority, adjust_front);
|
||||
}
|
||||
|
||||
void transfer_to_front(const T& element, u32 priority, MultiLevelQueue& other) {
|
||||
ListSplice(other.levels[priority], other.levels[priority].begin(), levels[priority],
|
||||
ListIterateTo(levels[priority], element));
|
||||
|
||||
other.used_priorities |= 1ULL << priority;
|
||||
|
||||
if (levels[priority].empty()) {
|
||||
used_priorities &= ~(1ULL << priority);
|
||||
}
|
||||
}
|
||||
|
||||
void transfer_to_front(const_iterator it, u32 priority, MultiLevelQueue& other) {
|
||||
transfer_to_front(*it, priority, other);
|
||||
}
|
||||
|
||||
void transfer_to_back(const T& element, u32 priority, MultiLevelQueue& other) {
|
||||
ListSplice(other.levels[priority], other.levels[priority].end(), levels[priority],
|
||||
ListIterateTo(levels[priority], element));
|
||||
|
||||
other.used_priorities |= 1ULL << priority;
|
||||
|
||||
if (levels[priority].empty()) {
|
||||
used_priorities &= ~(1ULL << priority);
|
||||
}
|
||||
}
|
||||
|
||||
void transfer_to_back(const_iterator it, u32 priority, MultiLevelQueue& other) {
|
||||
transfer_to_back(*it, priority, other);
|
||||
}
|
||||
|
||||
void yield(u32 priority, std::size_t n = 1) {
|
||||
ListShiftForward(levels[priority], n);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::size_t depth() const {
|
||||
return Depth;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::size_t size(u32 priority) const {
|
||||
return levels[priority].size();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::size_t size() const {
|
||||
u64 priorities = used_priorities;
|
||||
std::size_t size = 0;
|
||||
while (priorities != 0) {
|
||||
const u64 current_priority = CountTrailingZeroes64(priorities);
|
||||
size += levels[current_priority].size();
|
||||
priorities &= ~(1ULL << current_priority);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool empty() const {
|
||||
return used_priorities == 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool empty(u32 priority) const {
|
||||
return (used_priorities & (1ULL << priority)) == 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 highest_priority_set(u32 max_priority = 0) const {
|
||||
const u64 priorities =
|
||||
max_priority == 0 ? used_priorities : (used_priorities & ~((1ULL << max_priority) - 1));
|
||||
return priorities == 0 ? Depth : static_cast<u32>(CountTrailingZeroes64(priorities));
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 lowest_priority_set(u32 min_priority = Depth - 1) const {
|
||||
const u64 priorities = min_priority >= Depth - 1
|
||||
? used_priorities
|
||||
: (used_priorities & ((1ULL << (min_priority + 1)) - 1));
|
||||
return priorities == 0 ? Depth : 63 - CountLeadingZeroes64(priorities);
|
||||
}
|
||||
|
||||
[[nodiscard]] const_iterator cbegin(u32 max_prio = 0) const {
|
||||
const u32 priority = highest_priority_set(max_prio);
|
||||
return priority == Depth ? cend()
|
||||
: const_iterator{*this, levels[priority].cbegin(), priority};
|
||||
}
|
||||
[[nodiscard]] const_iterator begin(u32 max_prio = 0) const {
|
||||
return cbegin(max_prio);
|
||||
}
|
||||
[[nodiscard]] iterator begin(u32 max_prio = 0) {
|
||||
const u32 priority = highest_priority_set(max_prio);
|
||||
return priority == Depth ? end() : iterator{*this, levels[priority].begin(), priority};
|
||||
}
|
||||
|
||||
[[nodiscard]] const_iterator cend(u32 min_prio = Depth - 1) const {
|
||||
return min_prio == Depth - 1 ? const_iterator{*this, Depth} : cbegin(min_prio + 1);
|
||||
}
|
||||
[[nodiscard]] const_iterator end(u32 min_prio = Depth - 1) const {
|
||||
return cend(min_prio);
|
||||
}
|
||||
[[nodiscard]] iterator end(u32 min_prio = Depth - 1) {
|
||||
return min_prio == Depth - 1 ? iterator{*this, Depth} : begin(min_prio + 1);
|
||||
}
|
||||
|
||||
[[nodiscard]] T& front(u32 max_priority = 0) {
|
||||
const u32 priority = highest_priority_set(max_priority);
|
||||
return levels[priority == Depth ? 0 : priority].front();
|
||||
}
|
||||
[[nodiscard]] const T& front(u32 max_priority = 0) const {
|
||||
const u32 priority = highest_priority_set(max_priority);
|
||||
return levels[priority == Depth ? 0 : priority].front();
|
||||
}
|
||||
|
||||
[[nodiscard]] T& back(u32 min_priority = Depth - 1) {
|
||||
const u32 priority = lowest_priority_set(min_priority); // intended
|
||||
return levels[priority == Depth ? 63 : priority].back();
|
||||
}
|
||||
[[nodiscard]] const T& back(u32 min_priority = Depth - 1) const {
|
||||
const u32 priority = lowest_priority_set(min_priority); // intended
|
||||
return levels[priority == Depth ? 63 : priority].back();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
used_priorities = 0;
|
||||
for (std::size_t i = 0; i < Depth; i++) {
|
||||
levels[i].clear();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
using const_list_iterator = typename std::list<T>::const_iterator;
|
||||
|
||||
static void ListShiftForward(std::list<T>& list, const std::size_t shift = 1) {
|
||||
if (shift >= list.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto begin_range = list.begin();
|
||||
const auto end_range = std::next(begin_range, shift);
|
||||
list.splice(list.end(), list, begin_range, end_range);
|
||||
}
|
||||
|
||||
static void ListSplice(std::list<T>& in_list, const_list_iterator position,
|
||||
std::list<T>& out_list, const_list_iterator element) {
|
||||
in_list.splice(position, out_list, element);
|
||||
}
|
||||
|
||||
[[nodiscard]] static const_list_iterator ListIterateTo(const std::list<T>& list,
|
||||
const T& element) {
|
||||
auto it = list.cbegin();
|
||||
while (it != list.cend() && *it != element) {
|
||||
++it;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
std::array<std::list<T>, Depth> levels;
|
||||
u64 used_priorities = 0;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
27
src/common/nvidia_flags.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <filesystem>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/file_util.h"
|
||||
#include "common/nvidia_flags.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
void ConfigureNvidiaEnvironmentFlags() {
|
||||
#ifdef _WIN32
|
||||
const std::string shader_path = Common::FS::SanitizePath(
|
||||
fmt::format("{}/nvidia/", Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir)));
|
||||
const std::string windows_path =
|
||||
Common::FS::SanitizePath(shader_path, Common::FS::DirectorySeparator::BackwardSlash);
|
||||
void(Common::FS::CreateFullPath(shader_path + '/'));
|
||||
void(_putenv(fmt::format("__GL_SHADER_DISK_CACHE_PATH={}", windows_path).c_str()));
|
||||
void(_putenv("__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1"));
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
10
src/common/nvidia_flags.h
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
namespace Common {
|
||||
|
||||
/// Configure platform specific flags for Nvidia's driver
|
||||
void ConfigureNvidiaEnvironmentFlags();
|
||||
|
||||
} // namespace Common
|
||||
@@ -10,16 +10,10 @@ PageTable::PageTable() = default;
|
||||
|
||||
PageTable::~PageTable() noexcept = default;
|
||||
|
||||
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits,
|
||||
bool has_attribute) {
|
||||
const std::size_t num_page_table_entries{1ULL
|
||||
<< (address_space_width_in_bits - page_size_in_bits)};
|
||||
void PageTable::Resize(size_t address_space_width_in_bits, size_t page_size_in_bits) {
|
||||
const size_t num_page_table_entries{1ULL << (address_space_width_in_bits - page_size_in_bits)};
|
||||
pointers.resize(num_page_table_entries);
|
||||
backing_addr.resize(num_page_table_entries);
|
||||
|
||||
if (has_attribute) {
|
||||
attributes.resize(num_page_table_entries);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <tuple>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/memory_hook.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
namespace Common {
|
||||
@@ -20,27 +20,6 @@ enum class PageType : u8 {
|
||||
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
||||
/// invalidation
|
||||
RasterizerCachedMemory,
|
||||
/// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
|
||||
Special,
|
||||
/// Page is allocated for use.
|
||||
Allocated,
|
||||
};
|
||||
|
||||
struct SpecialRegion {
|
||||
enum class Type {
|
||||
DebugHook,
|
||||
IODevice,
|
||||
} type;
|
||||
|
||||
MemoryHookPointer handler;
|
||||
|
||||
[[nodiscard]] bool operator<(const SpecialRegion& other) const {
|
||||
return std::tie(type, handler) < std::tie(other.type, other.handler);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const SpecialRegion& other) const {
|
||||
return std::tie(type, handler) == std::tie(other.type, other.handler);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -48,6 +27,59 @@ struct SpecialRegion {
|
||||
* mimics the way a real CPU page table works.
|
||||
*/
|
||||
struct PageTable {
|
||||
/// Number of bits reserved for attribute tagging.
|
||||
/// This can be at most the guaranteed alignment of the pointers in the page table.
|
||||
static constexpr int ATTRIBUTE_BITS = 2;
|
||||
|
||||
/**
|
||||
* Pair of host pointer and page type attribute.
|
||||
* This uses the lower bits of a given pointer to store the attribute tag.
|
||||
* Writing and reading the pointer attribute pair is guaranteed to be atomic for the same method
|
||||
* call. In other words, they are guaranteed to be synchronized at all times.
|
||||
*/
|
||||
class PageInfo {
|
||||
public:
|
||||
/// Returns the page pointer
|
||||
[[nodiscard]] u8* Pointer() const noexcept {
|
||||
return ExtractPointer(raw.load(std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
/// Returns the page type attribute
|
||||
[[nodiscard]] PageType Type() const noexcept {
|
||||
return ExtractType(raw.load(std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
/// Returns the page pointer and attribute pair, extracted from the same atomic read
|
||||
[[nodiscard]] std::pair<u8*, PageType> PointerType() const noexcept {
|
||||
const uintptr_t non_atomic_raw = raw.load(std::memory_order_relaxed);
|
||||
return {ExtractPointer(non_atomic_raw), ExtractType(non_atomic_raw)};
|
||||
}
|
||||
|
||||
/// Returns the raw representation of the page information.
|
||||
/// Use ExtractPointer and ExtractType to unpack the value.
|
||||
[[nodiscard]] uintptr_t Raw() const noexcept {
|
||||
return raw.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
/// Write a page pointer and type pair atomically
|
||||
void Store(u8* pointer, PageType type) noexcept {
|
||||
raw.store(reinterpret_cast<uintptr_t>(pointer) | static_cast<uintptr_t>(type));
|
||||
}
|
||||
|
||||
/// Unpack a pointer from a page info raw representation
|
||||
[[nodiscard]] static u8* ExtractPointer(uintptr_t raw) noexcept {
|
||||
return reinterpret_cast<u8*>(raw & (~uintptr_t{0} << ATTRIBUTE_BITS));
|
||||
}
|
||||
|
||||
/// Unpack a page type from a page info raw representation
|
||||
[[nodiscard]] static PageType ExtractType(uintptr_t raw) noexcept {
|
||||
return static_cast<PageType>(raw & ((uintptr_t{1} << ATTRIBUTE_BITS) - 1));
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<uintptr_t> raw;
|
||||
};
|
||||
|
||||
PageTable();
|
||||
~PageTable() noexcept;
|
||||
|
||||
@@ -58,25 +90,21 @@ struct PageTable {
|
||||
PageTable& operator=(PageTable&&) noexcept = default;
|
||||
|
||||
/**
|
||||
* Resizes the page table to be able to accomodate enough pages within
|
||||
* Resizes the page table to be able to accommodate enough pages within
|
||||
* a given address space.
|
||||
*
|
||||
* @param address_space_width_in_bits The address size width in bits.
|
||||
* @param page_size_in_bits The page size in bits.
|
||||
* @param has_attribute Whether or not this page has any backing attributes.
|
||||
*/
|
||||
void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits,
|
||||
bool has_attribute);
|
||||
void Resize(size_t address_space_width_in_bits, size_t page_size_in_bits);
|
||||
|
||||
/**
|
||||
* Vector of memory pointers backing each page. An entry can only be non-null if the
|
||||
* corresponding entry in the `attributes` vector is of type `Memory`.
|
||||
* corresponding attribute element is of type `Memory`.
|
||||
*/
|
||||
VirtualBuffer<u8*> pointers;
|
||||
VirtualBuffer<PageInfo> pointers;
|
||||
|
||||
VirtualBuffer<u64> backing_addr;
|
||||
|
||||
VirtualBuffer<PageType> attributes;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
191
src/common/parent_of_member.h
Normal file
@@ -0,0 +1,191 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
namespace detail {
|
||||
template <typename T, size_t Size, size_t Align>
|
||||
struct TypedStorageImpl {
|
||||
std::aligned_storage_t<Size, Align> storage_;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <typename T>
|
||||
using TypedStorage = detail::TypedStorageImpl<T, sizeof(T), alignof(T)>;
|
||||
|
||||
template <typename T>
|
||||
static constexpr T* GetPointer(TypedStorage<T>& ts) {
|
||||
return static_cast<T*>(static_cast<void*>(std::addressof(ts.storage_)));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static constexpr const T* GetPointer(const TypedStorage<T>& ts) {
|
||||
return static_cast<const T*>(static_cast<const void*>(std::addressof(ts.storage_)));
|
||||
}
|
||||
|
||||
namespace impl {
|
||||
|
||||
template <size_t MaxDepth>
|
||||
struct OffsetOfUnionHolder {
|
||||
template <typename ParentType, typename MemberType, size_t Offset>
|
||||
union UnionImpl {
|
||||
using PaddingMember = char;
|
||||
static constexpr size_t GetOffset() {
|
||||
return Offset;
|
||||
}
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct {
|
||||
PaddingMember padding[Offset];
|
||||
MemberType members[(sizeof(ParentType) / sizeof(MemberType)) + 1];
|
||||
} data;
|
||||
#pragma pack(pop)
|
||||
UnionImpl<ParentType, MemberType, Offset + 1> next_union;
|
||||
};
|
||||
|
||||
template <typename ParentType, typename MemberType>
|
||||
union UnionImpl<ParentType, MemberType, 0> {
|
||||
static constexpr size_t GetOffset() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct {
|
||||
MemberType members[(sizeof(ParentType) / sizeof(MemberType)) + 1];
|
||||
} data;
|
||||
UnionImpl<ParentType, MemberType, 1> next_union;
|
||||
};
|
||||
|
||||
template <typename ParentType, typename MemberType>
|
||||
union UnionImpl<ParentType, MemberType, MaxDepth> {};
|
||||
};
|
||||
|
||||
template <typename ParentType, typename MemberType>
|
||||
struct OffsetOfCalculator {
|
||||
using UnionHolder =
|
||||
typename OffsetOfUnionHolder<sizeof(MemberType)>::template UnionImpl<ParentType, MemberType,
|
||||
0>;
|
||||
union Union {
|
||||
char c{};
|
||||
UnionHolder first_union;
|
||||
TypedStorage<ParentType> parent;
|
||||
|
||||
constexpr Union() : c() {}
|
||||
};
|
||||
static constexpr Union U = {};
|
||||
|
||||
static constexpr const MemberType* GetNextAddress(const MemberType* start,
|
||||
const MemberType* target) {
|
||||
while (start < target) {
|
||||
start++;
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
static constexpr std::ptrdiff_t GetDifference(const MemberType* start,
|
||||
const MemberType* target) {
|
||||
return (target - start) * sizeof(MemberType);
|
||||
}
|
||||
|
||||
template <typename CurUnion>
|
||||
static constexpr std::ptrdiff_t OffsetOfImpl(MemberType ParentType::*member,
|
||||
CurUnion& cur_union) {
|
||||
constexpr size_t Offset = CurUnion::GetOffset();
|
||||
const auto target = std::addressof(GetPointer(U.parent)->*member);
|
||||
const auto start = std::addressof(cur_union.data.members[0]);
|
||||
const auto next = GetNextAddress(start, target);
|
||||
|
||||
if (next != target) {
|
||||
if constexpr (Offset < sizeof(MemberType) - 1) {
|
||||
return OffsetOfImpl(member, cur_union.next_union);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
return (next - start) * sizeof(MemberType) + Offset;
|
||||
}
|
||||
|
||||
static constexpr std::ptrdiff_t OffsetOf(MemberType ParentType::*member) {
|
||||
return OffsetOfImpl(member, U.first_union);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct GetMemberPointerTraits;
|
||||
|
||||
template <typename P, typename M>
|
||||
struct GetMemberPointerTraits<M P::*> {
|
||||
using Parent = P;
|
||||
using Member = M;
|
||||
};
|
||||
|
||||
template <auto MemberPtr>
|
||||
using GetParentType = typename GetMemberPointerTraits<decltype(MemberPtr)>::Parent;
|
||||
|
||||
template <auto MemberPtr>
|
||||
using GetMemberType = typename GetMemberPointerTraits<decltype(MemberPtr)>::Member;
|
||||
|
||||
template <auto MemberPtr, typename RealParentType = GetParentType<MemberPtr>>
|
||||
static inline std::ptrdiff_t OffsetOf = [] {
|
||||
using DeducedParentType = GetParentType<MemberPtr>;
|
||||
using MemberType = GetMemberType<MemberPtr>;
|
||||
static_assert(std::is_base_of<DeducedParentType, RealParentType>::value ||
|
||||
std::is_same<RealParentType, DeducedParentType>::value);
|
||||
|
||||
return OffsetOfCalculator<RealParentType, MemberType>::OffsetOf(MemberPtr);
|
||||
}();
|
||||
|
||||
} // namespace impl
|
||||
|
||||
template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
||||
constexpr RealParentType& GetParentReference(impl::GetMemberType<MemberPtr>* member) {
|
||||
std::ptrdiff_t Offset = impl::OffsetOf<MemberPtr, RealParentType>;
|
||||
return *static_cast<RealParentType*>(
|
||||
static_cast<void*>(static_cast<uint8_t*>(static_cast<void*>(member)) - Offset));
|
||||
}
|
||||
|
||||
template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
||||
constexpr RealParentType const& GetParentReference(impl::GetMemberType<MemberPtr> const* member) {
|
||||
std::ptrdiff_t Offset = impl::OffsetOf<MemberPtr, RealParentType>;
|
||||
return *static_cast<const RealParentType*>(static_cast<const void*>(
|
||||
static_cast<const uint8_t*>(static_cast<const void*>(member)) - Offset));
|
||||
}
|
||||
|
||||
template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
||||
constexpr RealParentType* GetParentPointer(impl::GetMemberType<MemberPtr>* member) {
|
||||
return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
|
||||
}
|
||||
|
||||
template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
||||
constexpr RealParentType const* GetParentPointer(impl::GetMemberType<MemberPtr> const* member) {
|
||||
return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
|
||||
}
|
||||
|
||||
template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
||||
constexpr RealParentType& GetParentReference(impl::GetMemberType<MemberPtr>& member) {
|
||||
return GetParentReference<MemberPtr, RealParentType>(std::addressof(member));
|
||||
}
|
||||
|
||||
template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
||||
constexpr RealParentType const& GetParentReference(impl::GetMemberType<MemberPtr> const& member) {
|
||||
return GetParentReference<MemberPtr, RealParentType>(std::addressof(member));
|
||||
}
|
||||
|
||||
template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
||||
constexpr RealParentType* GetParentPointer(impl::GetMemberType<MemberPtr>& member) {
|
||||
return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
|
||||
}
|
||||
|
||||
template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
||||
constexpr RealParentType const* GetParentPointer(impl::GetMemberType<MemberPtr> const& member) {
|
||||
return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -19,15 +19,14 @@ namespace Common {
|
||||
/// SPSC ring buffer
|
||||
/// @tparam T Element type
|
||||
/// @tparam capacity Number of slots in ring buffer
|
||||
/// @tparam granularity Slot size in terms of number of elements
|
||||
template <typename T, std::size_t capacity, std::size_t granularity = 1>
|
||||
template <typename T, std::size_t capacity>
|
||||
class RingBuffer {
|
||||
/// A "slot" is made of `granularity` elements of `T`.
|
||||
static constexpr std::size_t slot_size = granularity * sizeof(T);
|
||||
/// A "slot" is made of a single `T`.
|
||||
static constexpr std::size_t slot_size = sizeof(T);
|
||||
// T must be safely memcpy-able and have a trivial default constructor.
|
||||
static_assert(std::is_trivial_v<T>);
|
||||
// Ensure capacity is sensible.
|
||||
static_assert(capacity < std::numeric_limits<std::size_t>::max() / 2 / granularity);
|
||||
static_assert(capacity < std::numeric_limits<std::size_t>::max() / 2);
|
||||
static_assert((capacity & (capacity - 1)) == 0, "capacity must be a power of two");
|
||||
// Ensure lock-free.
|
||||
static_assert(std::atomic_size_t::is_always_lock_free);
|
||||
@@ -47,7 +46,7 @@ public:
|
||||
const std::size_t second_copy = push_count - first_copy;
|
||||
|
||||
const char* in = static_cast<const char*>(new_slots);
|
||||
std::memcpy(m_data.data() + pos * granularity, in, first_copy * slot_size);
|
||||
std::memcpy(m_data.data() + pos, in, first_copy * slot_size);
|
||||
in += first_copy * slot_size;
|
||||
std::memcpy(m_data.data(), in, second_copy * slot_size);
|
||||
|
||||
@@ -74,7 +73,7 @@ public:
|
||||
const std::size_t second_copy = pop_count - first_copy;
|
||||
|
||||
char* out = static_cast<char*>(output);
|
||||
std::memcpy(out, m_data.data() + pos * granularity, first_copy * slot_size);
|
||||
std::memcpy(out, m_data.data() + pos, first_copy * slot_size);
|
||||
out += first_copy * slot_size;
|
||||
std::memcpy(out, m_data.data(), second_copy * slot_size);
|
||||
|
||||
@@ -84,9 +83,9 @@ public:
|
||||
}
|
||||
|
||||
std::vector<T> Pop(std::size_t max_slots = ~std::size_t(0)) {
|
||||
std::vector<T> out(std::min(max_slots, capacity) * granularity);
|
||||
const std::size_t count = Pop(out.data(), out.size() / granularity);
|
||||
out.resize(count * granularity);
|
||||
std::vector<T> out(std::min(max_slots, capacity));
|
||||
const std::size_t count = Pop(out.data(), out.size());
|
||||
out.resize(count);
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -113,7 +112,7 @@ private:
|
||||
alignas(128) std::atomic_size_t m_write_index{0};
|
||||
#endif
|
||||
|
||||
std::array<T, granularity * capacity> m_data;
|
||||
std::array<T, capacity> m_data;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
namespace detail {
|
||||
template <typename Func>
|
||||
struct ScopeExitHelper {
|
||||
explicit ScopeExitHelper(Func&& func) : func(std::move(func)) {}
|
||||
explicit ScopeExitHelper(Func&& func_) : func(std::move(func_)) {}
|
||||
~ScopeExitHelper() {
|
||||
if (active) {
|
||||
func();
|
||||
@@ -49,3 +49,9 @@ ScopeExitHelper<Func> ScopeExit(Func&& func) {
|
||||
* \endcode
|
||||
*/
|
||||
#define SCOPE_EXIT(body) auto CONCAT2(scope_exit_helper_, __LINE__) = detail::ScopeExit([&]() body)
|
||||
|
||||
/**
|
||||
* This macro is similar to SCOPE_EXIT, except the object is caller managed. This is intended to be
|
||||
* used when the caller might want to cancel the ScopeExit.
|
||||
*/
|
||||
#define SCOPE_GUARD(body) detail::ScopeExit([&]() body)
|
||||
|
||||
@@ -141,27 +141,13 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st
|
||||
}
|
||||
|
||||
std::string UTF16ToUTF8(const std::u16string& input) {
|
||||
#ifdef _MSC_VER
|
||||
// Workaround for missing char16_t/char32_t instantiations in MSVC2017
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<__int16>, __int16> convert;
|
||||
std::basic_string<__int16> tmp_buffer(input.cbegin(), input.cend());
|
||||
return convert.to_bytes(tmp_buffer);
|
||||
#else
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
|
||||
return convert.to_bytes(input);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::u16string UTF8ToUTF16(const std::string& input) {
|
||||
#ifdef _MSC_VER
|
||||
// Workaround for missing char16_t/char32_t instantiations in MSVC2017
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<__int16>, __int16> convert;
|
||||
auto tmp_buffer = convert.from_bytes(input);
|
||||
return std::u16string(tmp_buffer.cbegin(), tmp_buffer.cend());
|
||||
#else
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
|
||||
return convert.from_bytes(input);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
@@ -394,7 +394,7 @@ public:
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend S operator%(const S& p, const swapped_t v);
|
||||
|
||||
// Arithmetics + assignements
|
||||
// Arithmetics + assignments
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend S operator+=(const S& p, const swapped_t v);
|
||||
|
||||
@@ -451,7 +451,7 @@ S operator%(const S& i, const swap_struct_t<T, F> v) {
|
||||
return i % v.swap();
|
||||
}
|
||||
|
||||
// Arithmetics + assignements
|
||||
// Arithmetics + assignments
|
||||
template <typename S, typename T, typename F>
|
||||
S& operator+=(S& i, const swap_struct_t<T, F> v) {
|
||||
i += v.swap();
|
||||
|
||||
@@ -52,8 +52,8 @@ public:
|
||||
template <typename T>
|
||||
class Field : public FieldInterface {
|
||||
public:
|
||||
Field(FieldType type, std::string name, T value)
|
||||
: name(std::move(name)), type(type), value(std::move(value)) {}
|
||||
Field(FieldType type_, std::string name_, T value_)
|
||||
: name(std::move(name_)), type(type_), value(std::move(value_)) {}
|
||||
|
||||
Field(const Field&) = default;
|
||||
Field& operator=(const Field&) = default;
|
||||
|
||||
58
src/common/thread_worker.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright 2020 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/thread.h"
|
||||
#include "common/thread_worker.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
ThreadWorker::ThreadWorker(std::size_t num_workers, const std::string& name) {
|
||||
for (std::size_t i = 0; i < num_workers; ++i)
|
||||
threads.emplace_back([this, thread_name{std::string{name}}] {
|
||||
Common::SetCurrentThreadName(thread_name.c_str());
|
||||
|
||||
// Wait for first request
|
||||
{
|
||||
std::unique_lock lock{queue_mutex};
|
||||
condition.wait(lock, [this] { return stop || !requests.empty(); });
|
||||
}
|
||||
|
||||
while (true) {
|
||||
std::function<void()> task;
|
||||
|
||||
{
|
||||
std::unique_lock lock{queue_mutex};
|
||||
condition.wait(lock, [this] { return stop || !requests.empty(); });
|
||||
if (stop || requests.empty()) {
|
||||
return;
|
||||
}
|
||||
task = std::move(requests.front());
|
||||
requests.pop();
|
||||
}
|
||||
|
||||
task();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ThreadWorker::~ThreadWorker() {
|
||||
{
|
||||
std::unique_lock lock{queue_mutex};
|
||||
stop = true;
|
||||
}
|
||||
condition.notify_all();
|
||||
for (std::thread& thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadWorker::QueueWork(std::function<void()>&& work) {
|
||||
{
|
||||
std::unique_lock lock{queue_mutex};
|
||||
requests.emplace(work);
|
||||
}
|
||||
condition.notify_one();
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
30
src/common/thread_worker.h
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2020 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
namespace Common {
|
||||
|
||||
class ThreadWorker final {
|
||||
public:
|
||||
explicit ThreadWorker(std::size_t num_workers, const std::string& name);
|
||||
~ThreadWorker();
|
||||
void QueueWork(std::function<void()>&& work);
|
||||
|
||||
private:
|
||||
std::vector<std::thread> threads;
|
||||
std::queue<std::function<void()>> requests;
|
||||
std::mutex queue_mutex;
|
||||
std::condition_variable condition;
|
||||
std::atomic_bool stop{};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,159 +0,0 @@
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <ctime>
|
||||
#include <fmt/format.h>
|
||||
#include "common/common_types.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/timer.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
std::chrono::milliseconds Timer::GetTimeMs() {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch());
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
// Initiate, Start, Stop, and Update the time
|
||||
// --------------------------------------------
|
||||
|
||||
// Set initial values for the class
|
||||
Timer::Timer() : m_LastTime(0), m_StartTime(0), m_Running(false) {
|
||||
Update();
|
||||
}
|
||||
|
||||
// Write the starting time
|
||||
void Timer::Start() {
|
||||
m_StartTime = GetTimeMs();
|
||||
m_Running = true;
|
||||
}
|
||||
|
||||
// Stop the timer
|
||||
void Timer::Stop() {
|
||||
// Write the final time
|
||||
m_LastTime = GetTimeMs();
|
||||
m_Running = false;
|
||||
}
|
||||
|
||||
// Update the last time variable
|
||||
void Timer::Update() {
|
||||
m_LastTime = GetTimeMs();
|
||||
// TODO(ector) - QPF
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
// Get time difference and elapsed time
|
||||
// -------------------------------------
|
||||
|
||||
// Get the number of milliseconds since the last Update()
|
||||
std::chrono::milliseconds Timer::GetTimeDifference() {
|
||||
return GetTimeMs() - m_LastTime;
|
||||
}
|
||||
|
||||
// Add the time difference since the last Update() to the starting time.
|
||||
// This is used to compensate for a paused game.
|
||||
void Timer::AddTimeDifference() {
|
||||
m_StartTime += GetTimeDifference();
|
||||
}
|
||||
|
||||
// Get the time elapsed since the Start()
|
||||
std::chrono::milliseconds Timer::GetTimeElapsed() {
|
||||
// If we have not started yet, return 1 (because then I don't
|
||||
// have to change the FPS calculation in CoreRerecording.cpp .
|
||||
if (m_StartTime.count() == 0)
|
||||
return std::chrono::milliseconds(1);
|
||||
|
||||
// Return the final timer time if the timer is stopped
|
||||
if (!m_Running)
|
||||
return (m_LastTime - m_StartTime);
|
||||
|
||||
return (GetTimeMs() - m_StartTime);
|
||||
}
|
||||
|
||||
// Get the formatted time elapsed since the Start()
|
||||
std::string Timer::GetTimeElapsedFormatted() const {
|
||||
// If we have not started yet, return zero
|
||||
if (m_StartTime.count() == 0)
|
||||
return "00:00:00:000";
|
||||
|
||||
// The number of milliseconds since the start.
|
||||
// Use a different value if the timer is stopped.
|
||||
std::chrono::milliseconds Milliseconds;
|
||||
if (m_Running)
|
||||
Milliseconds = GetTimeMs() - m_StartTime;
|
||||
else
|
||||
Milliseconds = m_LastTime - m_StartTime;
|
||||
// Seconds
|
||||
std::chrono::seconds Seconds = std::chrono::duration_cast<std::chrono::seconds>(Milliseconds);
|
||||
// Minutes
|
||||
std::chrono::minutes Minutes = std::chrono::duration_cast<std::chrono::minutes>(Milliseconds);
|
||||
// Hours
|
||||
std::chrono::hours Hours = std::chrono::duration_cast<std::chrono::hours>(Milliseconds);
|
||||
|
||||
std::string TmpStr = fmt::format("{:02}:{:02}:{:02}:{:03}", Hours.count(), Minutes.count() % 60,
|
||||
Seconds.count() % 60, Milliseconds.count() % 1000);
|
||||
return TmpStr;
|
||||
}
|
||||
|
||||
// Get the number of seconds since January 1 1970
|
||||
std::chrono::seconds Timer::GetTimeSinceJan1970() {
|
||||
return std::chrono::duration_cast<std::chrono::seconds>(GetTimeMs());
|
||||
}
|
||||
|
||||
std::chrono::seconds Timer::GetLocalTimeSinceJan1970() {
|
||||
time_t sysTime, tzDiff, tzDST;
|
||||
struct tm* gmTime;
|
||||
|
||||
time(&sysTime);
|
||||
|
||||
// Account for DST where needed
|
||||
gmTime = localtime(&sysTime);
|
||||
if (gmTime->tm_isdst == 1)
|
||||
tzDST = 3600;
|
||||
else
|
||||
tzDST = 0;
|
||||
|
||||
// Lazy way to get local time in sec
|
||||
gmTime = gmtime(&sysTime);
|
||||
tzDiff = sysTime - mktime(gmTime);
|
||||
|
||||
return std::chrono::seconds(sysTime + tzDiff + tzDST);
|
||||
}
|
||||
|
||||
// Return the current time formatted as Minutes:Seconds:Milliseconds
|
||||
// in the form 00:00:000.
|
||||
std::string Timer::GetTimeFormatted() {
|
||||
time_t sysTime;
|
||||
struct tm* gmTime;
|
||||
char tmp[13];
|
||||
|
||||
time(&sysTime);
|
||||
gmTime = localtime(&sysTime);
|
||||
|
||||
strftime(tmp, 6, "%M:%S", gmTime);
|
||||
|
||||
u64 milliseconds = static_cast<u64>(GetTimeMs().count()) % 1000;
|
||||
return fmt::format("{}:{:03}", tmp, milliseconds);
|
||||
}
|
||||
|
||||
// Returns a timestamp with decimals for precise time comparisons
|
||||
// ----------------
|
||||
double Timer::GetDoubleTime() {
|
||||
// Get continuous timestamp
|
||||
auto tmp_seconds = static_cast<u64>(GetTimeSinceJan1970().count());
|
||||
const auto ms = static_cast<double>(static_cast<u64>(GetTimeMs().count()) % 1000);
|
||||
|
||||
// Remove a few years. We only really want enough seconds to make
|
||||
// sure that we are detecting actual actions, perhaps 60 seconds is
|
||||
// enough really, but I leave a year of seconds anyway, in case the
|
||||
// user's clock is incorrect or something like that.
|
||||
tmp_seconds = tmp_seconds - (38 * 365 * 24 * 60 * 60);
|
||||
|
||||
// Make a smaller integer that fits in the double
|
||||
const auto seconds = static_cast<u32>(tmp_seconds);
|
||||
return seconds + ms;
|
||||
}
|
||||
|
||||
} // Namespace Common
|
||||
@@ -1,41 +0,0 @@
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
class Timer {
|
||||
public:
|
||||
Timer();
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
void Update();
|
||||
|
||||
// The time difference is always returned in milliseconds, regardless of alternative internal
|
||||
// representation
|
||||
[[nodiscard]] std::chrono::milliseconds GetTimeDifference();
|
||||
void AddTimeDifference();
|
||||
|
||||
[[nodiscard]] static std::chrono::seconds GetTimeSinceJan1970();
|
||||
[[nodiscard]] static std::chrono::seconds GetLocalTimeSinceJan1970();
|
||||
[[nodiscard]] static double GetDoubleTime();
|
||||
|
||||
[[nodiscard]] static std::string GetTimeFormatted();
|
||||
[[nodiscard]] std::string GetTimeElapsedFormatted() const;
|
||||
[[nodiscard]] std::chrono::milliseconds GetTimeElapsed();
|
||||
|
||||
[[nodiscard]] static std::chrono::milliseconds GetTimeMs();
|
||||
|
||||
private:
|
||||
std::chrono::milliseconds m_LastTime;
|
||||
std::chrono::milliseconds m_StartTime;
|
||||
bool m_Running;
|
||||
};
|
||||
|
||||
} // Namespace Common
|
||||
674
src/common/tree.h
Normal file
@@ -0,0 +1,674 @@
|
||||
/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */
|
||||
/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
|
||||
/*-
|
||||
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* This file defines data structures for red-black trees.
|
||||
*
|
||||
* A red-black tree is a binary search tree with the node color as an
|
||||
* extra attribute. It fulfills a set of conditions:
|
||||
* - every search path from the root to a leaf consists of the
|
||||
* same number of black nodes,
|
||||
* - each red node (except for the root) has a black parent,
|
||||
* - each leaf node is black.
|
||||
*
|
||||
* Every operation on a red-black tree is bounded as O(lg n).
|
||||
* The maximum height of a red-black tree is 2lg (n+1).
|
||||
*/
|
||||
|
||||
namespace Common {
|
||||
template <typename T>
|
||||
class RBHead {
|
||||
public:
|
||||
[[nodiscard]] T* Root() {
|
||||
return rbh_root;
|
||||
}
|
||||
|
||||
[[nodiscard]] const T* Root() const {
|
||||
return rbh_root;
|
||||
}
|
||||
|
||||
void SetRoot(T* root) {
|
||||
rbh_root = root;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsEmpty() const {
|
||||
return Root() == nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
T* rbh_root = nullptr;
|
||||
};
|
||||
|
||||
enum class EntryColor {
|
||||
Black,
|
||||
Red,
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class RBEntry {
|
||||
public:
|
||||
[[nodiscard]] T* Left() {
|
||||
return rbe_left;
|
||||
}
|
||||
|
||||
[[nodiscard]] const T* Left() const {
|
||||
return rbe_left;
|
||||
}
|
||||
|
||||
void SetLeft(T* left) {
|
||||
rbe_left = left;
|
||||
}
|
||||
|
||||
[[nodiscard]] T* Right() {
|
||||
return rbe_right;
|
||||
}
|
||||
|
||||
[[nodiscard]] const T* Right() const {
|
||||
return rbe_right;
|
||||
}
|
||||
|
||||
void SetRight(T* right) {
|
||||
rbe_right = right;
|
||||
}
|
||||
|
||||
[[nodiscard]] T* Parent() {
|
||||
return rbe_parent;
|
||||
}
|
||||
|
||||
[[nodiscard]] const T* Parent() const {
|
||||
return rbe_parent;
|
||||
}
|
||||
|
||||
void SetParent(T* parent) {
|
||||
rbe_parent = parent;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsBlack() const {
|
||||
return rbe_color == EntryColor::Black;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsRed() const {
|
||||
return rbe_color == EntryColor::Red;
|
||||
}
|
||||
|
||||
[[nodiscard]] EntryColor Color() const {
|
||||
return rbe_color;
|
||||
}
|
||||
|
||||
void SetColor(EntryColor color) {
|
||||
rbe_color = color;
|
||||
}
|
||||
|
||||
private:
|
||||
T* rbe_left = nullptr;
|
||||
T* rbe_right = nullptr;
|
||||
T* rbe_parent = nullptr;
|
||||
EntryColor rbe_color{};
|
||||
};
|
||||
|
||||
template <typename Node>
|
||||
[[nodiscard]] RBEntry<Node>& RB_ENTRY(Node* node) {
|
||||
return node->GetEntry();
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
[[nodiscard]] const RBEntry<Node>& RB_ENTRY(const Node* node) {
|
||||
return node->GetEntry();
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
[[nodiscard]] Node* RB_PARENT(Node* node) {
|
||||
return RB_ENTRY(node).Parent();
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
[[nodiscard]] const Node* RB_PARENT(const Node* node) {
|
||||
return RB_ENTRY(node).Parent();
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
void RB_SET_PARENT(Node* node, Node* parent) {
|
||||
return RB_ENTRY(node).SetParent(parent);
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
[[nodiscard]] Node* RB_LEFT(Node* node) {
|
||||
return RB_ENTRY(node).Left();
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
[[nodiscard]] const Node* RB_LEFT(const Node* node) {
|
||||
return RB_ENTRY(node).Left();
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
void RB_SET_LEFT(Node* node, Node* left) {
|
||||
return RB_ENTRY(node).SetLeft(left);
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
[[nodiscard]] Node* RB_RIGHT(Node* node) {
|
||||
return RB_ENTRY(node).Right();
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
[[nodiscard]] const Node* RB_RIGHT(const Node* node) {
|
||||
return RB_ENTRY(node).Right();
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
void RB_SET_RIGHT(Node* node, Node* right) {
|
||||
return RB_ENTRY(node).SetRight(right);
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
[[nodiscard]] bool RB_IS_BLACK(const Node* node) {
|
||||
return RB_ENTRY(node).IsBlack();
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
[[nodiscard]] bool RB_IS_RED(const Node* node) {
|
||||
return RB_ENTRY(node).IsRed();
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
[[nodiscard]] EntryColor RB_COLOR(const Node* node) {
|
||||
return RB_ENTRY(node).Color();
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
void RB_SET_COLOR(Node* node, EntryColor color) {
|
||||
return RB_ENTRY(node).SetColor(color);
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
void RB_SET(Node* node, Node* parent) {
|
||||
auto& entry = RB_ENTRY(node);
|
||||
entry.SetParent(parent);
|
||||
entry.SetLeft(nullptr);
|
||||
entry.SetRight(nullptr);
|
||||
entry.SetColor(EntryColor::Red);
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
void RB_SET_BLACKRED(Node* black, Node* red) {
|
||||
RB_SET_COLOR(black, EntryColor::Black);
|
||||
RB_SET_COLOR(red, EntryColor::Red);
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
void RB_ROTATE_LEFT(RBHead<Node>* head, Node* elm, Node*& tmp) {
|
||||
tmp = RB_RIGHT(elm);
|
||||
RB_SET_RIGHT(elm, RB_LEFT(tmp));
|
||||
if (RB_RIGHT(elm) != nullptr) {
|
||||
RB_SET_PARENT(RB_LEFT(tmp), elm);
|
||||
}
|
||||
|
||||
RB_SET_PARENT(tmp, RB_PARENT(elm));
|
||||
if (RB_PARENT(tmp) != nullptr) {
|
||||
if (elm == RB_LEFT(RB_PARENT(elm))) {
|
||||
RB_SET_LEFT(RB_PARENT(elm), tmp);
|
||||
} else {
|
||||
RB_SET_RIGHT(RB_PARENT(elm), tmp);
|
||||
}
|
||||
} else {
|
||||
head->SetRoot(tmp);
|
||||
}
|
||||
|
||||
RB_SET_LEFT(tmp, elm);
|
||||
RB_SET_PARENT(elm, tmp);
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
void RB_ROTATE_RIGHT(RBHead<Node>* head, Node* elm, Node*& tmp) {
|
||||
tmp = RB_LEFT(elm);
|
||||
RB_SET_LEFT(elm, RB_RIGHT(tmp));
|
||||
if (RB_LEFT(elm) != nullptr) {
|
||||
RB_SET_PARENT(RB_RIGHT(tmp), elm);
|
||||
}
|
||||
|
||||
RB_SET_PARENT(tmp, RB_PARENT(elm));
|
||||
if (RB_PARENT(tmp) != nullptr) {
|
||||
if (elm == RB_LEFT(RB_PARENT(elm))) {
|
||||
RB_SET_LEFT(RB_PARENT(elm), tmp);
|
||||
} else {
|
||||
RB_SET_RIGHT(RB_PARENT(elm), tmp);
|
||||
}
|
||||
} else {
|
||||
head->SetRoot(tmp);
|
||||
}
|
||||
|
||||
RB_SET_RIGHT(tmp, elm);
|
||||
RB_SET_PARENT(elm, tmp);
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
void RB_INSERT_COLOR(RBHead<Node>* head, Node* elm) {
|
||||
Node* parent = nullptr;
|
||||
Node* tmp = nullptr;
|
||||
|
||||
while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) {
|
||||
Node* gparent = RB_PARENT(parent);
|
||||
if (parent == RB_LEFT(gparent)) {
|
||||
tmp = RB_RIGHT(gparent);
|
||||
if (tmp && RB_IS_RED(tmp)) {
|
||||
RB_SET_COLOR(tmp, EntryColor::Black);
|
||||
RB_SET_BLACKRED(parent, gparent);
|
||||
elm = gparent;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (RB_RIGHT(parent) == elm) {
|
||||
RB_ROTATE_LEFT(head, parent, tmp);
|
||||
tmp = parent;
|
||||
parent = elm;
|
||||
elm = tmp;
|
||||
}
|
||||
|
||||
RB_SET_BLACKRED(parent, gparent);
|
||||
RB_ROTATE_RIGHT(head, gparent, tmp);
|
||||
} else {
|
||||
tmp = RB_LEFT(gparent);
|
||||
if (tmp && RB_IS_RED(tmp)) {
|
||||
RB_SET_COLOR(tmp, EntryColor::Black);
|
||||
RB_SET_BLACKRED(parent, gparent);
|
||||
elm = gparent;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (RB_LEFT(parent) == elm) {
|
||||
RB_ROTATE_RIGHT(head, parent, tmp);
|
||||
tmp = parent;
|
||||
parent = elm;
|
||||
elm = tmp;
|
||||
}
|
||||
|
||||
RB_SET_BLACKRED(parent, gparent);
|
||||
RB_ROTATE_LEFT(head, gparent, tmp);
|
||||
}
|
||||
}
|
||||
|
||||
RB_SET_COLOR(head->Root(), EntryColor::Black);
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) {
|
||||
Node* tmp;
|
||||
while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head->Root()) {
|
||||
if (RB_LEFT(parent) == elm) {
|
||||
tmp = RB_RIGHT(parent);
|
||||
if (RB_IS_RED(tmp)) {
|
||||
RB_SET_BLACKRED(tmp, parent);
|
||||
RB_ROTATE_LEFT(head, parent, tmp);
|
||||
tmp = RB_RIGHT(parent);
|
||||
}
|
||||
|
||||
if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) &&
|
||||
(RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) {
|
||||
RB_SET_COLOR(tmp, EntryColor::Red);
|
||||
elm = parent;
|
||||
parent = RB_PARENT(elm);
|
||||
} else {
|
||||
if (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp))) {
|
||||
Node* oleft;
|
||||
if ((oleft = RB_LEFT(tmp)) != nullptr) {
|
||||
RB_SET_COLOR(oleft, EntryColor::Black);
|
||||
}
|
||||
|
||||
RB_SET_COLOR(tmp, EntryColor::Red);
|
||||
RB_ROTATE_RIGHT(head, tmp, oleft);
|
||||
tmp = RB_RIGHT(parent);
|
||||
}
|
||||
|
||||
RB_SET_COLOR(tmp, RB_COLOR(parent));
|
||||
RB_SET_COLOR(parent, EntryColor::Black);
|
||||
if (RB_RIGHT(tmp)) {
|
||||
RB_SET_COLOR(RB_RIGHT(tmp), EntryColor::Black);
|
||||
}
|
||||
|
||||
RB_ROTATE_LEFT(head, parent, tmp);
|
||||
elm = head->Root();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
tmp = RB_LEFT(parent);
|
||||
if (RB_IS_RED(tmp)) {
|
||||
RB_SET_BLACKRED(tmp, parent);
|
||||
RB_ROTATE_RIGHT(head, parent, tmp);
|
||||
tmp = RB_LEFT(parent);
|
||||
}
|
||||
|
||||
if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) &&
|
||||
(RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) {
|
||||
RB_SET_COLOR(tmp, EntryColor::Red);
|
||||
elm = parent;
|
||||
parent = RB_PARENT(elm);
|
||||
} else {
|
||||
if (RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) {
|
||||
Node* oright;
|
||||
if ((oright = RB_RIGHT(tmp)) != nullptr) {
|
||||
RB_SET_COLOR(oright, EntryColor::Black);
|
||||
}
|
||||
|
||||
RB_SET_COLOR(tmp, EntryColor::Red);
|
||||
RB_ROTATE_LEFT(head, tmp, oright);
|
||||
tmp = RB_LEFT(parent);
|
||||
}
|
||||
|
||||
RB_SET_COLOR(tmp, RB_COLOR(parent));
|
||||
RB_SET_COLOR(parent, EntryColor::Black);
|
||||
|
||||
if (RB_LEFT(tmp)) {
|
||||
RB_SET_COLOR(RB_LEFT(tmp), EntryColor::Black);
|
||||
}
|
||||
|
||||
RB_ROTATE_RIGHT(head, parent, tmp);
|
||||
elm = head->Root();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (elm) {
|
||||
RB_SET_COLOR(elm, EntryColor::Black);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
Node* RB_REMOVE(RBHead<Node>* head, Node* elm) {
|
||||
Node* child = nullptr;
|
||||
Node* parent = nullptr;
|
||||
Node* old = elm;
|
||||
EntryColor color{};
|
||||
|
||||
const auto finalize = [&] {
|
||||
if (color == EntryColor::Black) {
|
||||
RB_REMOVE_COLOR(head, parent, child);
|
||||
}
|
||||
|
||||
return old;
|
||||
};
|
||||
|
||||
if (RB_LEFT(elm) == nullptr) {
|
||||
child = RB_RIGHT(elm);
|
||||
} else if (RB_RIGHT(elm) == nullptr) {
|
||||
child = RB_LEFT(elm);
|
||||
} else {
|
||||
Node* left;
|
||||
elm = RB_RIGHT(elm);
|
||||
while ((left = RB_LEFT(elm)) != nullptr) {
|
||||
elm = left;
|
||||
}
|
||||
|
||||
child = RB_RIGHT(elm);
|
||||
parent = RB_PARENT(elm);
|
||||
color = RB_COLOR(elm);
|
||||
|
||||
if (child) {
|
||||
RB_SET_PARENT(child, parent);
|
||||
}
|
||||
if (parent) {
|
||||
if (RB_LEFT(parent) == elm) {
|
||||
RB_SET_LEFT(parent, child);
|
||||
} else {
|
||||
RB_SET_RIGHT(parent, child);
|
||||
}
|
||||
} else {
|
||||
head->SetRoot(child);
|
||||
}
|
||||
|
||||
if (RB_PARENT(elm) == old) {
|
||||
parent = elm;
|
||||
}
|
||||
|
||||
elm->SetEntry(old->GetEntry());
|
||||
|
||||
if (RB_PARENT(old)) {
|
||||
if (RB_LEFT(RB_PARENT(old)) == old) {
|
||||
RB_SET_LEFT(RB_PARENT(old), elm);
|
||||
} else {
|
||||
RB_SET_RIGHT(RB_PARENT(old), elm);
|
||||
}
|
||||
} else {
|
||||
head->SetRoot(elm);
|
||||
}
|
||||
RB_SET_PARENT(RB_LEFT(old), elm);
|
||||
if (RB_RIGHT(old)) {
|
||||
RB_SET_PARENT(RB_RIGHT(old), elm);
|
||||
}
|
||||
if (parent) {
|
||||
left = parent;
|
||||
}
|
||||
|
||||
return finalize();
|
||||
}
|
||||
|
||||
parent = RB_PARENT(elm);
|
||||
color = RB_COLOR(elm);
|
||||
|
||||
if (child) {
|
||||
RB_SET_PARENT(child, parent);
|
||||
}
|
||||
if (parent) {
|
||||
if (RB_LEFT(parent) == elm) {
|
||||
RB_SET_LEFT(parent, child);
|
||||
} else {
|
||||
RB_SET_RIGHT(parent, child);
|
||||
}
|
||||
} else {
|
||||
head->SetRoot(child);
|
||||
}
|
||||
|
||||
return finalize();
|
||||
}
|
||||
|
||||
// Inserts a node into the RB tree
|
||||
template <typename Node, typename CompareFunction>
|
||||
Node* RB_INSERT(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
|
||||
Node* parent = nullptr;
|
||||
Node* tmp = head->Root();
|
||||
int comp = 0;
|
||||
|
||||
while (tmp) {
|
||||
parent = tmp;
|
||||
comp = cmp(elm, parent);
|
||||
if (comp < 0) {
|
||||
tmp = RB_LEFT(tmp);
|
||||
} else if (comp > 0) {
|
||||
tmp = RB_RIGHT(tmp);
|
||||
} else {
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
RB_SET(elm, parent);
|
||||
|
||||
if (parent != nullptr) {
|
||||
if (comp < 0) {
|
||||
RB_SET_LEFT(parent, elm);
|
||||
} else {
|
||||
RB_SET_RIGHT(parent, elm);
|
||||
}
|
||||
} else {
|
||||
head->SetRoot(elm);
|
||||
}
|
||||
|
||||
RB_INSERT_COLOR(head, elm);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Finds the node with the same key as elm
|
||||
template <typename Node, typename CompareFunction>
|
||||
Node* RB_FIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
|
||||
Node* tmp = head->Root();
|
||||
|
||||
while (tmp) {
|
||||
const int comp = cmp(elm, tmp);
|
||||
if (comp < 0) {
|
||||
tmp = RB_LEFT(tmp);
|
||||
} else if (comp > 0) {
|
||||
tmp = RB_RIGHT(tmp);
|
||||
} else {
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Finds the first node greater than or equal to the search key
|
||||
template <typename Node, typename CompareFunction>
|
||||
Node* RB_NFIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
|
||||
Node* tmp = head->Root();
|
||||
Node* res = nullptr;
|
||||
|
||||
while (tmp) {
|
||||
const int comp = cmp(elm, tmp);
|
||||
if (comp < 0) {
|
||||
res = tmp;
|
||||
tmp = RB_LEFT(tmp);
|
||||
} else if (comp > 0) {
|
||||
tmp = RB_RIGHT(tmp);
|
||||
} else {
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Finds the node with the same key as lelm
|
||||
template <typename Node, typename CompareFunction>
|
||||
Node* RB_FIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) {
|
||||
Node* tmp = head->Root();
|
||||
|
||||
while (tmp) {
|
||||
const int comp = lcmp(lelm, tmp);
|
||||
if (comp < 0) {
|
||||
tmp = RB_LEFT(tmp);
|
||||
} else if (comp > 0) {
|
||||
tmp = RB_RIGHT(tmp);
|
||||
} else {
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Finds the first node greater than or equal to the search key
|
||||
template <typename Node, typename CompareFunction>
|
||||
Node* RB_NFIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) {
|
||||
Node* tmp = head->Root();
|
||||
Node* res = nullptr;
|
||||
|
||||
while (tmp) {
|
||||
const int comp = lcmp(lelm, tmp);
|
||||
if (comp < 0) {
|
||||
res = tmp;
|
||||
tmp = RB_LEFT(tmp);
|
||||
} else if (comp > 0) {
|
||||
tmp = RB_RIGHT(tmp);
|
||||
} else {
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
Node* RB_NEXT(Node* elm) {
|
||||
if (RB_RIGHT(elm)) {
|
||||
elm = RB_RIGHT(elm);
|
||||
while (RB_LEFT(elm)) {
|
||||
elm = RB_LEFT(elm);
|
||||
}
|
||||
} else {
|
||||
if (RB_PARENT(elm) && (elm == RB_LEFT(RB_PARENT(elm)))) {
|
||||
elm = RB_PARENT(elm);
|
||||
} else {
|
||||
while (RB_PARENT(elm) && (elm == RB_RIGHT(RB_PARENT(elm)))) {
|
||||
elm = RB_PARENT(elm);
|
||||
}
|
||||
elm = RB_PARENT(elm);
|
||||
}
|
||||
}
|
||||
return elm;
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
Node* RB_PREV(Node* elm) {
|
||||
if (RB_LEFT(elm)) {
|
||||
elm = RB_LEFT(elm);
|
||||
while (RB_RIGHT(elm)) {
|
||||
elm = RB_RIGHT(elm);
|
||||
}
|
||||
} else {
|
||||
if (RB_PARENT(elm) && (elm == RB_RIGHT(RB_PARENT(elm)))) {
|
||||
elm = RB_PARENT(elm);
|
||||
} else {
|
||||
while (RB_PARENT(elm) && (elm == RB_LEFT(RB_PARENT(elm)))) {
|
||||
elm = RB_PARENT(elm);
|
||||
}
|
||||
elm = RB_PARENT(elm);
|
||||
}
|
||||
}
|
||||
return elm;
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
Node* RB_MINMAX(RBHead<Node>* head, bool is_min) {
|
||||
Node* tmp = head->Root();
|
||||
Node* parent = nullptr;
|
||||
|
||||
while (tmp) {
|
||||
parent = tmp;
|
||||
if (is_min) {
|
||||
tmp = RB_LEFT(tmp);
|
||||
} else {
|
||||
tmp = RB_RIGHT(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
Node* RB_MIN(RBHead<Node>* head) {
|
||||
return RB_MINMAX(head, true);
|
||||
}
|
||||
|
||||
template <typename Node>
|
||||
Node* RB_MAX(RBHead<Node>* head) {
|
||||
return RB_MINMAX(head, false);
|
||||
}
|
||||
} // namespace Common
|
||||
@@ -14,8 +14,8 @@ constexpr u128 INVALID_UUID{{0, 0}};
|
||||
|
||||
struct UUID {
|
||||
// UUIDs which are 0 are considered invalid!
|
||||
u128 uuid = INVALID_UUID;
|
||||
constexpr UUID() = default;
|
||||
u128 uuid;
|
||||
UUID() = default;
|
||||
constexpr explicit UUID(const u128& id) : uuid{id} {}
|
||||
constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
|
||||
|
||||
|
||||
@@ -15,10 +15,12 @@ void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
||||
template <typename T>
|
||||
class VirtualBuffer final {
|
||||
public:
|
||||
static_assert(
|
||||
std::is_trivially_constructible_v<T>,
|
||||
"T must be trivially constructible, as non-trivial constructors will not be executed "
|
||||
"with the current allocator");
|
||||
// TODO: Uncomment this and change Common::PageTable::PageInfo to be trivially constructible
|
||||
// using std::atomic_ref once libc++ has support for it
|
||||
// static_assert(
|
||||
// std::is_trivially_constructible_v<T>,
|
||||
// "T must be trivially constructible, as non-trivial constructors will not be executed "
|
||||
// "with the current allocator");
|
||||
|
||||
constexpr VirtualBuffer() = default;
|
||||
explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} {
|
||||
|
||||
@@ -17,8 +17,8 @@ using base_time_point = std::chrono::time_point<base_timer>;
|
||||
|
||||
class StandardWallClock final : public WallClock {
|
||||
public:
|
||||
StandardWallClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency)
|
||||
: WallClock(emulated_cpu_frequency, emulated_clock_frequency, false) {
|
||||
explicit StandardWallClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_)
|
||||
: WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, false) {
|
||||
start_time = base_timer::now();
|
||||
}
|
||||
|
||||
|
||||
@@ -38,9 +38,9 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
WallClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency, bool is_native)
|
||||
: emulated_cpu_frequency{emulated_cpu_frequency},
|
||||
emulated_clock_frequency{emulated_clock_frequency}, is_native{is_native} {}
|
||||
explicit WallClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_, bool is_native_)
|
||||
: emulated_cpu_frequency{emulated_cpu_frequency_},
|
||||
emulated_clock_frequency{emulated_clock_frequency_}, is_native{is_native_} {}
|
||||
|
||||
u64 emulated_cpu_frequency;
|
||||
u64 emulated_clock_frequency;
|
||||
|
||||