Compare commits
563 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93e20867b0 | ||
|
|
b1e27890e8 | ||
|
|
93109c870e | ||
|
|
65e0178cc0 | ||
|
|
9e520e8f12 | ||
|
|
1d162f28d1 | ||
|
|
7ed5dd0d62 | ||
|
|
701ef616b2 | ||
|
|
f7a008d77f | ||
|
|
6a19086001 | ||
|
|
a02566136c | ||
|
|
e7c33d1ad6 | ||
|
|
c9235764c7 | ||
|
|
6da91da08e | ||
|
|
24e1e17a8a | ||
|
|
b5b92fd1e5 | ||
|
|
0d62f30b00 | ||
|
|
51e8b2733c | ||
|
|
9cae3e6e90 | ||
|
|
0c24ae300c | ||
|
|
6686468df0 | ||
|
|
1c550ff954 | ||
|
|
c864f2c532 | ||
|
|
90f93a408a | ||
|
|
f3805376f7 | ||
|
|
9ca5e52f07 | ||
|
|
6be0975bf2 | ||
|
|
723e038dba | ||
|
|
aaccb21f81 | ||
|
|
df1a9d09a9 | ||
|
|
24d0cc3ab8 | ||
|
|
86212d4bcd | ||
|
|
f3345e84ad | ||
|
|
592a649918 | ||
|
|
0a91599aec | ||
|
|
cffa6f4e62 | ||
|
|
ed543c4d5c | ||
|
|
b53b50adec | ||
|
|
48cfc47050 | ||
|
|
90610bde9b | ||
|
|
8378b8a61f | ||
|
|
6269cd7f1d | ||
|
|
b0a3915351 | ||
|
|
eae9f2e440 | ||
|
|
d9a8060ce3 | ||
|
|
594973bdd2 | ||
|
|
51c13606d6 | ||
|
|
d25011c92f | ||
|
|
0bbf5e61f1 | ||
|
|
b8ffdbb167 | ||
|
|
70353649d7 | ||
|
|
95722823b9 | ||
|
|
c7325c6a4c | ||
|
|
b675c44e49 | ||
|
|
3c37d66c28 | ||
|
|
09722cb4a7 | ||
|
|
77564f987c | ||
|
|
ac265a72ce | ||
|
|
83227ad981 | ||
|
|
dd9caf9aa0 | ||
|
|
6171566296 | ||
|
|
682d82faf3 | ||
|
|
710aa22f7c | ||
|
|
6f1ad6aa9f | ||
|
|
06e3d3a658 | ||
|
|
757fd1e917 | ||
|
|
d3c7a7e7cf | ||
|
|
13becdf18a | ||
|
|
5b35b01070 | ||
|
|
025fe458ae | ||
|
|
3a2eefb16c | ||
|
|
0b8b961442 | ||
|
|
93a69b6cc8 | ||
|
|
7402442442 | ||
|
|
75fd3f95a3 | ||
|
|
0b631f22fc | ||
|
|
3da87d3f12 | ||
|
|
2b95c137ff | ||
|
|
ec9354d6d9 | ||
|
|
a02b4e1df6 | ||
|
|
35df1d1864 | ||
|
|
8fd518ec40 | ||
|
|
82c2601555 | ||
|
|
a39d9c5194 | ||
|
|
47d5ec6cfc | ||
|
|
40ed0cb920 | ||
|
|
1a987054c5 | ||
|
|
79afdeaf08 | ||
|
|
004a8d6a7a | ||
|
|
16f97ded21 | ||
|
|
9735c34f5d | ||
|
|
dde19e7d75 | ||
|
|
75ccd9959c | ||
|
|
19156292a3 | ||
|
|
9d8ca6cc4a | ||
|
|
069afcc633 | ||
|
|
7ad63ea542 | ||
|
|
d28b942458 | ||
|
|
4a7fd91857 | ||
|
|
c86d770af9 | ||
|
|
ec9b6641b1 | ||
|
|
5fa6b15215 | ||
|
|
37939482fb | ||
|
|
dcc0617cc2 | ||
|
|
a0379c2db5 | ||
|
|
e53b6ecc76 | ||
|
|
f06c3f4907 | ||
|
|
886043a6d2 | ||
|
|
3e6e0d8f13 | ||
|
|
dee133ab3d | ||
|
|
3c22ce035b | ||
|
|
a5e184e948 | ||
|
|
c44ab0f8f6 | ||
|
|
0e004269a9 | ||
|
|
a58086ae0d | ||
|
|
68f718943e | ||
|
|
70db238f80 | ||
|
|
245d60bfff | ||
|
|
0cd40fb523 | ||
|
|
1dab8acf5f | ||
|
|
d64ba58759 | ||
|
|
a352f34462 | ||
|
|
cda24b8eb1 | ||
|
|
10636d2494 | ||
|
|
783dc9e112 | ||
|
|
864762cac9 | ||
|
|
5c7c212f61 | ||
|
|
bcd4e4f650 | ||
|
|
2e32ab4e0b | ||
|
|
b6736db324 | ||
|
|
d6ebb5c171 | ||
|
|
26669d9e13 | ||
|
|
1b730827dd | ||
|
|
7666c0994c | ||
|
|
d1ac25b632 | ||
|
|
903311729a | ||
|
|
ff58ad2050 | ||
|
|
a994a40467 | ||
|
|
0896089092 | ||
|
|
3f9eb56972 | ||
|
|
52b79ac009 | ||
|
|
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 | ||
|
|
8e77d331be | ||
|
|
ac1e4734c2 | ||
|
|
d235cf3933 | ||
|
|
3753553b6a | ||
|
|
4801f4250d | ||
|
|
7d904fef2e | ||
|
|
3a49c1a691 | ||
|
|
87a8925523 | ||
|
|
974d731926 | ||
|
|
7265e80c12 | ||
|
|
86592b274e | ||
|
|
71e18dddbe | ||
|
|
f64456c7e2 | ||
|
|
ec58aabb26 | ||
|
|
c90268127b | ||
|
|
beaa25d777 | ||
|
|
8a5356357f | ||
|
|
57c9da1b39 | ||
|
|
53d92318b8 | ||
|
|
d4f871cb6a | ||
|
|
5dfb8743cb | ||
|
|
cdbee27692 | ||
|
|
7344a7c447 | ||
|
|
f687392e6f | ||
|
|
53ea06dc17 | ||
|
|
085adfea00 | ||
|
|
11f0f7598d | ||
|
|
dce8720780 | ||
|
|
47843b4f09 | ||
|
|
25f88d99ce | ||
|
|
d1435009ed | ||
|
|
d937421422 | ||
|
|
43cad754d5 | ||
|
|
661483f313 | ||
|
|
ff3aa5d380 | ||
|
|
bc69cc1511 | ||
|
|
24c1bb3842 | ||
|
|
a19dc3bf00 | ||
|
|
d53b79ff5c | ||
|
|
e6f9fe1f60 |
@@ -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 -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
|
||||
|
||||
@@ -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/ ';'
|
||||
|
||||
3
.gitmodules
vendored
@@ -37,3 +37,6 @@
|
||||
[submodule "opus"]
|
||||
path = externals/opus/opus
|
||||
url = https://github.com/xiph/opus.git
|
||||
[submodule "ffmpeg"]
|
||||
path = externals/ffmpeg
|
||||
url = https://git.ffmpeg.org/ffmpeg.git
|
||||
|
||||
158
CMakeLists.txt
@@ -18,6 +18,8 @@ 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)
|
||||
@@ -26,6 +28,10 @@ option(ENABLE_CUBEB "Enables the cubeb audio 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)
|
||||
@@ -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})
|
||||
@@ -239,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()
|
||||
@@ -257,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()
|
||||
|
||||
@@ -322,7 +328,7 @@ if (CONAN_REQUIRED_LIBS)
|
||||
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}")
|
||||
@@ -380,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.3.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,10 +1,6 @@
|
||||
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}
|
||||
avcodec-58.dll
|
||||
avutil-56.dll
|
||||
swresample-3.dll
|
||||
swscale-5.dll
|
||||
)
|
||||
file(READ "${FFmpeg_PATH}/requirements.txt" FFmpeg_REQUIRED_DLLS)
|
||||
windows_copy_files(${target_dir} ${FFmpeg_DLL_DIR} ${DLL_DEST} ${FFmpeg_REQUIRED_DLLS})
|
||||
endfunction(copy_yuzu_FFmpeg_deps)
|
||||
|
||||
@@ -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 |
@@ -1,7 +1,7 @@
|
||||
[Icon Theme]
|
||||
Name=colorful_dark
|
||||
Comment=Colorful theme (Dark style)
|
||||
Inherits=default
|
||||
Inherits=colorful
|
||||
Directories=16x16
|
||||
|
||||
[16x16]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[Icon Theme]
|
||||
Name=colorful_midnight_blue
|
||||
Comment=Colorful theme (Midnight Blue style)
|
||||
Inherits=default
|
||||
Inherits=colorful
|
||||
Directories=16x16
|
||||
|
||||
[16x16]
|
||||
|
||||
@@ -1257,10 +1257,6 @@ QComboBox::item:alternate {
|
||||
background: #19232D;
|
||||
}
|
||||
|
||||
QComboBox::item:checked {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
QComboBox::item:selected {
|
||||
border: 0px solid transparent;
|
||||
}
|
||||
|
||||
BIN
dist/yuzu.bmp
vendored
Normal file
|
After Width: | Height: | Size: 256 KiB |
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,11 +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
|
||||
@@ -122,7 +132,6 @@ add_subdirectory(tests)
|
||||
|
||||
if (ENABLE_SDL2)
|
||||
add_subdirectory(yuzu_cmd)
|
||||
add_subdirectory(yuzu_tester)
|
||||
endif()
|
||||
|
||||
if (ENABLE_QT)
|
||||
|
||||
@@ -15,6 +15,8 @@ add_library(audio_core STATIC
|
||||
command_generator.cpp
|
||||
command_generator.h
|
||||
common.h
|
||||
delay_line.cpp
|
||||
delay_line.h
|
||||
effect_context.cpp
|
||||
effect_context.h
|
||||
info_updater.cpp
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cmath>
|
||||
#include <numbers>
|
||||
#include "audio_core/algorithm/interpolate.h"
|
||||
#include "audio_core/command_generator.h"
|
||||
#include "audio_core/effect_context.h"
|
||||
@@ -13,6 +15,20 @@ namespace AudioCore {
|
||||
namespace {
|
||||
constexpr std::size_t MIX_BUFFER_SIZE = 0x3f00;
|
||||
constexpr std::size_t SCALED_MIX_BUFFER_SIZE = MIX_BUFFER_SIZE << 15ULL;
|
||||
using DelayLineTimes = std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT>;
|
||||
|
||||
constexpr DelayLineTimes FDN_MIN_DELAY_LINE_TIMES{5.0f, 6.0f, 13.0f, 14.0f};
|
||||
constexpr DelayLineTimes FDN_MAX_DELAY_LINE_TIMES{45.704f, 82.782f, 149.94f, 271.58f};
|
||||
constexpr DelayLineTimes DECAY0_MAX_DELAY_LINE_TIMES{17.0f, 13.0f, 9.0f, 7.0f};
|
||||
constexpr DelayLineTimes DECAY1_MAX_DELAY_LINE_TIMES{19.0f, 11.0f, 10.0f, 6.0f};
|
||||
constexpr std::array<f32, AudioCommon::I3DL2REVERB_TAPS> EARLY_TAP_TIMES{
|
||||
0.017136f, 0.059154f, 0.161733f, 0.390186f, 0.425262f, 0.455411f, 0.689737f,
|
||||
0.745910f, 0.833844f, 0.859502f, 0.000000f, 0.075024f, 0.168788f, 0.299901f,
|
||||
0.337443f, 0.371903f, 0.599011f, 0.716741f, 0.817859f, 0.851664f};
|
||||
constexpr std::array<f32, AudioCommon::I3DL2REVERB_TAPS> EARLY_GAIN{
|
||||
0.67096f, 0.61027f, 1.0f, 0.35680f, 0.68361f, 0.65978f, 0.51939f,
|
||||
0.24712f, 0.45945f, 0.45021f, 0.64196f, 0.54879f, 0.92925f, 0.38270f,
|
||||
0.72867f, 0.69794f, 0.5464f, 0.24563f, 0.45214f, 0.44042f};
|
||||
|
||||
template <std::size_t N>
|
||||
void ApplyMix(s32* output, const s32* input, s32 gain, s32 sample_count) {
|
||||
@@ -65,6 +81,154 @@ s32 ApplyMixDepop(s32* output, s32 first_sample, s32 delta, s32 sample_count) {
|
||||
}
|
||||
}
|
||||
|
||||
float Pow10(float x) {
|
||||
if (x >= 0.0f) {
|
||||
return 1.0f;
|
||||
} else if (x <= -5.3f) {
|
||||
return 0.0f;
|
||||
}
|
||||
return std::pow(10.0f, x);
|
||||
}
|
||||
|
||||
float SinD(float degrees) {
|
||||
return std::sin(degrees * std::numbers::pi_v<float> / 180.0f);
|
||||
}
|
||||
|
||||
float CosD(float degrees) {
|
||||
return std::cos(degrees * std::numbers::pi_v<float> / 180.0f);
|
||||
}
|
||||
|
||||
float ToFloat(s32 sample) {
|
||||
return static_cast<float>(sample) / 65536.f;
|
||||
}
|
||||
|
||||
s32 ToS32(float sample) {
|
||||
constexpr auto min = -8388608.0f;
|
||||
constexpr auto max = 8388607.f;
|
||||
float rescaled_sample = sample * 65536.0f;
|
||||
if (rescaled_sample < min) {
|
||||
rescaled_sample = min;
|
||||
}
|
||||
if (rescaled_sample > max) {
|
||||
rescaled_sample = max;
|
||||
}
|
||||
return static_cast<s32>(rescaled_sample);
|
||||
}
|
||||
|
||||
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_1CH{0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_2CH{0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
|
||||
1, 1, 1, 0, 0, 0, 0, 1, 1, 1};
|
||||
|
||||
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_4CH{0, 0, 0, 1, 1, 1, 1, 2, 2, 2,
|
||||
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
|
||||
|
||||
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_6CH{4, 0, 0, 1, 1, 1, 1, 2, 2, 2,
|
||||
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
|
||||
|
||||
template <std::size_t CHANNEL_COUNT>
|
||||
void ApplyReverbGeneric(I3dl2ReverbState& state,
|
||||
const std::array<const s32*, AudioCommon::MAX_CHANNEL_COUNT>& input,
|
||||
const std::array<s32*, AudioCommon::MAX_CHANNEL_COUNT>& output,
|
||||
s32 sample_count) {
|
||||
|
||||
auto GetTapLookup = []() {
|
||||
if constexpr (CHANNEL_COUNT == 1) {
|
||||
return REVERB_TAP_INDEX_1CH;
|
||||
} else if constexpr (CHANNEL_COUNT == 2) {
|
||||
return REVERB_TAP_INDEX_2CH;
|
||||
} else if constexpr (CHANNEL_COUNT == 4) {
|
||||
return REVERB_TAP_INDEX_4CH;
|
||||
} else if constexpr (CHANNEL_COUNT == 6) {
|
||||
return REVERB_TAP_INDEX_6CH;
|
||||
}
|
||||
};
|
||||
|
||||
const auto& tap_index_lut = GetTapLookup();
|
||||
for (s32 sample = 0; sample < sample_count; sample++) {
|
||||
std::array<f32, CHANNEL_COUNT> out_samples{};
|
||||
std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> fsamp{};
|
||||
std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> mixed{};
|
||||
std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> osamp{};
|
||||
|
||||
// Mix everything into a single sample
|
||||
s32 temp_mixed_sample = 0;
|
||||
for (std::size_t i = 0; i < CHANNEL_COUNT; i++) {
|
||||
temp_mixed_sample += input[i][sample];
|
||||
}
|
||||
const auto current_sample = ToFloat(temp_mixed_sample);
|
||||
const auto early_tap = state.early_delay_line.TapOut(state.early_to_late_taps);
|
||||
|
||||
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_TAPS; i++) {
|
||||
const auto tapped_samp =
|
||||
state.early_delay_line.TapOut(state.early_tap_steps[i]) * EARLY_GAIN[i];
|
||||
out_samples[tap_index_lut[i]] += tapped_samp;
|
||||
|
||||
if constexpr (CHANNEL_COUNT == 6) {
|
||||
// handle lfe
|
||||
out_samples[5] += tapped_samp;
|
||||
}
|
||||
}
|
||||
|
||||
state.lowpass_0 = current_sample * state.lowpass_2 + state.lowpass_0 * state.lowpass_1;
|
||||
state.early_delay_line.Tick(state.lowpass_0);
|
||||
|
||||
for (std::size_t i = 0; i < CHANNEL_COUNT; i++) {
|
||||
out_samples[i] *= state.early_gain;
|
||||
}
|
||||
|
||||
// Two channel seems to apply a latet gain, we require to save this
|
||||
f32 filter{};
|
||||
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
|
||||
filter = state.fdn_delay_line[i].GetOutputSample();
|
||||
const auto computed = filter * state.lpf_coefficients[0][i] + state.shelf_filter[i];
|
||||
state.shelf_filter[i] =
|
||||
filter * state.lpf_coefficients[1][i] + computed * state.lpf_coefficients[2][i];
|
||||
fsamp[i] = computed;
|
||||
}
|
||||
|
||||
// Mixing matrix
|
||||
mixed[0] = fsamp[1] + fsamp[2];
|
||||
mixed[1] = -fsamp[0] - fsamp[3];
|
||||
mixed[2] = fsamp[0] - fsamp[3];
|
||||
mixed[3] = fsamp[1] - fsamp[2];
|
||||
|
||||
if constexpr (CHANNEL_COUNT == 2) {
|
||||
for (auto& mix : mixed) {
|
||||
mix *= (filter * state.late_gain);
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
|
||||
const auto late = early_tap * state.late_gain;
|
||||
osamp[i] = state.decay_delay_line0[i].Tick(late + mixed[i]);
|
||||
osamp[i] = state.decay_delay_line1[i].Tick(osamp[i]);
|
||||
state.fdn_delay_line[i].Tick(osamp[i]);
|
||||
}
|
||||
|
||||
if constexpr (CHANNEL_COUNT == 1) {
|
||||
output[0][sample] = ToS32(state.dry_gain * ToFloat(input[0][sample]) +
|
||||
(out_samples[0] + osamp[0] + osamp[1]));
|
||||
} else if constexpr (CHANNEL_COUNT == 2 || CHANNEL_COUNT == 4) {
|
||||
for (std::size_t i = 0; i < CHANNEL_COUNT; i++) {
|
||||
output[i][sample] =
|
||||
ToS32(state.dry_gain * ToFloat(input[i][sample]) + (out_samples[i] + osamp[i]));
|
||||
}
|
||||
} else if constexpr (CHANNEL_COUNT == 6) {
|
||||
const auto temp_center = state.center_delay_line.Tick(0.5f * (osamp[2] - osamp[3]));
|
||||
for (std::size_t i = 0; i < 4; i++) {
|
||||
output[i][sample] =
|
||||
ToS32(state.dry_gain * ToFloat(input[i][sample]) + (out_samples[i] + osamp[i]));
|
||||
}
|
||||
output[4][sample] =
|
||||
ToS32(state.dry_gain * ToFloat(input[4][sample]) + (out_samples[4] + temp_center));
|
||||
output[5][sample] =
|
||||
ToS32(state.dry_gain * ToFloat(input[5][sample]) + (out_samples[5] + osamp[3]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_params_,
|
||||
@@ -271,11 +435,10 @@ void CommandGenerator::GenerateBiquadFilterCommandForVoice(ServerVoiceInfo& voic
|
||||
}
|
||||
|
||||
// Generate biquad filter
|
||||
// GenerateBiquadFilterCommand(mix_buffer_count, biquad_filter,
|
||||
// dsp_state.biquad_filter_state,
|
||||
// mix_buffer_count + channel, mix_buffer_count +
|
||||
// channel, worker_params.sample_count,
|
||||
// voice_info.GetInParams().node_id);
|
||||
// GenerateBiquadFilterCommand(mix_buffer_count, biquad_filter,
|
||||
// dsp_state.biquad_filter_state,
|
||||
// mix_buffer_count + channel, mix_buffer_count + channel,
|
||||
// worker_params.sample_count, voice_info.GetInParams().node_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,17 +539,53 @@ void CommandGenerator::GenerateEffectCommand(ServerMixInfo& mix_info) {
|
||||
|
||||
void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info,
|
||||
bool enabled) {
|
||||
if (!enabled) {
|
||||
auto* reverb = dynamic_cast<EffectI3dl2Reverb*>(info);
|
||||
const auto& params = reverb->GetParams();
|
||||
auto& state = reverb->GetState();
|
||||
const auto channel_count = params.channel_count;
|
||||
|
||||
if (channel_count != 1 && channel_count != 2 && channel_count != 4 && channel_count != 6) {
|
||||
return;
|
||||
}
|
||||
const auto& params = dynamic_cast<EffectI3dl2Reverb*>(info)->GetParams();
|
||||
const auto channel_count = params.channel_count;
|
||||
|
||||
std::array<const s32*, AudioCommon::MAX_CHANNEL_COUNT> input{};
|
||||
std::array<s32*, AudioCommon::MAX_CHANNEL_COUNT> output{};
|
||||
|
||||
const auto status = params.status;
|
||||
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);
|
||||
input[i] = GetMixBuffer(mix_buffer_offset + params.input[i]);
|
||||
output[i] = GetMixBuffer(mix_buffer_offset + params.output[i]);
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
if (status == ParameterStatus::Initialized) {
|
||||
InitializeI3dl2Reverb(reverb->GetParams(), state, info->GetWorkBuffer());
|
||||
} else if (status == ParameterStatus::Updating) {
|
||||
UpdateI3dl2Reverb(reverb->GetParams(), state, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
switch (channel_count) {
|
||||
case 1:
|
||||
ApplyReverbGeneric<1>(state, input, output, worker_params.sample_count);
|
||||
break;
|
||||
case 2:
|
||||
ApplyReverbGeneric<2>(state, input, output, worker_params.sample_count);
|
||||
break;
|
||||
case 4:
|
||||
ApplyReverbGeneric<4>(state, input, output, worker_params.sample_count);
|
||||
break;
|
||||
case 6:
|
||||
ApplyReverbGeneric<6>(state, input, output, worker_params.sample_count);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
for (s32 i = 0; i < channel_count; i++) {
|
||||
// Only copy if the buffer input and output do not match!
|
||||
if ((mix_buffer_offset + params.input[i]) != (mix_buffer_offset + params.output[i])) {
|
||||
std::memcpy(output[i], input[i], worker_params.sample_count * sizeof(s32));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -525,6 +724,133 @@ s32 CommandGenerator::ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u3
|
||||
return sample_count;
|
||||
}
|
||||
|
||||
void CommandGenerator::InitializeI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state,
|
||||
std::vector<u8>& work_buffer) {
|
||||
// Reset state
|
||||
state.lowpass_0 = 0.0f;
|
||||
state.lowpass_1 = 0.0f;
|
||||
state.lowpass_2 = 0.0f;
|
||||
|
||||
state.early_delay_line.Reset();
|
||||
state.early_tap_steps.fill(0);
|
||||
state.early_gain = 0.0f;
|
||||
state.late_gain = 0.0f;
|
||||
state.early_to_late_taps = 0;
|
||||
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
|
||||
state.fdn_delay_line[i].Reset();
|
||||
state.decay_delay_line0[i].Reset();
|
||||
state.decay_delay_line1[i].Reset();
|
||||
}
|
||||
state.last_reverb_echo = 0.0f;
|
||||
state.center_delay_line.Reset();
|
||||
for (auto& coef : state.lpf_coefficients) {
|
||||
coef.fill(0.0f);
|
||||
}
|
||||
state.shelf_filter.fill(0.0f);
|
||||
state.dry_gain = 0.0f;
|
||||
|
||||
const auto sample_rate = info.sample_rate / 1000;
|
||||
f32* work_buffer_ptr = reinterpret_cast<f32*>(work_buffer.data());
|
||||
|
||||
s32 delay_samples{};
|
||||
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
|
||||
delay_samples =
|
||||
AudioCommon::CalculateDelaySamples(sample_rate, FDN_MAX_DELAY_LINE_TIMES[i]);
|
||||
state.fdn_delay_line[i].Initialize(delay_samples, work_buffer_ptr);
|
||||
work_buffer_ptr += delay_samples + 1;
|
||||
|
||||
delay_samples =
|
||||
AudioCommon::CalculateDelaySamples(sample_rate, DECAY0_MAX_DELAY_LINE_TIMES[i]);
|
||||
state.decay_delay_line0[i].Initialize(delay_samples, 0.0f, work_buffer_ptr);
|
||||
work_buffer_ptr += delay_samples + 1;
|
||||
|
||||
delay_samples =
|
||||
AudioCommon::CalculateDelaySamples(sample_rate, DECAY1_MAX_DELAY_LINE_TIMES[i]);
|
||||
state.decay_delay_line1[i].Initialize(delay_samples, 0.0f, work_buffer_ptr);
|
||||
work_buffer_ptr += delay_samples + 1;
|
||||
}
|
||||
delay_samples = AudioCommon::CalculateDelaySamples(sample_rate, 5.0f);
|
||||
state.center_delay_line.Initialize(delay_samples, work_buffer_ptr);
|
||||
work_buffer_ptr += delay_samples + 1;
|
||||
|
||||
delay_samples = AudioCommon::CalculateDelaySamples(sample_rate, 400.0f);
|
||||
state.early_delay_line.Initialize(delay_samples, work_buffer_ptr);
|
||||
|
||||
UpdateI3dl2Reverb(info, state, true);
|
||||
}
|
||||
|
||||
void CommandGenerator::UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state,
|
||||
bool should_clear) {
|
||||
|
||||
state.dry_gain = info.dry_gain;
|
||||
state.shelf_filter.fill(0.0f);
|
||||
state.lowpass_0 = 0.0f;
|
||||
state.early_gain = Pow10(std::min(info.room + info.reflection, 5000.0f) / 2000.0f);
|
||||
state.late_gain = Pow10(std::min(info.room + info.reverb, 5000.0f) / 2000.0f);
|
||||
|
||||
const auto sample_rate = info.sample_rate / 1000;
|
||||
const f32 hf_gain = Pow10(info.room_hf / 2000.0f);
|
||||
if (hf_gain >= 1.0f) {
|
||||
state.lowpass_2 = 1.0f;
|
||||
state.lowpass_1 = 0.0f;
|
||||
} else {
|
||||
const auto a = 1.0f - hf_gain;
|
||||
const auto b = 2.0f * (1.0f - hf_gain * CosD(256.0f * info.hf_reference /
|
||||
static_cast<f32>(info.sample_rate)));
|
||||
const auto c = std::sqrt(b * b - 4.0f * a * a);
|
||||
|
||||
state.lowpass_1 = (b - c) / (2.0f * a);
|
||||
state.lowpass_2 = 1.0f - state.lowpass_1;
|
||||
}
|
||||
state.early_to_late_taps = AudioCommon::CalculateDelaySamples(
|
||||
sample_rate, 1000.0f * (info.reflection_delay + info.reverb_delay));
|
||||
|
||||
state.last_reverb_echo = 0.6f * info.diffusion * 0.01f;
|
||||
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
|
||||
const auto length =
|
||||
FDN_MIN_DELAY_LINE_TIMES[i] +
|
||||
(info.density / 100.0f) * (FDN_MAX_DELAY_LINE_TIMES[i] - FDN_MIN_DELAY_LINE_TIMES[i]);
|
||||
state.fdn_delay_line[i].SetDelay(AudioCommon::CalculateDelaySamples(sample_rate, length));
|
||||
|
||||
const auto delay_sample_counts = state.fdn_delay_line[i].GetDelay() +
|
||||
state.decay_delay_line0[i].GetDelay() +
|
||||
state.decay_delay_line1[i].GetDelay();
|
||||
|
||||
float a = (-60.0f * static_cast<f32>(delay_sample_counts)) /
|
||||
(info.decay_time * static_cast<f32>(info.sample_rate));
|
||||
float b = a / info.hf_decay_ratio;
|
||||
float c = CosD(128.0f * 0.5f * info.hf_reference / static_cast<f32>(info.sample_rate)) /
|
||||
SinD(128.0f * 0.5f * info.hf_reference / static_cast<f32>(info.sample_rate));
|
||||
float d = Pow10((b - a) / 40.0f);
|
||||
float e = Pow10((b + a) / 40.0f) * 0.7071f;
|
||||
|
||||
state.lpf_coefficients[0][i] = e * ((d * c) + 1.0f) / (c + d);
|
||||
state.lpf_coefficients[1][i] = e * (1.0f - (d * c)) / (c + d);
|
||||
state.lpf_coefficients[2][i] = (c - d) / (c + d);
|
||||
|
||||
state.decay_delay_line0[i].SetCoefficient(state.last_reverb_echo);
|
||||
state.decay_delay_line1[i].SetCoefficient(-0.9f * state.last_reverb_echo);
|
||||
}
|
||||
|
||||
if (should_clear) {
|
||||
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
|
||||
state.fdn_delay_line[i].Clear();
|
||||
state.decay_delay_line0[i].Clear();
|
||||
state.decay_delay_line1[i].Clear();
|
||||
}
|
||||
state.early_delay_line.Clear();
|
||||
state.center_delay_line.Clear();
|
||||
}
|
||||
|
||||
const auto max_early_delay = state.early_delay_line.GetMaxDelay();
|
||||
const auto reflection_time = 1000.0f * (0.0098f * info.reverb_delay + 0.02f);
|
||||
for (std::size_t tap = 0; tap < AudioCommon::I3DL2REVERB_TAPS; tap++) {
|
||||
const auto length = AudioCommon::CalculateDelaySamples(
|
||||
sample_rate, 1000.0f * info.reflection_delay + reflection_time * EARLY_TAP_TIMES[tap]);
|
||||
state.early_tap_steps[tap] = std::min(length, max_early_delay);
|
||||
}
|
||||
}
|
||||
|
||||
void CommandGenerator::GenerateVolumeRampCommand(float last_volume, float current_volume,
|
||||
s32 channel, s32 node_id) {
|
||||
const auto last = static_cast<s32>(last_volume * 32768.0f);
|
||||
|
||||
@@ -21,6 +21,8 @@ class ServerMixInfo;
|
||||
class EffectContext;
|
||||
class EffectBase;
|
||||
struct AuxInfoDSP;
|
||||
struct I3dl2ReverbParams;
|
||||
struct I3dl2ReverbState;
|
||||
using MixVolumeBuffer = std::array<float, AudioCommon::MAX_MIX_BUFFERS>;
|
||||
|
||||
class CommandGenerator {
|
||||
@@ -80,6 +82,9 @@ private:
|
||||
s32 ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples, s32* out_data,
|
||||
u32 sample_count, u32 read_offset, u32 read_count);
|
||||
|
||||
void InitializeI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state,
|
||||
std::vector<u8>& work_buffer);
|
||||
void UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state, bool should_clear);
|
||||
// DSP Code
|
||||
s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count,
|
||||
s32 channel, std::size_t mix_offset);
|
||||
|
||||
@@ -33,6 +33,29 @@ constexpr std::size_t TEMP_MIX_BASE_SIZE = 0x3f00; // TODO(ogniK): Work out this
|
||||
// and our const ends up being 0x3f04, the 4 bytes are most
|
||||
// likely the sample history
|
||||
constexpr std::size_t TOTAL_TEMP_MIX_SIZE = TEMP_MIX_BASE_SIZE + AudioCommon::MAX_SAMPLE_HISTORY;
|
||||
constexpr f32 I3DL2REVERB_MAX_LEVEL = 5000.0f;
|
||||
constexpr f32 I3DL2REVERB_MIN_REFLECTION_DURATION = 0.02f;
|
||||
constexpr std::size_t I3DL2REVERB_TAPS = 20;
|
||||
constexpr std::size_t I3DL2REVERB_DELAY_LINE_COUNT = 4;
|
||||
using Fractional = s32;
|
||||
|
||||
template <typename T>
|
||||
constexpr Fractional ToFractional(T x) {
|
||||
return static_cast<Fractional>(x * static_cast<T>(0x4000));
|
||||
}
|
||||
|
||||
constexpr Fractional MultiplyFractional(Fractional lhs, Fractional rhs) {
|
||||
return static_cast<Fractional>(static_cast<s64>(lhs) * rhs >> 14);
|
||||
}
|
||||
|
||||
constexpr s32 FractionalToFixed(Fractional x) {
|
||||
const auto s = x & (1 << 13);
|
||||
return static_cast<s32>(x >> 14) + s;
|
||||
}
|
||||
|
||||
constexpr s32 CalculateDelaySamples(s32 sample_rate_khz, float time) {
|
||||
return FractionalToFixed(MultiplyFractional(ToFractional(sample_rate_khz), ToFractional(time)));
|
||||
}
|
||||
|
||||
static constexpr u32 VersionFromRevision(u32_le rev) {
|
||||
// "REV7" -> 7
|
||||
|
||||
104
src/audio_core/delay_line.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
#include <cstring>
|
||||
#include "audio_core/delay_line.h"
|
||||
|
||||
namespace AudioCore {
|
||||
DelayLineBase::DelayLineBase() = default;
|
||||
DelayLineBase::~DelayLineBase() = default;
|
||||
|
||||
void DelayLineBase::Initialize(s32 max_delay_, float* src_buffer) {
|
||||
buffer = src_buffer;
|
||||
buffer_end = buffer + max_delay_;
|
||||
max_delay = max_delay_;
|
||||
output = buffer;
|
||||
SetDelay(max_delay_);
|
||||
Clear();
|
||||
}
|
||||
|
||||
void DelayLineBase::SetDelay(s32 new_delay) {
|
||||
if (max_delay < new_delay) {
|
||||
return;
|
||||
}
|
||||
delay = new_delay;
|
||||
input = (buffer + ((output - buffer) + new_delay) % (max_delay + 1));
|
||||
}
|
||||
|
||||
s32 DelayLineBase::GetDelay() const {
|
||||
return delay;
|
||||
}
|
||||
|
||||
s32 DelayLineBase::GetMaxDelay() const {
|
||||
return max_delay;
|
||||
}
|
||||
|
||||
f32 DelayLineBase::TapOut(s32 last_sample) {
|
||||
const float* ptr = input - (last_sample + 1);
|
||||
if (ptr < buffer) {
|
||||
ptr += (max_delay + 1);
|
||||
}
|
||||
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
f32 DelayLineBase::Tick(f32 sample) {
|
||||
*(input++) = sample;
|
||||
const auto out_sample = *(output++);
|
||||
|
||||
if (buffer_end < input) {
|
||||
input = buffer;
|
||||
}
|
||||
|
||||
if (buffer_end < output) {
|
||||
output = buffer;
|
||||
}
|
||||
|
||||
return out_sample;
|
||||
}
|
||||
|
||||
float* DelayLineBase::GetInput() {
|
||||
return input;
|
||||
}
|
||||
|
||||
const float* DelayLineBase::GetInput() const {
|
||||
return input;
|
||||
}
|
||||
|
||||
f32 DelayLineBase::GetOutputSample() const {
|
||||
return *output;
|
||||
}
|
||||
|
||||
void DelayLineBase::Clear() {
|
||||
std::memset(buffer, 0, sizeof(float) * max_delay);
|
||||
}
|
||||
|
||||
void DelayLineBase::Reset() {
|
||||
buffer = nullptr;
|
||||
buffer_end = nullptr;
|
||||
max_delay = 0;
|
||||
input = nullptr;
|
||||
output = nullptr;
|
||||
delay = 0;
|
||||
}
|
||||
|
||||
DelayLineAllPass::DelayLineAllPass() = default;
|
||||
DelayLineAllPass::~DelayLineAllPass() = default;
|
||||
|
||||
void DelayLineAllPass::Initialize(u32 delay_, float coeffcient_, f32* src_buffer) {
|
||||
DelayLineBase::Initialize(delay_, src_buffer);
|
||||
SetCoefficient(coeffcient_);
|
||||
}
|
||||
|
||||
void DelayLineAllPass::SetCoefficient(float coeffcient_) {
|
||||
coefficient = coeffcient_;
|
||||
}
|
||||
|
||||
f32 DelayLineAllPass::Tick(f32 sample) {
|
||||
const auto temp = sample - coefficient * *output;
|
||||
return coefficient * temp + DelayLineBase::Tick(temp);
|
||||
}
|
||||
|
||||
void DelayLineAllPass::Reset() {
|
||||
coefficient = 0.0f;
|
||||
DelayLineBase::Reset();
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
46
src/audio_core/delay_line.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class DelayLineBase {
|
||||
public:
|
||||
DelayLineBase();
|
||||
~DelayLineBase();
|
||||
|
||||
void Initialize(s32 max_delay_, float* src_buffer);
|
||||
void SetDelay(s32 new_delay);
|
||||
s32 GetDelay() const;
|
||||
s32 GetMaxDelay() const;
|
||||
f32 TapOut(s32 last_sample);
|
||||
f32 Tick(f32 sample);
|
||||
float* GetInput();
|
||||
const float* GetInput() const;
|
||||
f32 GetOutputSample() const;
|
||||
void Clear();
|
||||
void Reset();
|
||||
|
||||
protected:
|
||||
float* buffer{nullptr};
|
||||
float* buffer_end{nullptr};
|
||||
s32 max_delay{};
|
||||
float* input{nullptr};
|
||||
float* output{nullptr};
|
||||
s32 delay{};
|
||||
};
|
||||
|
||||
class DelayLineAllPass final : public DelayLineBase {
|
||||
public:
|
||||
DelayLineAllPass();
|
||||
~DelayLineAllPass();
|
||||
|
||||
void Initialize(u32 delay, float coeffcient_, f32* src_buffer);
|
||||
void SetCoefficient(float coeffcient_);
|
||||
f32 Tick(f32 sample);
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
float coefficient{};
|
||||
};
|
||||
} // namespace AudioCore
|
||||
@@ -90,6 +90,14 @@ s32 EffectBase::GetProcessingOrder() const {
|
||||
return processing_order;
|
||||
}
|
||||
|
||||
std::vector<u8>& EffectBase::GetWorkBuffer() {
|
||||
return work_buffer;
|
||||
}
|
||||
|
||||
const std::vector<u8>& EffectBase::GetWorkBuffer() const {
|
||||
return work_buffer;
|
||||
}
|
||||
|
||||
EffectI3dl2Reverb::EffectI3dl2Reverb() : EffectGeneric(EffectType::I3dl2Reverb) {}
|
||||
EffectI3dl2Reverb::~EffectI3dl2Reverb() = default;
|
||||
|
||||
@@ -117,6 +125,12 @@ void EffectI3dl2Reverb::Update(EffectInfo::InParams& in_params) {
|
||||
usage = UsageState::Initialized;
|
||||
params.status = ParameterStatus::Initialized;
|
||||
skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
|
||||
if (!skipped) {
|
||||
auto& cur_work_buffer = GetWorkBuffer();
|
||||
// Has two buffers internally
|
||||
cur_work_buffer.resize(in_params.buffer_size * 2);
|
||||
std::fill(cur_work_buffer.begin(), cur_work_buffer.end(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +143,14 @@ void EffectI3dl2Reverb::UpdateForCommandGeneration() {
|
||||
GetParams().status = ParameterStatus::Updated;
|
||||
}
|
||||
|
||||
I3dl2ReverbState& EffectI3dl2Reverb::GetState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
const I3dl2ReverbState& EffectI3dl2Reverb::GetState() const {
|
||||
return state;
|
||||
}
|
||||
|
||||
EffectBiquadFilter::EffectBiquadFilter() : EffectGeneric(EffectType::BiquadFilter) {}
|
||||
EffectBiquadFilter::~EffectBiquadFilter() = default;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "audio_core/common.h"
|
||||
#include "audio_core/delay_line.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
@@ -194,6 +195,8 @@ public:
|
||||
[[nodiscard]] bool IsEnabled() const;
|
||||
[[nodiscard]] s32 GetMixID() const;
|
||||
[[nodiscard]] s32 GetProcessingOrder() const;
|
||||
[[nodiscard]] std::vector<u8>& GetWorkBuffer();
|
||||
[[nodiscard]] const std::vector<u8>& GetWorkBuffer() const;
|
||||
|
||||
protected:
|
||||
UsageState usage{UsageState::Invalid};
|
||||
@@ -201,6 +204,7 @@ protected:
|
||||
s32 mix_id{};
|
||||
s32 processing_order{};
|
||||
bool enabled = false;
|
||||
std::vector<u8> work_buffer{};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@@ -212,7 +216,7 @@ public:
|
||||
return internal_params;
|
||||
}
|
||||
|
||||
const I3dl2ReverbParams& GetParams() const {
|
||||
const T& GetParams() const {
|
||||
return internal_params;
|
||||
}
|
||||
|
||||
@@ -229,6 +233,27 @@ public:
|
||||
void UpdateForCommandGeneration() override;
|
||||
};
|
||||
|
||||
struct I3dl2ReverbState {
|
||||
f32 lowpass_0{};
|
||||
f32 lowpass_1{};
|
||||
f32 lowpass_2{};
|
||||
|
||||
DelayLineBase early_delay_line{};
|
||||
std::array<u32, AudioCommon::I3DL2REVERB_TAPS> early_tap_steps{};
|
||||
f32 early_gain{};
|
||||
f32 late_gain{};
|
||||
|
||||
u32 early_to_late_taps{};
|
||||
std::array<DelayLineBase, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> fdn_delay_line{};
|
||||
std::array<DelayLineAllPass, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> decay_delay_line0{};
|
||||
std::array<DelayLineAllPass, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> decay_delay_line1{};
|
||||
f32 last_reverb_echo{};
|
||||
DelayLineBase center_delay_line{};
|
||||
std::array<std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT>, 3> lpf_coefficients{};
|
||||
std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> shelf_filter{};
|
||||
f32 dry_gain{};
|
||||
};
|
||||
|
||||
class EffectI3dl2Reverb : public EffectGeneric<I3dl2ReverbParams> {
|
||||
public:
|
||||
explicit EffectI3dl2Reverb();
|
||||
@@ -237,8 +262,12 @@ public:
|
||||
void Update(EffectInfo::InParams& in_params) override;
|
||||
void UpdateForCommandGeneration() override;
|
||||
|
||||
I3dl2ReverbState& GetState();
|
||||
const I3dl2ReverbState& GetState() const;
|
||||
|
||||
private:
|
||||
bool skipped = false;
|
||||
I3dl2ReverbState state{};
|
||||
};
|
||||
|
||||
class EffectBiquadFilter : public EffectGeneric<BiquadFilterParams> {
|
||||
|
||||
@@ -40,17 +40,17 @@ 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(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;
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -98,7 +98,6 @@ add_library(common STATIC
|
||||
algorithm.h
|
||||
alignment.h
|
||||
assert.h
|
||||
atomic_ops.cpp
|
||||
atomic_ops.h
|
||||
detached_tasks.cpp
|
||||
detached_tasks.h
|
||||
@@ -108,7 +107,6 @@ add_library(common STATIC
|
||||
bit_util.h
|
||||
cityhash.cpp
|
||||
cityhash.h
|
||||
color.h
|
||||
common_funcs.h
|
||||
common_paths.h
|
||||
common_types.h
|
||||
@@ -123,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
|
||||
@@ -139,10 +138,13 @@ add_library(common STATIC
|
||||
microprofile.h
|
||||
microprofileui.h
|
||||
misc.cpp
|
||||
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
|
||||
@@ -165,9 +167,8 @@ add_library(common STATIC
|
||||
threadsafe_queue.h
|
||||
time_zone.cpp
|
||||
time_zone.h
|
||||
timer.cpp
|
||||
timer.h
|
||||
uint128.cpp
|
||||
tiny_mt.h
|
||||
tree.h
|
||||
uint128.h
|
||||
uuid.cpp
|
||||
uuid.h
|
||||
@@ -205,6 +206,8 @@ if (MSVC)
|
||||
else()
|
||||
target_compile_options(common PRIVATE
|
||||
-Werror
|
||||
|
||||
$<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -9,50 +9,50 @@
|
||||
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, typename U>
|
||||
requires std::is_integral_v<T>[[nodiscard]] constexpr T DivideUp(T x, U y) {
|
||||
return (x + (y - 1)) / y;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -28,8 +28,10 @@
|
||||
// compromising on hash quality.
|
||||
|
||||
#include <algorithm>
|
||||
#include <string.h> // for memcpy and memset
|
||||
#include "cityhash.h"
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
|
||||
#include "common/cityhash.h"
|
||||
#include "common/swap.h"
|
||||
|
||||
// #include "config.h"
|
||||
@@ -42,21 +44,17 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef uint8_t uint8;
|
||||
typedef uint32_t uint32;
|
||||
typedef uint64_t uint64;
|
||||
|
||||
namespace Common {
|
||||
|
||||
static uint64 UNALIGNED_LOAD64(const char* p) {
|
||||
uint64 result;
|
||||
memcpy(&result, p, sizeof(result));
|
||||
static u64 unaligned_load64(const char* p) {
|
||||
u64 result;
|
||||
std::memcpy(&result, p, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint32 UNALIGNED_LOAD32(const char* p) {
|
||||
uint32 result;
|
||||
memcpy(&result, p, sizeof(result));
|
||||
static u32 unaligned_load32(const char* p) {
|
||||
u32 result;
|
||||
std::memcpy(&result, p, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -76,64 +74,64 @@ static uint32 UNALIGNED_LOAD32(const char* p) {
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static uint64 Fetch64(const char* p) {
|
||||
return uint64_in_expected_order(UNALIGNED_LOAD64(p));
|
||||
static u64 Fetch64(const char* p) {
|
||||
return uint64_in_expected_order(unaligned_load64(p));
|
||||
}
|
||||
|
||||
static uint32 Fetch32(const char* p) {
|
||||
return uint32_in_expected_order(UNALIGNED_LOAD32(p));
|
||||
static u32 Fetch32(const char* p) {
|
||||
return uint32_in_expected_order(unaligned_load32(p));
|
||||
}
|
||||
|
||||
// Some primes between 2^63 and 2^64 for various uses.
|
||||
static const uint64 k0 = 0xc3a5c85c97cb3127ULL;
|
||||
static const uint64 k1 = 0xb492b66fbe98f273ULL;
|
||||
static const uint64 k2 = 0x9ae16a3b2f90404fULL;
|
||||
static constexpr u64 k0 = 0xc3a5c85c97cb3127ULL;
|
||||
static constexpr u64 k1 = 0xb492b66fbe98f273ULL;
|
||||
static constexpr u64 k2 = 0x9ae16a3b2f90404fULL;
|
||||
|
||||
// Bitwise right rotate. Normally this will compile to a single
|
||||
// instruction, especially if the shift is a manifest constant.
|
||||
static uint64 Rotate(uint64 val, int shift) {
|
||||
static u64 Rotate(u64 val, int shift) {
|
||||
// Avoid shifting by 64: doing so yields an undefined result.
|
||||
return shift == 0 ? val : ((val >> shift) | (val << (64 - shift)));
|
||||
}
|
||||
|
||||
static uint64 ShiftMix(uint64 val) {
|
||||
static u64 ShiftMix(u64 val) {
|
||||
return val ^ (val >> 47);
|
||||
}
|
||||
|
||||
static uint64 HashLen16(uint64 u, uint64 v) {
|
||||
return Hash128to64(uint128(u, v));
|
||||
static u64 HashLen16(u64 u, u64 v) {
|
||||
return Hash128to64(u128{u, v});
|
||||
}
|
||||
|
||||
static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) {
|
||||
static u64 HashLen16(u64 u, u64 v, u64 mul) {
|
||||
// Murmur-inspired hashing.
|
||||
uint64 a = (u ^ v) * mul;
|
||||
u64 a = (u ^ v) * mul;
|
||||
a ^= (a >> 47);
|
||||
uint64 b = (v ^ a) * mul;
|
||||
u64 b = (v ^ a) * mul;
|
||||
b ^= (b >> 47);
|
||||
b *= mul;
|
||||
return b;
|
||||
}
|
||||
|
||||
static uint64 HashLen0to16(const char* s, std::size_t len) {
|
||||
static u64 HashLen0to16(const char* s, size_t len) {
|
||||
if (len >= 8) {
|
||||
uint64 mul = k2 + len * 2;
|
||||
uint64 a = Fetch64(s) + k2;
|
||||
uint64 b = Fetch64(s + len - 8);
|
||||
uint64 c = Rotate(b, 37) * mul + a;
|
||||
uint64 d = (Rotate(a, 25) + b) * mul;
|
||||
u64 mul = k2 + len * 2;
|
||||
u64 a = Fetch64(s) + k2;
|
||||
u64 b = Fetch64(s + len - 8);
|
||||
u64 c = Rotate(b, 37) * mul + a;
|
||||
u64 d = (Rotate(a, 25) + b) * mul;
|
||||
return HashLen16(c, d, mul);
|
||||
}
|
||||
if (len >= 4) {
|
||||
uint64 mul = k2 + len * 2;
|
||||
uint64 a = Fetch32(s);
|
||||
u64 mul = k2 + len * 2;
|
||||
u64 a = Fetch32(s);
|
||||
return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul);
|
||||
}
|
||||
if (len > 0) {
|
||||
uint8 a = s[0];
|
||||
uint8 b = s[len >> 1];
|
||||
uint8 c = s[len - 1];
|
||||
uint32 y = static_cast<uint32>(a) + (static_cast<uint32>(b) << 8);
|
||||
uint32 z = static_cast<uint32>(len) + (static_cast<uint32>(c) << 2);
|
||||
u8 a = s[0];
|
||||
u8 b = s[len >> 1];
|
||||
u8 c = s[len - 1];
|
||||
u32 y = static_cast<u32>(a) + (static_cast<u32>(b) << 8);
|
||||
u32 z = static_cast<u32>(len) + (static_cast<u32>(c) << 2);
|
||||
return ShiftMix(y * k2 ^ z * k0) * k2;
|
||||
}
|
||||
return k2;
|
||||
@@ -141,22 +139,21 @@ static uint64 HashLen0to16(const char* s, std::size_t len) {
|
||||
|
||||
// This probably works well for 16-byte strings as well, but it may be overkill
|
||||
// in that case.
|
||||
static uint64 HashLen17to32(const char* s, std::size_t len) {
|
||||
uint64 mul = k2 + len * 2;
|
||||
uint64 a = Fetch64(s) * k1;
|
||||
uint64 b = Fetch64(s + 8);
|
||||
uint64 c = Fetch64(s + len - 8) * mul;
|
||||
uint64 d = Fetch64(s + len - 16) * k2;
|
||||
static u64 HashLen17to32(const char* s, size_t len) {
|
||||
u64 mul = k2 + len * 2;
|
||||
u64 a = Fetch64(s) * k1;
|
||||
u64 b = Fetch64(s + 8);
|
||||
u64 c = Fetch64(s + len - 8) * mul;
|
||||
u64 d = Fetch64(s + len - 16) * k2;
|
||||
return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d, a + Rotate(b + k2, 18) + c, mul);
|
||||
}
|
||||
|
||||
// Return a 16-byte hash for 48 bytes. Quick and dirty.
|
||||
// Callers do best to use "random-looking" values for a and b.
|
||||
static pair<uint64, uint64> WeakHashLen32WithSeeds(uint64 w, uint64 x, uint64 y, uint64 z, uint64 a,
|
||||
uint64 b) {
|
||||
static pair<u64, u64> WeakHashLen32WithSeeds(u64 w, u64 x, u64 y, u64 z, u64 a, u64 b) {
|
||||
a += w;
|
||||
b = Rotate(b + a + z, 21);
|
||||
uint64 c = a;
|
||||
u64 c = a;
|
||||
a += x;
|
||||
a += y;
|
||||
b += Rotate(a, 44);
|
||||
@@ -164,34 +161,34 @@ static pair<uint64, uint64> WeakHashLen32WithSeeds(uint64 w, uint64 x, uint64 y,
|
||||
}
|
||||
|
||||
// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty.
|
||||
static pair<uint64, uint64> WeakHashLen32WithSeeds(const char* s, uint64 a, uint64 b) {
|
||||
static pair<u64, u64> WeakHashLen32WithSeeds(const char* s, u64 a, u64 b) {
|
||||
return WeakHashLen32WithSeeds(Fetch64(s), Fetch64(s + 8), Fetch64(s + 16), Fetch64(s + 24), a,
|
||||
b);
|
||||
}
|
||||
|
||||
// Return an 8-byte hash for 33 to 64 bytes.
|
||||
static uint64 HashLen33to64(const char* s, std::size_t len) {
|
||||
uint64 mul = k2 + len * 2;
|
||||
uint64 a = Fetch64(s) * k2;
|
||||
uint64 b = Fetch64(s + 8);
|
||||
uint64 c = Fetch64(s + len - 24);
|
||||
uint64 d = Fetch64(s + len - 32);
|
||||
uint64 e = Fetch64(s + 16) * k2;
|
||||
uint64 f = Fetch64(s + 24) * 9;
|
||||
uint64 g = Fetch64(s + len - 8);
|
||||
uint64 h = Fetch64(s + len - 16) * mul;
|
||||
uint64 u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9;
|
||||
uint64 v = ((a + g) ^ d) + f + 1;
|
||||
uint64 w = swap64((u + v) * mul) + h;
|
||||
uint64 x = Rotate(e + f, 42) + c;
|
||||
uint64 y = (swap64((v + w) * mul) + g) * mul;
|
||||
uint64 z = e + f + c;
|
||||
static u64 HashLen33to64(const char* s, size_t len) {
|
||||
u64 mul = k2 + len * 2;
|
||||
u64 a = Fetch64(s) * k2;
|
||||
u64 b = Fetch64(s + 8);
|
||||
u64 c = Fetch64(s + len - 24);
|
||||
u64 d = Fetch64(s + len - 32);
|
||||
u64 e = Fetch64(s + 16) * k2;
|
||||
u64 f = Fetch64(s + 24) * 9;
|
||||
u64 g = Fetch64(s + len - 8);
|
||||
u64 h = Fetch64(s + len - 16) * mul;
|
||||
u64 u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9;
|
||||
u64 v = ((a + g) ^ d) + f + 1;
|
||||
u64 w = swap64((u + v) * mul) + h;
|
||||
u64 x = Rotate(e + f, 42) + c;
|
||||
u64 y = (swap64((v + w) * mul) + g) * mul;
|
||||
u64 z = e + f + c;
|
||||
a = swap64((x + z) * mul + y) + b;
|
||||
b = ShiftMix((z + a) * mul + d + h) * mul;
|
||||
return b + x;
|
||||
}
|
||||
|
||||
uint64 CityHash64(const char* s, std::size_t len) {
|
||||
u64 CityHash64(const char* s, size_t len) {
|
||||
if (len <= 32) {
|
||||
if (len <= 16) {
|
||||
return HashLen0to16(s, len);
|
||||
@@ -204,15 +201,15 @@ uint64 CityHash64(const char* s, std::size_t len) {
|
||||
|
||||
// For strings over 64 bytes we hash the end first, and then as we
|
||||
// loop we keep 56 bytes of state: v, w, x, y, and z.
|
||||
uint64 x = Fetch64(s + len - 40);
|
||||
uint64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56);
|
||||
uint64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24));
|
||||
pair<uint64, uint64> v = WeakHashLen32WithSeeds(s + len - 64, len, z);
|
||||
pair<uint64, uint64> w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x);
|
||||
u64 x = Fetch64(s + len - 40);
|
||||
u64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56);
|
||||
u64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24));
|
||||
pair<u64, u64> v = WeakHashLen32WithSeeds(s + len - 64, len, z);
|
||||
pair<u64, u64> w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x);
|
||||
x = x * k1 + Fetch64(s);
|
||||
|
||||
// Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.
|
||||
len = (len - 1) & ~static_cast<std::size_t>(63);
|
||||
len = (len - 1) & ~static_cast<size_t>(63);
|
||||
do {
|
||||
x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
|
||||
y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
|
||||
@@ -229,21 +226,21 @@ uint64 CityHash64(const char* s, std::size_t len) {
|
||||
HashLen16(v.second, w.second) + x);
|
||||
}
|
||||
|
||||
uint64 CityHash64WithSeed(const char* s, std::size_t len, uint64 seed) {
|
||||
u64 CityHash64WithSeed(const char* s, size_t len, u64 seed) {
|
||||
return CityHash64WithSeeds(s, len, k2, seed);
|
||||
}
|
||||
|
||||
uint64 CityHash64WithSeeds(const char* s, std::size_t len, uint64 seed0, uint64 seed1) {
|
||||
u64 CityHash64WithSeeds(const char* s, size_t len, u64 seed0, u64 seed1) {
|
||||
return HashLen16(CityHash64(s, len) - seed0, seed1);
|
||||
}
|
||||
|
||||
// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings
|
||||
// of any length representable in signed long. Based on City and Murmur.
|
||||
static uint128 CityMurmur(const char* s, std::size_t len, uint128 seed) {
|
||||
uint64 a = Uint128Low64(seed);
|
||||
uint64 b = Uint128High64(seed);
|
||||
uint64 c = 0;
|
||||
uint64 d = 0;
|
||||
static u128 CityMurmur(const char* s, size_t len, u128 seed) {
|
||||
u64 a = seed[0];
|
||||
u64 b = seed[1];
|
||||
u64 c = 0;
|
||||
u64 d = 0;
|
||||
signed long l = static_cast<long>(len) - 16;
|
||||
if (l <= 0) { // len <= 16
|
||||
a = ShiftMix(a * k1) * k1;
|
||||
@@ -266,20 +263,20 @@ static uint128 CityMurmur(const char* s, std::size_t len, uint128 seed) {
|
||||
}
|
||||
a = HashLen16(a, c);
|
||||
b = HashLen16(d, b);
|
||||
return uint128(a ^ b, HashLen16(b, a));
|
||||
return u128{a ^ b, HashLen16(b, a)};
|
||||
}
|
||||
|
||||
uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) {
|
||||
u128 CityHash128WithSeed(const char* s, size_t len, u128 seed) {
|
||||
if (len < 128) {
|
||||
return CityMurmur(s, len, seed);
|
||||
}
|
||||
|
||||
// We expect len >= 128 to be the common case. Keep 56 bytes of state:
|
||||
// v, w, x, y, and z.
|
||||
pair<uint64, uint64> v, w;
|
||||
uint64 x = Uint128Low64(seed);
|
||||
uint64 y = Uint128High64(seed);
|
||||
uint64 z = len * k1;
|
||||
pair<u64, u64> v, w;
|
||||
u64 x = seed[0];
|
||||
u64 y = seed[1];
|
||||
u64 z = len * k1;
|
||||
v.first = Rotate(y ^ k1, 49) * k1 + Fetch64(s);
|
||||
v.second = Rotate(v.first, 42) * k1 + Fetch64(s + 8);
|
||||
w.first = Rotate(y + z, 35) * k1 + x;
|
||||
@@ -313,7 +310,7 @@ uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) {
|
||||
w.first *= 9;
|
||||
v.first *= k0;
|
||||
// If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s.
|
||||
for (std::size_t tail_done = 0; tail_done < len;) {
|
||||
for (size_t tail_done = 0; tail_done < len;) {
|
||||
tail_done += 32;
|
||||
y = Rotate(x + y, 42) * k0 + v.second;
|
||||
w.first += Fetch64(s + len - tail_done + 16);
|
||||
@@ -328,13 +325,12 @@ uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) {
|
||||
// different 56-byte-to-8-byte hashes to get a 16-byte final result.
|
||||
x = HashLen16(x, v.first);
|
||||
y = HashLen16(y + z, w.first);
|
||||
return uint128(HashLen16(x + v.second, w.second) + y, HashLen16(x + w.second, y + v.second));
|
||||
return u128{HashLen16(x + v.second, w.second) + y, HashLen16(x + w.second, y + v.second)};
|
||||
}
|
||||
|
||||
uint128 CityHash128(const char* s, std::size_t len) {
|
||||
return len >= 16
|
||||
? CityHash128WithSeed(s + 16, len - 16, uint128(Fetch64(s), Fetch64(s + 8) + k0))
|
||||
: CityHash128WithSeed(s, len, uint128(k0, k1));
|
||||
u128 CityHash128(const char* s, size_t len) {
|
||||
return len >= 16 ? CityHash128WithSeed(s + 16, len - 16, u128{Fetch64(s), Fetch64(s + 8) + k0})
|
||||
: CityHash128WithSeed(s, len, u128{k0, k1});
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -61,50 +61,38 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
using uint128 = std::pair<uint64_t, uint64_t>;
|
||||
|
||||
[[nodiscard]] inline uint64_t Uint128Low64(const uint128& x) {
|
||||
return x.first;
|
||||
}
|
||||
[[nodiscard]] inline uint64_t Uint128High64(const uint128& x) {
|
||||
return x.second;
|
||||
}
|
||||
|
||||
// Hash function for a byte array.
|
||||
[[nodiscard]] uint64_t CityHash64(const char* buf, std::size_t len);
|
||||
[[nodiscard]] u64 CityHash64(const char* buf, size_t len);
|
||||
|
||||
// Hash function for a byte array. For convenience, a 64-bit seed is also
|
||||
// hashed into the result.
|
||||
[[nodiscard]] uint64_t CityHash64WithSeed(const char* buf, std::size_t len, uint64_t seed);
|
||||
[[nodiscard]] u64 CityHash64WithSeed(const char* buf, size_t len, u64 seed);
|
||||
|
||||
// Hash function for a byte array. For convenience, two seeds are also
|
||||
// hashed into the result.
|
||||
[[nodiscard]] uint64_t CityHash64WithSeeds(const char* buf, std::size_t len, uint64_t seed0,
|
||||
uint64_t seed1);
|
||||
[[nodiscard]] u64 CityHash64WithSeeds(const char* buf, size_t len, u64 seed0, u64 seed1);
|
||||
|
||||
// Hash function for a byte array.
|
||||
[[nodiscard]] uint128 CityHash128(const char* s, std::size_t len);
|
||||
[[nodiscard]] u128 CityHash128(const char* s, size_t len);
|
||||
|
||||
// Hash function for a byte array. For convenience, a 128-bit seed is also
|
||||
// hashed into the result.
|
||||
[[nodiscard]] uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed);
|
||||
[[nodiscard]] u128 CityHash128WithSeed(const char* s, size_t len, u128 seed);
|
||||
|
||||
// Hash 128 input bits down to 64 bits of output.
|
||||
// This is intended to be a reasonably good hash function.
|
||||
[[nodiscard]] inline uint64_t Hash128to64(const uint128& x) {
|
||||
[[nodiscard]] inline u64 Hash128to64(const u128& x) {
|
||||
// Murmur-inspired hashing.
|
||||
const uint64_t kMul = 0x9ddfea08eb382d69ULL;
|
||||
uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul;
|
||||
const u64 mul = 0x9ddfea08eb382d69ULL;
|
||||
u64 a = (x[0] ^ x[1]) * mul;
|
||||
a ^= (a >> 47);
|
||||
uint64_t b = (Uint128High64(x) ^ a) * kMul;
|
||||
u64 b = (x[1] ^ a) * mul;
|
||||
b ^= (b >> 47);
|
||||
b *= kMul;
|
||||
b *= mul;
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
250
src/common/tiny_mt.h
Normal file
@@ -0,0 +1,250 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
// Implementation of TinyMT (mersenne twister RNG).
|
||||
// Like Nintendo, we will use the sample parameters.
|
||||
class TinyMT {
|
||||
public:
|
||||
static constexpr std::size_t NumStateWords = 4;
|
||||
|
||||
struct State {
|
||||
std::array<u32, NumStateWords> data{};
|
||||
};
|
||||
|
||||
private:
|
||||
static constexpr u32 ParamMat1 = 0x8F7011EE;
|
||||
static constexpr u32 ParamMat2 = 0xFC78FF1F;
|
||||
static constexpr u32 ParamTmat = 0x3793FDFF;
|
||||
|
||||
static constexpr u32 ParamMult = 0x6C078965;
|
||||
static constexpr u32 ParamPlus = 0x0019660D;
|
||||
static constexpr u32 ParamXor = 0x5D588B65;
|
||||
|
||||
static constexpr u32 TopBitmask = 0x7FFFFFFF;
|
||||
|
||||
static constexpr int MinimumInitIterations = 8;
|
||||
static constexpr int NumDiscardedInitOutputs = 8;
|
||||
|
||||
static constexpr u32 XorByShifted27(u32 value) {
|
||||
return value ^ (value >> 27);
|
||||
}
|
||||
|
||||
static constexpr u32 XorByShifted30(u32 value) {
|
||||
return value ^ (value >> 30);
|
||||
}
|
||||
|
||||
private:
|
||||
State state{};
|
||||
|
||||
private:
|
||||
// Internal API.
|
||||
void FinalizeInitialization() {
|
||||
const u32 state0 = this->state.data[0] & TopBitmask;
|
||||
const u32 state1 = this->state.data[1];
|
||||
const u32 state2 = this->state.data[2];
|
||||
const u32 state3 = this->state.data[3];
|
||||
|
||||
if (state0 == 0 && state1 == 0 && state2 == 0 && state3 == 0) {
|
||||
this->state.data[0] = 'T';
|
||||
this->state.data[1] = 'I';
|
||||
this->state.data[2] = 'N';
|
||||
this->state.data[3] = 'Y';
|
||||
}
|
||||
|
||||
for (int i = 0; i < NumDiscardedInitOutputs; i++) {
|
||||
this->GenerateRandomU32();
|
||||
}
|
||||
}
|
||||
|
||||
u32 GenerateRandomU24() {
|
||||
return (this->GenerateRandomU32() >> 8);
|
||||
}
|
||||
|
||||
static void GenerateInitialValuePlus(TinyMT::State* state, int index, u32 value) {
|
||||
u32& state0 = state->data[(index + 0) % NumStateWords];
|
||||
u32& state1 = state->data[(index + 1) % NumStateWords];
|
||||
u32& state2 = state->data[(index + 2) % NumStateWords];
|
||||
u32& state3 = state->data[(index + 3) % NumStateWords];
|
||||
|
||||
const u32 x = XorByShifted27(state0 ^ state1 ^ state3) * ParamPlus;
|
||||
const u32 y = x + index + value;
|
||||
|
||||
state0 = y;
|
||||
state1 += x;
|
||||
state2 += y;
|
||||
}
|
||||
|
||||
static void GenerateInitialValueXor(TinyMT::State* state, int index) {
|
||||
u32& state0 = state->data[(index + 0) % NumStateWords];
|
||||
u32& state1 = state->data[(index + 1) % NumStateWords];
|
||||
u32& state2 = state->data[(index + 2) % NumStateWords];
|
||||
u32& state3 = state->data[(index + 3) % NumStateWords];
|
||||
|
||||
const u32 x = XorByShifted27(state0 + state1 + state3) * ParamXor;
|
||||
const u32 y = x - index;
|
||||
|
||||
state0 = y;
|
||||
state1 ^= x;
|
||||
state2 ^= y;
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr TinyMT() = default;
|
||||
|
||||
// Public API.
|
||||
|
||||
// Initialization.
|
||||
void Initialize(u32 seed) {
|
||||
this->state.data[0] = seed;
|
||||
this->state.data[1] = ParamMat1;
|
||||
this->state.data[2] = ParamMat2;
|
||||
this->state.data[3] = ParamTmat;
|
||||
|
||||
for (int i = 1; i < MinimumInitIterations; i++) {
|
||||
const u32 mixed = XorByShifted30(this->state.data[(i - 1) % NumStateWords]);
|
||||
this->state.data[i % NumStateWords] ^= mixed * ParamMult + i;
|
||||
}
|
||||
|
||||
this->FinalizeInitialization();
|
||||
}
|
||||
|
||||
void Initialize(const u32* seed, int seed_count) {
|
||||
this->state.data[0] = 0;
|
||||
this->state.data[1] = ParamMat1;
|
||||
this->state.data[2] = ParamMat2;
|
||||
this->state.data[3] = ParamTmat;
|
||||
|
||||
{
|
||||
const int num_init_iterations = std::max(seed_count + 1, MinimumInitIterations) - 1;
|
||||
|
||||
GenerateInitialValuePlus(&this->state, 0, seed_count);
|
||||
|
||||
for (int i = 0; i < num_init_iterations; i++) {
|
||||
GenerateInitialValuePlus(&this->state, (i + 1) % NumStateWords,
|
||||
(i < seed_count) ? seed[i] : 0);
|
||||
}
|
||||
|
||||
for (int i = 0; i < static_cast<int>(NumStateWords); i++) {
|
||||
GenerateInitialValueXor(&this->state,
|
||||
(i + 1 + num_init_iterations) % NumStateWords);
|
||||
}
|
||||
}
|
||||
|
||||
this->FinalizeInitialization();
|
||||
}
|
||||
|
||||
// State management.
|
||||
void GetState(TinyMT::State& out) const {
|
||||
out.data = this->state.data;
|
||||
}
|
||||
|
||||
void SetState(const TinyMT::State& state_) {
|
||||
this->state.data = state_.data;
|
||||
}
|
||||
|
||||
// Random generation.
|
||||
void GenerateRandomBytes(void* dst, std::size_t size) {
|
||||
const uintptr_t start = reinterpret_cast<uintptr_t>(dst);
|
||||
const uintptr_t end = start + size;
|
||||
const uintptr_t aligned_start = Common::AlignUp(start, 4);
|
||||
const uintptr_t aligned_end = Common::AlignDown(end, 4);
|
||||
|
||||
// Make sure we're aligned.
|
||||
if (start < aligned_start) {
|
||||
const u32 rnd = this->GenerateRandomU32();
|
||||
std::memcpy(dst, &rnd, aligned_start - start);
|
||||
}
|
||||
|
||||
// Write as many aligned u32s as we can.
|
||||
{
|
||||
u32* cur_dst = reinterpret_cast<u32*>(aligned_start);
|
||||
u32* const end_dst = reinterpret_cast<u32*>(aligned_end);
|
||||
|
||||
while (cur_dst < end_dst) {
|
||||
*(cur_dst++) = this->GenerateRandomU32();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle any leftover unaligned data.
|
||||
if (aligned_end < end) {
|
||||
const u32 rnd = this->GenerateRandomU32();
|
||||
std::memcpy(reinterpret_cast<void*>(aligned_end), &rnd, end - aligned_end);
|
||||
}
|
||||
}
|
||||
|
||||
u32 GenerateRandomU32() {
|
||||
// Advance state.
|
||||
const u32 x0 =
|
||||
(this->state.data[0] & TopBitmask) ^ this->state.data[1] ^ this->state.data[2];
|
||||
const u32 y0 = this->state.data[3];
|
||||
const u32 x1 = x0 ^ (x0 << 1);
|
||||
const u32 y1 = y0 ^ (y0 >> 1) ^ x1;
|
||||
|
||||
const u32 state0 = this->state.data[1];
|
||||
u32 state1 = this->state.data[2];
|
||||
u32 state2 = x1 ^ (y1 << 10);
|
||||
const u32 state3 = y1;
|
||||
|
||||
if ((y1 & 1) != 0) {
|
||||
state1 ^= ParamMat1;
|
||||
state2 ^= ParamMat2;
|
||||
}
|
||||
|
||||
this->state.data[0] = state0;
|
||||
this->state.data[1] = state1;
|
||||
this->state.data[2] = state2;
|
||||
this->state.data[3] = state3;
|
||||
|
||||
// Temper.
|
||||
const u32 t1 = state0 + (state2 >> 8);
|
||||
u32 t0 = state3 ^ t1;
|
||||
|
||||
if ((t1 & 1) != 0) {
|
||||
t0 ^= ParamTmat;
|
||||
}
|
||||
|
||||
return t0;
|
||||
}
|
||||
|
||||
u64 GenerateRandomU64() {
|
||||
const u32 lo = this->GenerateRandomU32();
|
||||
const u32 hi = this->GenerateRandomU32();
|
||||
return (u64{hi} << 32) | u64{lo};
|
||||
}
|
||||
|
||||
float GenerateRandomF32() {
|
||||
// Floats have 24 bits of mantissa.
|
||||
constexpr u32 MantissaBits = 24;
|
||||
return static_cast<float>(GenerateRandomU24()) * (1.0f / (1U << MantissaBits));
|
||||
}
|
||||
|
||||
double GenerateRandomF64() {
|
||||
// Doubles have 53 bits of mantissa.
|
||||
// The smart way to generate 53 bits of random would be to use 32 bits
|
||||
// from the first rnd32() call, and then 21 from the second.
|
||||
// Nintendo does not. They use (32 - 5) = 27 bits from the first rnd32()
|
||||
// call, and (32 - 6) bits from the second. We'll do what they do, but
|
||||
// There's not a clear reason why.
|
||||
constexpr u32 MantissaBits = 53;
|
||||
constexpr u32 Shift1st = (64 - MantissaBits) / 2;
|
||||
constexpr u32 Shift2nd = (64 - MantissaBits) - Shift1st;
|
||||
|
||||
const u32 first = (this->GenerateRandomU32() >> Shift1st);
|
||||
const u32 second = (this->GenerateRandomU32() >> Shift2nd);
|
||||
|
||||
return (1.0 * first * (u64{1} << (32 - Shift2nd)) + second) *
|
||||
(1.0 / (u64{1} << MantissaBits));
|
||||
}
|
||||
};
|
||||
|
||||
} // 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
|
||||
@@ -1,71 +0,0 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
|
||||
#pragma intrinsic(_umul128)
|
||||
#pragma intrinsic(_udiv128)
|
||||
#endif
|
||||
#include <cstring>
|
||||
#include "common/uint128.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) {
|
||||
u128 r{};
|
||||
r[0] = _umul128(a, b, &r[1]);
|
||||
u64 remainder;
|
||||
#if _MSC_VER < 1923
|
||||
return udiv128(r[1], r[0], d, &remainder);
|
||||
#else
|
||||
return _udiv128(r[1], r[0], d, &remainder);
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) {
|
||||
const u64 diva = a / d;
|
||||
const u64 moda = a % d;
|
||||
const u64 divb = b / d;
|
||||
const u64 modb = b % d;
|
||||
return diva * b + moda * divb + moda * modb / d;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
u128 Multiply64Into128(u64 a, u64 b) {
|
||||
u128 result;
|
||||
#ifdef _MSC_VER
|
||||
result[0] = _umul128(a, b, &result[1]);
|
||||
#else
|
||||
unsigned __int128 tmp = a;
|
||||
tmp *= b;
|
||||
std::memcpy(&result, &tmp, sizeof(u128));
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor) {
|
||||
u64 remainder = dividend[0] % divisor;
|
||||
u64 accum = dividend[0] / divisor;
|
||||
if (dividend[1] == 0)
|
||||
return {accum, remainder};
|
||||
// We ignore dividend[1] / divisor as that overflows
|
||||
const u64 first_segment = (dividend[1] % divisor) << 32;
|
||||
accum += (first_segment / divisor) << 32;
|
||||
const u64 second_segment = (first_segment % divisor) << 32;
|
||||
accum += (second_segment / divisor);
|
||||
remainder += second_segment % divisor;
|
||||
if (remainder >= divisor) {
|
||||
accum++;
|
||||
remainder -= divisor;
|
||||
}
|
||||
return {accum, remainder};
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -4,19 +4,98 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#pragma intrinsic(__umulh)
|
||||
#pragma intrinsic(_umul128)
|
||||
#pragma intrinsic(_udiv128)
|
||||
#else
|
||||
#include <x86intrin.h>
|
||||
#endif
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
// This function multiplies 2 u64 values and divides it by a u64 value.
|
||||
[[nodiscard]] u64 MultiplyAndDivide64(u64 a, u64 b, u64 d);
|
||||
[[nodiscard]] static inline u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) {
|
||||
#ifdef _MSC_VER
|
||||
u128 r{};
|
||||
r[0] = _umul128(a, b, &r[1]);
|
||||
u64 remainder;
|
||||
#if _MSC_VER < 1923
|
||||
return udiv128(r[1], r[0], d, &remainder);
|
||||
#else
|
||||
return _udiv128(r[1], r[0], d, &remainder);
|
||||
#endif
|
||||
#else
|
||||
const u64 diva = a / d;
|
||||
const u64 moda = a % d;
|
||||
const u64 divb = b / d;
|
||||
const u64 modb = b % d;
|
||||
return diva * b + moda * divb + moda * modb / d;
|
||||
#endif
|
||||
}
|
||||
|
||||
// This function multiplies 2 u64 values and produces a u128 value;
|
||||
[[nodiscard]] u128 Multiply64Into128(u64 a, u64 b);
|
||||
[[nodiscard]] static inline u128 Multiply64Into128(u64 a, u64 b) {
|
||||
u128 result;
|
||||
#ifdef _MSC_VER
|
||||
result[0] = _umul128(a, b, &result[1]);
|
||||
#else
|
||||
unsigned __int128 tmp = a;
|
||||
tmp *= b;
|
||||
std::memcpy(&result, &tmp, sizeof(u128));
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
// This function divides a u128 by a u32 value and produces two u64 values:
|
||||
// the result of division and the remainder
|
||||
[[nodiscard]] std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor);
|
||||
[[nodiscard]] static inline u64 GetFixedPoint64Factor(u64 numerator, u64 divisor) {
|
||||
#ifdef __SIZEOF_INT128__
|
||||
const auto base = static_cast<unsigned __int128>(numerator) << 64ULL;
|
||||
return static_cast<u64>(base / divisor);
|
||||
#elif defined(_M_X64) || defined(_M_ARM64)
|
||||
std::array<u64, 2> r = {0, numerator};
|
||||
u64 remainder;
|
||||
#if _MSC_VER < 1923
|
||||
return udiv128(r[1], r[0], divisor, &remainder);
|
||||
#else
|
||||
return _udiv128(r[1], r[0], divisor, &remainder);
|
||||
#endif
|
||||
#else
|
||||
// This one is bit more inaccurate.
|
||||
return MultiplyAndDivide64(std::numeric_limits<u64>::max(), numerator, divisor);
|
||||
#endif
|
||||
}
|
||||
|
||||
[[nodiscard]] static inline u64 MultiplyHigh(u64 a, u64 b) {
|
||||
#ifdef __SIZEOF_INT128__
|
||||
return (static_cast<unsigned __int128>(a) * static_cast<unsigned __int128>(b)) >> 64;
|
||||
#elif defined(_M_X64) || defined(_M_ARM64)
|
||||
return __umulh(a, b); // MSVC
|
||||
#else
|
||||
// Generic fallback
|
||||
const u64 a_lo = u32(a);
|
||||
const u64 a_hi = a >> 32;
|
||||
const u64 b_lo = u32(b);
|
||||
const u64 b_hi = b >> 32;
|
||||
|
||||
const u64 a_x_b_hi = a_hi * b_hi;
|
||||
const u64 a_x_b_mid = a_hi * b_lo;
|
||||
const u64 b_x_a_mid = b_hi * a_lo;
|
||||
const u64 a_x_b_lo = a_lo * b_lo;
|
||||
|
||||
const u64 carry_bit = (static_cast<u64>(static_cast<u32>(a_x_b_mid)) +
|
||||
static_cast<u64>(static_cast<u32>(b_x_a_mid)) + (a_x_b_lo >> 32)) >>
|
||||
32;
|
||||
|
||||
const u64 multhi = a_x_b_hi + (a_x_b_mid >> 32) + (b_x_a_mid >> 32) + carry_bit;
|
||||
|
||||
return multhi;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // 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}} {}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "common/uint128.h"
|
||||
#include "common/wall_clock.h"
|
||||
|
||||
@@ -18,7 +20,9 @@ using base_time_point = std::chrono::time_point<base_timer>;
|
||||
class StandardWallClock final : public WallClock {
|
||||
public:
|
||||
explicit StandardWallClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_)
|
||||
: WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, false) {
|
||||
: WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, false),
|
||||
emulated_clock_factor{GetFixedPoint64Factor(emulated_clock_frequency, 1000000000)},
|
||||
emulated_cpu_factor{GetFixedPoint64Factor(emulated_cpu_frequency, 1000000000)} {
|
||||
start_time = base_timer::now();
|
||||
}
|
||||
|
||||
@@ -41,16 +45,11 @@ public:
|
||||
}
|
||||
|
||||
u64 GetClockCycles() override {
|
||||
std::chrono::nanoseconds time_now = GetTimeNS();
|
||||
const u128 temporary =
|
||||
Common::Multiply64Into128(time_now.count(), emulated_clock_frequency);
|
||||
return Common::Divide128On32(temporary, 1000000000).first;
|
||||
return MultiplyHigh(GetTimeNS().count(), emulated_clock_factor);
|
||||
}
|
||||
|
||||
u64 GetCPUCycles() override {
|
||||
std::chrono::nanoseconds time_now = GetTimeNS();
|
||||
const u128 temporary = Common::Multiply64Into128(time_now.count(), emulated_cpu_frequency);
|
||||
return Common::Divide128On32(temporary, 1000000000).first;
|
||||
return MultiplyHigh(GetTimeNS().count(), emulated_cpu_factor);
|
||||
}
|
||||
|
||||
void Pause([[maybe_unused]] bool is_paused) override {
|
||||
@@ -59,6 +58,8 @@ public:
|
||||
|
||||
private:
|
||||
base_time_point start_time;
|
||||
const u64 emulated_clock_factor;
|
||||
const u64 emulated_cpu_factor;
|
||||
};
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
|
||||
@@ -2,16 +2,13 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#else
|
||||
#include <x86intrin.h>
|
||||
#endif
|
||||
|
||||
#include "common/atomic_ops.h"
|
||||
#include "common/uint128.h"
|
||||
#include "common/x64/native_clock.h"
|
||||
|
||||
@@ -48,54 +45,71 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
|
||||
: WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{
|
||||
rtsc_frequency_} {
|
||||
_mm_mfence();
|
||||
last_measure = __rdtsc();
|
||||
accumulated_ticks = 0U;
|
||||
time_point.inner.last_measure = __rdtsc();
|
||||
time_point.inner.accumulated_ticks = 0U;
|
||||
ns_rtsc_factor = GetFixedPoint64Factor(1000000000, rtsc_frequency);
|
||||
us_rtsc_factor = GetFixedPoint64Factor(1000000, rtsc_frequency);
|
||||
ms_rtsc_factor = GetFixedPoint64Factor(1000, rtsc_frequency);
|
||||
clock_rtsc_factor = GetFixedPoint64Factor(emulated_clock_frequency, rtsc_frequency);
|
||||
cpu_rtsc_factor = GetFixedPoint64Factor(emulated_cpu_frequency, rtsc_frequency);
|
||||
}
|
||||
|
||||
u64 NativeClock::GetRTSC() {
|
||||
std::scoped_lock scope{rtsc_serialize};
|
||||
_mm_mfence();
|
||||
const u64 current_measure = __rdtsc();
|
||||
u64 diff = current_measure - last_measure;
|
||||
diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
|
||||
if (current_measure > last_measure) {
|
||||
last_measure = current_measure;
|
||||
}
|
||||
accumulated_ticks += diff;
|
||||
TimePoint new_time_point{};
|
||||
TimePoint current_time_point{};
|
||||
do {
|
||||
current_time_point.pack = time_point.pack;
|
||||
_mm_mfence();
|
||||
const u64 current_measure = __rdtsc();
|
||||
u64 diff = current_measure - current_time_point.inner.last_measure;
|
||||
diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
|
||||
new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure
|
||||
? current_measure
|
||||
: current_time_point.inner.last_measure;
|
||||
new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff;
|
||||
} while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
|
||||
current_time_point.pack));
|
||||
/// The clock cannot be more precise than the guest timer, remove the lower bits
|
||||
return accumulated_ticks & inaccuracy_mask;
|
||||
return new_time_point.inner.accumulated_ticks & inaccuracy_mask;
|
||||
}
|
||||
|
||||
void NativeClock::Pause(bool is_paused) {
|
||||
if (!is_paused) {
|
||||
_mm_mfence();
|
||||
last_measure = __rdtsc();
|
||||
TimePoint current_time_point{};
|
||||
TimePoint new_time_point{};
|
||||
do {
|
||||
current_time_point.pack = time_point.pack;
|
||||
new_time_point.pack = current_time_point.pack;
|
||||
_mm_mfence();
|
||||
new_time_point.inner.last_measure = __rdtsc();
|
||||
} while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
|
||||
current_time_point.pack));
|
||||
}
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds NativeClock::GetTimeNS() {
|
||||
const u64 rtsc_value = GetRTSC();
|
||||
return std::chrono::nanoseconds{MultiplyAndDivide64(rtsc_value, 1000000000, rtsc_frequency)};
|
||||
return std::chrono::nanoseconds{MultiplyHigh(rtsc_value, ns_rtsc_factor)};
|
||||
}
|
||||
|
||||
std::chrono::microseconds NativeClock::GetTimeUS() {
|
||||
const u64 rtsc_value = GetRTSC();
|
||||
return std::chrono::microseconds{MultiplyAndDivide64(rtsc_value, 1000000, rtsc_frequency)};
|
||||
return std::chrono::microseconds{MultiplyHigh(rtsc_value, us_rtsc_factor)};
|
||||
}
|
||||
|
||||
std::chrono::milliseconds NativeClock::GetTimeMS() {
|
||||
const u64 rtsc_value = GetRTSC();
|
||||
return std::chrono::milliseconds{MultiplyAndDivide64(rtsc_value, 1000, rtsc_frequency)};
|
||||
return std::chrono::milliseconds{MultiplyHigh(rtsc_value, ms_rtsc_factor)};
|
||||
}
|
||||
|
||||
u64 NativeClock::GetClockCycles() {
|
||||
const u64 rtsc_value = GetRTSC();
|
||||
return MultiplyAndDivide64(rtsc_value, emulated_clock_frequency, rtsc_frequency);
|
||||
return MultiplyHigh(rtsc_value, clock_rtsc_factor);
|
||||
}
|
||||
|
||||
u64 NativeClock::GetCPUCycles() {
|
||||
const u64 rtsc_value = GetRTSC();
|
||||
return MultiplyAndDivide64(rtsc_value, emulated_cpu_frequency, rtsc_frequency);
|
||||
return MultiplyHigh(rtsc_value, cpu_rtsc_factor);
|
||||
}
|
||||
|
||||
} // namespace X64
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "common/spin_lock.h"
|
||||
#include "common/wall_clock.h"
|
||||
|
||||
namespace Common {
|
||||
@@ -32,14 +31,28 @@ public:
|
||||
private:
|
||||
u64 GetRTSC();
|
||||
|
||||
union alignas(16) TimePoint {
|
||||
TimePoint() : pack{} {}
|
||||
u128 pack{};
|
||||
struct Inner {
|
||||
u64 last_measure{};
|
||||
u64 accumulated_ticks{};
|
||||
} inner;
|
||||
};
|
||||
|
||||
/// value used to reduce the native clocks accuracy as some apss rely on
|
||||
/// undefined behavior where the level of accuracy in the clock shouldn't
|
||||
/// be higher.
|
||||
static constexpr u64 inaccuracy_mask = ~(UINT64_C(0x400) - 1);
|
||||
|
||||
SpinLock rtsc_serialize{};
|
||||
u64 last_measure{};
|
||||
u64 accumulated_ticks{};
|
||||
TimePoint time_point;
|
||||
// factors
|
||||
u64 clock_rtsc_factor{};
|
||||
u64 cpu_rtsc_factor{};
|
||||
u64 ns_rtsc_factor{};
|
||||
u64 us_rtsc_factor{};
|
||||
u64 ms_rtsc_factor{};
|
||||
|
||||
u64 rtsc_frequency;
|
||||
};
|
||||
} // namespace X64
|
||||
|
||||
@@ -19,7 +19,6 @@ add_library(core STATIC
|
||||
core.h
|
||||
core_timing.cpp
|
||||
core_timing.h
|
||||
core_timing_util.cpp
|
||||
core_timing_util.h
|
||||
cpu_manager.cpp
|
||||
cpu_manager.h
|
||||
@@ -142,49 +141,71 @@ add_library(core STATIC
|
||||
hardware_interrupt_manager.h
|
||||
hle/ipc.h
|
||||
hle/ipc_helpers.h
|
||||
hle/kernel/address_arbiter.cpp
|
||||
hle/kernel/address_arbiter.h
|
||||
hle/kernel/client_port.cpp
|
||||
hle/kernel/client_port.h
|
||||
hle/kernel/client_session.cpp
|
||||
hle/kernel/client_session.h
|
||||
hle/kernel/code_set.cpp
|
||||
hle/kernel/code_set.h
|
||||
hle/kernel/errors.h
|
||||
hle/kernel/svc_results.h
|
||||
hle/kernel/global_scheduler_context.cpp
|
||||
hle/kernel/global_scheduler_context.h
|
||||
hle/kernel/handle_table.cpp
|
||||
hle/kernel/handle_table.h
|
||||
hle/kernel/hle_ipc.cpp
|
||||
hle/kernel/hle_ipc.h
|
||||
hle/kernel/k_address_arbiter.cpp
|
||||
hle/kernel/k_address_arbiter.h
|
||||
hle/kernel/k_address_space_info.cpp
|
||||
hle/kernel/k_address_space_info.h
|
||||
hle/kernel/k_affinity_mask.h
|
||||
hle/kernel/k_condition_variable.cpp
|
||||
hle/kernel/k_condition_variable.h
|
||||
hle/kernel/k_event.cpp
|
||||
hle/kernel/k_event.h
|
||||
hle/kernel/k_light_condition_variable.h
|
||||
hle/kernel/k_light_lock.cpp
|
||||
hle/kernel/k_light_lock.h
|
||||
hle/kernel/k_memory_block.h
|
||||
hle/kernel/k_memory_block_manager.cpp
|
||||
hle/kernel/k_memory_block_manager.h
|
||||
hle/kernel/k_memory_layout.h
|
||||
hle/kernel/k_memory_manager.cpp
|
||||
hle/kernel/k_memory_manager.h
|
||||
hle/kernel/k_page_bitmap.h
|
||||
hle/kernel/k_page_heap.cpp
|
||||
hle/kernel/k_page_heap.h
|
||||
hle/kernel/k_page_linked_list.h
|
||||
hle/kernel/k_page_table.cpp
|
||||
hle/kernel/k_page_table.h
|
||||
hle/kernel/k_priority_queue.h
|
||||
hle/kernel/k_readable_event.cpp
|
||||
hle/kernel/k_readable_event.h
|
||||
hle/kernel/k_resource_limit.cpp
|
||||
hle/kernel/k_resource_limit.h
|
||||
hle/kernel/k_scheduler.cpp
|
||||
hle/kernel/k_scheduler.h
|
||||
hle/kernel/k_scheduler_lock.h
|
||||
hle/kernel/k_scoped_lock.h
|
||||
hle/kernel/k_scoped_resource_reservation.h
|
||||
hle/kernel/k_scoped_scheduler_lock_and_sleep.h
|
||||
hle/kernel/k_shared_memory.cpp
|
||||
hle/kernel/k_shared_memory.h
|
||||
hle/kernel/k_slab_heap.h
|
||||
hle/kernel/k_spin_lock.cpp
|
||||
hle/kernel/k_spin_lock.h
|
||||
hle/kernel/k_synchronization_object.cpp
|
||||
hle/kernel/k_synchronization_object.h
|
||||
hle/kernel/k_system_control.cpp
|
||||
hle/kernel/k_system_control.h
|
||||
hle/kernel/k_thread.cpp
|
||||
hle/kernel/k_thread.h
|
||||
hle/kernel/k_thread_queue.h
|
||||
hle/kernel/k_writable_event.cpp
|
||||
hle/kernel/k_writable_event.h
|
||||
hle/kernel/kernel.cpp
|
||||
hle/kernel/kernel.h
|
||||
hle/kernel/memory/address_space_info.cpp
|
||||
hle/kernel/memory/address_space_info.h
|
||||
hle/kernel/memory/memory_block.h
|
||||
hle/kernel/memory/memory_block_manager.cpp
|
||||
hle/kernel/memory/memory_block_manager.h
|
||||
hle/kernel/memory/memory_layout.h
|
||||
hle/kernel/memory/memory_manager.cpp
|
||||
hle/kernel/memory/memory_manager.h
|
||||
hle/kernel/memory/memory_types.h
|
||||
hle/kernel/memory/page_linked_list.h
|
||||
hle/kernel/memory/page_heap.cpp
|
||||
hle/kernel/memory/page_heap.h
|
||||
hle/kernel/memory/page_table.cpp
|
||||
hle/kernel/memory/page_table.h
|
||||
hle/kernel/memory/slab_heap.h
|
||||
hle/kernel/memory/system_control.cpp
|
||||
hle/kernel/memory/system_control.h
|
||||
hle/kernel/mutex.cpp
|
||||
hle/kernel/mutex.h
|
||||
hle/kernel/memory_types.h
|
||||
hle/kernel/object.cpp
|
||||
hle/kernel/object.h
|
||||
hle/kernel/physical_core.cpp
|
||||
@@ -194,10 +215,6 @@ add_library(core STATIC
|
||||
hle/kernel/process.h
|
||||
hle/kernel/process_capability.cpp
|
||||
hle/kernel/process_capability.h
|
||||
hle/kernel/readable_event.cpp
|
||||
hle/kernel/readable_event.h
|
||||
hle/kernel/resource_limit.cpp
|
||||
hle/kernel/resource_limit.h
|
||||
hle/kernel/server_port.cpp
|
||||
hle/kernel/server_port.h
|
||||
hle/kernel/server_session.cpp
|
||||
@@ -206,24 +223,15 @@ add_library(core STATIC
|
||||
hle/kernel/service_thread.h
|
||||
hle/kernel/session.cpp
|
||||
hle/kernel/session.h
|
||||
hle/kernel/shared_memory.cpp
|
||||
hle/kernel/shared_memory.h
|
||||
hle/kernel/svc.cpp
|
||||
hle/kernel/svc.h
|
||||
hle/kernel/svc_common.h
|
||||
hle/kernel/svc_types.h
|
||||
hle/kernel/svc_wrap.h
|
||||
hle/kernel/synchronization_object.cpp
|
||||
hle/kernel/synchronization_object.h
|
||||
hle/kernel/synchronization.cpp
|
||||
hle/kernel/synchronization.h
|
||||
hle/kernel/thread.cpp
|
||||
hle/kernel/thread.h
|
||||
hle/kernel/time_manager.cpp
|
||||
hle/kernel/time_manager.h
|
||||
hle/kernel/transfer_memory.cpp
|
||||
hle/kernel/transfer_memory.h
|
||||
hle/kernel/writable_event.cpp
|
||||
hle/kernel/writable_event.h
|
||||
hle/lock.cpp
|
||||
hle/lock.h
|
||||
hle/result.h
|
||||
@@ -260,6 +268,7 @@ add_library(core STATIC
|
||||
hle/service/am/applets/software_keyboard.h
|
||||
hle/service/am/applets/web_browser.cpp
|
||||
hle/service/am/applets/web_browser.h
|
||||
hle/service/am/applets/web_types.h
|
||||
hle/service/am/idle.cpp
|
||||
hle/service/am/idle.h
|
||||
hle/service/am/omm.cpp
|
||||
@@ -394,14 +403,13 @@ add_library(core STATIC
|
||||
hle/service/hid/controllers/xpad.h
|
||||
hle/service/lbl/lbl.cpp
|
||||
hle/service/lbl/lbl.h
|
||||
hle/service/ldn/errors.h
|
||||
hle/service/ldn/ldn.cpp
|
||||
hle/service/ldn/ldn.h
|
||||
hle/service/ldr/ldr.cpp
|
||||
hle/service/ldr/ldr.h
|
||||
hle/service/lm/lm.cpp
|
||||
hle/service/lm/lm.h
|
||||
hle/service/lm/manager.cpp
|
||||
hle/service/lm/manager.h
|
||||
hle/service/mig/mig.cpp
|
||||
hle/service/mig/mig.h
|
||||
hle/service/mii/manager.cpp
|
||||
@@ -635,19 +643,22 @@ if (MSVC)
|
||||
/we4267
|
||||
# 'context' : truncation from 'type1' to 'type2'
|
||||
/we4305
|
||||
# 'function' : not all control paths return a value
|
||||
/we4715
|
||||
)
|
||||
else()
|
||||
target_compile_options(core PRIVATE
|
||||
-Werror=conversion
|
||||
-Werror=ignored-qualifiers
|
||||
-Werror=implicit-fallthrough
|
||||
-Werror=reorder
|
||||
-Werror=sign-compare
|
||||
-Werror=unused-variable
|
||||
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess>
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
|
||||
|
||||
$<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>
|
||||
|
||||
-Wno-sign-conversion
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -26,9 +26,10 @@ using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CO
|
||||
/// Generic ARMv8 CPU interface
|
||||
class ARM_Interface : NonCopyable {
|
||||
public:
|
||||
explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers, bool uses_wall_clock)
|
||||
: system{system_}, interrupt_handlers{interrupt_handlers}, uses_wall_clock{
|
||||
uses_wall_clock} {}
|
||||
explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers_,
|
||||
bool uses_wall_clock_)
|
||||
: system{system_}, interrupt_handlers{interrupt_handlers_}, uses_wall_clock{
|
||||
uses_wall_clock_} {}
|
||||
virtual ~ARM_Interface() = default;
|
||||
|
||||
struct ThreadContext32 {
|
||||
|
||||
@@ -71,15 +71,9 @@ public:
|
||||
}
|
||||
|
||||
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
|
||||
switch (exception) {
|
||||
case Dynarmic::A32::Exception::UndefinedInstruction:
|
||||
case Dynarmic::A32::Exception::UnpredictableInstruction:
|
||||
break;
|
||||
case Dynarmic::A32::Exception::Breakpoint:
|
||||
break;
|
||||
}
|
||||
LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
|
||||
static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
|
||||
LOG_CRITICAL(Core_ARM,
|
||||
"ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
|
||||
exception, pc, MemoryReadCode(pc), parent.IsInThumbMode());
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
@@ -181,6 +175,9 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
|
||||
if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
|
||||
}
|
||||
if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_unique<Dynarmic::A32::Jit>(config);
|
||||
@@ -259,6 +256,9 @@ void ARM_Dynarmic_32::ChangeProcessorID(std::size_t new_core_id) {
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
|
||||
if (!jit) {
|
||||
return;
|
||||
}
|
||||
Dynarmic::A32::Context context;
|
||||
jit->SaveContext(context);
|
||||
ctx.cpu_registers = context.Regs();
|
||||
@@ -268,6 +268,9 @@ void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
|
||||
if (!jit) {
|
||||
return;
|
||||
}
|
||||
Dynarmic::A32::Context context;
|
||||
context.Regs() = ctx.cpu_registers;
|
||||
context.ExtRegs() = ctx.extension_registers;
|
||||
|
||||
@@ -50,6 +50,10 @@ public:
|
||||
u64 GetTPIDR_EL0() const override;
|
||||
void ChangeProcessorID(std::size_t new_core_id) override;
|
||||
|
||||
bool IsInThumbMode() const {
|
||||
return (GetPSTATE() & 0x20) != 0;
|
||||
}
|
||||
|
||||
void SaveContext(ThreadContext32& ctx) override;
|
||||
void SaveContext(ThreadContext64& ctx) override {}
|
||||
void LoadContext(const ThreadContext32& ctx) override;
|
||||
|
||||
@@ -212,6 +212,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
|
||||
if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
|
||||
}
|
||||
if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_shared<Dynarmic::A64::Jit>(config);
|
||||
@@ -291,6 +294,9 @@ void ARM_Dynarmic_64::ChangeProcessorID(std::size_t new_core_id) {
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
|
||||
if (!jit) {
|
||||
return;
|
||||
}
|
||||
ctx.cpu_registers = jit->GetRegisters();
|
||||
ctx.sp = jit->GetSP();
|
||||
ctx.pc = jit->GetPC();
|
||||
@@ -302,6 +308,9 @@ void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) {
|
||||
if (!jit) {
|
||||
return;
|
||||
}
|
||||
jit->SetRegisters(ctx.cpu_registers);
|
||||
jit->SetSP(ctx.sp);
|
||||
jit->SetPC(ctx.pc);
|
||||
|
||||
@@ -28,15 +28,14 @@
|
||||
#include "core/hardware_interrupt_manager.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/physical_core.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
#include "core/hle/service/apm/controller.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/glue/manager.h"
|
||||
#include "core/hle/service/lm/manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/hle/service/time/time_manager.h"
|
||||
@@ -293,8 +292,6 @@ struct System::Impl {
|
||||
perf_stats->GetMeanFrametime());
|
||||
}
|
||||
|
||||
lm_manager.Flush();
|
||||
|
||||
is_powered_on = false;
|
||||
exit_lock = false;
|
||||
|
||||
@@ -398,7 +395,6 @@ struct System::Impl {
|
||||
|
||||
/// Service State
|
||||
Service::Glue::ARPManager arp_manager;
|
||||
Service::LM::Manager lm_manager{reporter};
|
||||
Service::Time::TimeManager time_manager;
|
||||
|
||||
/// Service manager
|
||||
@@ -720,14 +716,6 @@ const Service::APM::Controller& System::GetAPMController() const {
|
||||
return impl->apm_controller;
|
||||
}
|
||||
|
||||
Service::LM::Manager& System::GetLogManager() {
|
||||
return impl->lm_manager;
|
||||
}
|
||||
|
||||
const Service::LM::Manager& System::GetLogManager() const {
|
||||
return impl->lm_manager;
|
||||
}
|
||||
|
||||
Service::Time::TimeManager& System::GetTimeManager() {
|
||||
return impl->time_manager;
|
||||
}
|
||||
|
||||
@@ -62,10 +62,6 @@ namespace Glue {
|
||||
class ARPManager;
|
||||
}
|
||||
|
||||
namespace LM {
|
||||
class Manager;
|
||||
} // namespace LM
|
||||
|
||||
namespace SM {
|
||||
class ServiceManager;
|
||||
} // namespace SM
|
||||
@@ -351,9 +347,6 @@ public:
|
||||
[[nodiscard]] Service::APM::Controller& GetAPMController();
|
||||
[[nodiscard]] const Service::APM::Controller& GetAPMController() const;
|
||||
|
||||
[[nodiscard]] Service::LM::Manager& GetLogManager();
|
||||
[[nodiscard]] const Service::LM::Manager& GetLogManager() const;
|
||||
|
||||
[[nodiscard]] Service::Time::TimeManager& GetTimeManager();
|
||||
[[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ void CoreTiming::ThreadEntry(CoreTiming& instance) {
|
||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh);
|
||||
instance.on_thread_init();
|
||||
instance.ThreadLoop();
|
||||
MicroProfileOnThreadExit();
|
||||
}
|
||||
|
||||
void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
// Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/core_timing_util.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <limits>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/uint128.h"
|
||||
#include "core/hardware_properties.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
|
||||
constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / Hardware::BASE_CLOCK_RATE;
|
||||
|
||||
s64 msToCycles(std::chrono::milliseconds ms) {
|
||||
if (static_cast<u64>(ms.count() / 1000) > MAX_VALUE_TO_MULTIPLY) {
|
||||
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
|
||||
return std::numeric_limits<s64>::max();
|
||||
}
|
||||
if (static_cast<u64>(ms.count()) > MAX_VALUE_TO_MULTIPLY) {
|
||||
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
|
||||
return Hardware::BASE_CLOCK_RATE * (ms.count() / 1000);
|
||||
}
|
||||
return (Hardware::BASE_CLOCK_RATE * ms.count()) / 1000;
|
||||
}
|
||||
|
||||
s64 usToCycles(std::chrono::microseconds us) {
|
||||
if (static_cast<u64>(us.count() / 1000000) > MAX_VALUE_TO_MULTIPLY) {
|
||||
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
|
||||
return std::numeric_limits<s64>::max();
|
||||
}
|
||||
if (static_cast<u64>(us.count()) > MAX_VALUE_TO_MULTIPLY) {
|
||||
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
|
||||
return Hardware::BASE_CLOCK_RATE * (us.count() / 1000000);
|
||||
}
|
||||
return (Hardware::BASE_CLOCK_RATE * us.count()) / 1000000;
|
||||
}
|
||||
|
||||
s64 nsToCycles(std::chrono::nanoseconds ns) {
|
||||
const u128 temporal = Common::Multiply64Into128(ns.count(), Hardware::BASE_CLOCK_RATE);
|
||||
return Common::Divide128On32(temporal, static_cast<u32>(1000000000)).first;
|
||||
}
|
||||
|
||||
u64 msToClockCycles(std::chrono::milliseconds ns) {
|
||||
const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ);
|
||||
return Common::Divide128On32(temp, 1000).first;
|
||||
}
|
||||
|
||||
u64 usToClockCycles(std::chrono::microseconds ns) {
|
||||
const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ);
|
||||
return Common::Divide128On32(temp, 1000000).first;
|
||||
}
|
||||
|
||||
u64 nsToClockCycles(std::chrono::nanoseconds ns) {
|
||||
const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ);
|
||||
return Common::Divide128On32(temp, 1000000000).first;
|
||||
}
|
||||
|
||||
u64 CpuCyclesToClockCycles(u64 ticks) {
|
||||
const u128 temporal = Common::Multiply64Into128(ticks, Hardware::CNTFREQ);
|
||||
return Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first;
|
||||
}
|
||||
|
||||
std::chrono::milliseconds CyclesToMs(s64 cycles) {
|
||||
const u128 temporal = Common::Multiply64Into128(cycles, 1000);
|
||||
u64 ms = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first;
|
||||
return std::chrono::milliseconds(ms);
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds CyclesToNs(s64 cycles) {
|
||||
const u128 temporal = Common::Multiply64Into128(cycles, 1000000000);
|
||||
u64 ns = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first;
|
||||
return std::chrono::nanoseconds(ns);
|
||||
}
|
||||
|
||||
std::chrono::microseconds CyclesToUs(s64 cycles) {
|
||||
const u128 temporal = Common::Multiply64Into128(cycles, 1000000);
|
||||
u64 us = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first;
|
||||
return std::chrono::microseconds(us);
|
||||
}
|
||||
|
||||
} // namespace Core::Timing
|
||||
@@ -1,24 +1,59 @@
|
||||
// Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hardware_properties.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
|
||||
s64 msToCycles(std::chrono::milliseconds ms);
|
||||
s64 usToCycles(std::chrono::microseconds us);
|
||||
s64 nsToCycles(std::chrono::nanoseconds ns);
|
||||
u64 msToClockCycles(std::chrono::milliseconds ns);
|
||||
u64 usToClockCycles(std::chrono::microseconds ns);
|
||||
u64 nsToClockCycles(std::chrono::nanoseconds ns);
|
||||
std::chrono::milliseconds CyclesToMs(s64 cycles);
|
||||
std::chrono::nanoseconds CyclesToNs(s64 cycles);
|
||||
std::chrono::microseconds CyclesToUs(s64 cycles);
|
||||
namespace detail {
|
||||
constexpr u64 CNTFREQ_ADJUSTED = Hardware::CNTFREQ / 1000;
|
||||
constexpr u64 BASE_CLOCK_RATE_ADJUSTED = Hardware::BASE_CLOCK_RATE / 1000;
|
||||
} // namespace detail
|
||||
|
||||
u64 CpuCyclesToClockCycles(u64 ticks);
|
||||
[[nodiscard]] constexpr s64 msToCycles(std::chrono::milliseconds ms) {
|
||||
return ms.count() * detail::BASE_CLOCK_RATE_ADJUSTED;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr s64 usToCycles(std::chrono::microseconds us) {
|
||||
return us.count() * detail::BASE_CLOCK_RATE_ADJUSTED / 1000;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr s64 nsToCycles(std::chrono::nanoseconds ns) {
|
||||
return ns.count() * detail::BASE_CLOCK_RATE_ADJUSTED / 1000000;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr u64 msToClockCycles(std::chrono::milliseconds ms) {
|
||||
return static_cast<u64>(ms.count()) * detail::CNTFREQ_ADJUSTED;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr u64 usToClockCycles(std::chrono::microseconds us) {
|
||||
return us.count() * detail::CNTFREQ_ADJUSTED / 1000;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr u64 nsToClockCycles(std::chrono::nanoseconds ns) {
|
||||
return ns.count() * detail::CNTFREQ_ADJUSTED / 1000000;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr u64 CpuCyclesToClockCycles(u64 ticks) {
|
||||
return ticks * detail::CNTFREQ_ADJUSTED / detail::BASE_CLOCK_RATE_ADJUSTED;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::chrono::milliseconds CyclesToMs(s64 cycles) {
|
||||
return std::chrono::milliseconds(cycles / detail::BASE_CLOCK_RATE_ADJUSTED);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::chrono::nanoseconds CyclesToNs(s64 cycles) {
|
||||
return std::chrono::nanoseconds(cycles * 1000000 / detail::BASE_CLOCK_RATE_ADJUSTED);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::chrono::microseconds CyclesToUs(s64 cycles) {
|
||||
return std::chrono::microseconds(cycles * 1000 / detail::BASE_CLOCK_RATE_ADJUSTED);
|
||||
}
|
||||
|
||||
} // namespace Core::Timing
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
#include "core/core_timing.h"
|
||||
#include "core/cpu_manager.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/physical_core.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "video_core/gpu.h"
|
||||
|
||||
namespace Core {
|
||||
@@ -147,7 +147,7 @@ void CpuManager::MultiCoreRunSuspendThread() {
|
||||
while (true) {
|
||||
auto core = kernel.GetCurrentHostThreadID();
|
||||
auto& scheduler = *kernel.CurrentScheduler();
|
||||
Kernel::Thread* current_thread = scheduler.GetCurrentThread();
|
||||
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context);
|
||||
ASSERT(scheduler.ContextSwitchPending());
|
||||
ASSERT(core == kernel.GetCurrentHostThreadID());
|
||||
@@ -208,7 +208,6 @@ void CpuManager::SingleCoreRunGuestThread() {
|
||||
|
||||
void CpuManager::SingleCoreRunGuestLoop() {
|
||||
auto& kernel = system.Kernel();
|
||||
auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
|
||||
while (true) {
|
||||
auto* physical_core = &kernel.CurrentPhysicalCore();
|
||||
system.EnterDynarmicProfile();
|
||||
@@ -217,9 +216,9 @@ void CpuManager::SingleCoreRunGuestLoop() {
|
||||
physical_core = &kernel.CurrentPhysicalCore();
|
||||
}
|
||||
system.ExitDynarmicProfile();
|
||||
thread->SetPhantomMode(true);
|
||||
kernel.SetIsPhantomModeForSingleCore(true);
|
||||
system.CoreTiming().Advance();
|
||||
thread->SetPhantomMode(false);
|
||||
kernel.SetIsPhantomModeForSingleCore(false);
|
||||
physical_core->ArmInterface().ClearExclusiveState();
|
||||
PreemptSingleCore();
|
||||
auto& scheduler = kernel.Scheduler(current_core);
|
||||
@@ -245,7 +244,7 @@ void CpuManager::SingleCoreRunSuspendThread() {
|
||||
while (true) {
|
||||
auto core = kernel.GetCurrentHostThreadID();
|
||||
auto& scheduler = *kernel.CurrentScheduler();
|
||||
Kernel::Thread* current_thread = scheduler.GetCurrentThread();
|
||||
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[0].host_context);
|
||||
ASSERT(scheduler.ContextSwitchPending());
|
||||
ASSERT(core == kernel.GetCurrentHostThreadID());
|
||||
@@ -255,22 +254,23 @@ void CpuManager::SingleCoreRunSuspendThread() {
|
||||
|
||||
void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
|
||||
{
|
||||
auto& scheduler = system.Kernel().Scheduler(current_core);
|
||||
Kernel::Thread* current_thread = scheduler.GetCurrentThread();
|
||||
auto& kernel = system.Kernel();
|
||||
auto& scheduler = kernel.Scheduler(current_core);
|
||||
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
|
||||
if (idle_count >= 4 || from_running_enviroment) {
|
||||
if (!from_running_enviroment) {
|
||||
system.CoreTiming().Idle();
|
||||
idle_count = 0;
|
||||
}
|
||||
current_thread->SetPhantomMode(true);
|
||||
kernel.SetIsPhantomModeForSingleCore(true);
|
||||
system.CoreTiming().Advance();
|
||||
current_thread->SetPhantomMode(false);
|
||||
kernel.SetIsPhantomModeForSingleCore(false);
|
||||
}
|
||||
current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES);
|
||||
system.CoreTiming().ResetTicks();
|
||||
scheduler.Unload(scheduler.GetCurrentThread());
|
||||
|
||||
auto& next_scheduler = system.Kernel().Scheduler(current_core);
|
||||
auto& next_scheduler = kernel.Scheduler(current_core);
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext());
|
||||
}
|
||||
|
||||
@@ -278,8 +278,7 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
|
||||
{
|
||||
auto& scheduler = system.Kernel().Scheduler(current_core);
|
||||
scheduler.Reload(scheduler.GetCurrentThread());
|
||||
auto* currrent_thread2 = scheduler.GetCurrentThread();
|
||||
if (!currrent_thread2->IsIdleThread()) {
|
||||
if (!scheduler.IsIdle()) {
|
||||
idle_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +143,7 @@ u64 GetSignatureTypeDataSize(SignatureType type) {
|
||||
return 0x3C;
|
||||
}
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 GetSignatureTypePaddingSize(SignatureType type) {
|
||||
@@ -157,6 +158,7 @@ u64 GetSignatureTypePaddingSize(SignatureType type) {
|
||||
return 0x40;
|
||||
}
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
SignatureType Ticket::GetSignatureType() const {
|
||||
@@ -169,8 +171,7 @@ SignatureType Ticket::GetSignatureType() const {
|
||||
if (const auto* ticket = std::get_if<ECDSATicket>(&data)) {
|
||||
return ticket->sig_type;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
throw std::bad_variant_access{};
|
||||
}
|
||||
|
||||
TicketData& Ticket::GetData() {
|
||||
@@ -183,8 +184,7 @@ TicketData& Ticket::GetData() {
|
||||
if (auto* ticket = std::get_if<ECDSATicket>(&data)) {
|
||||
return ticket->data;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
throw std::bad_variant_access{};
|
||||
}
|
||||
|
||||
const TicketData& Ticket::GetData() const {
|
||||
@@ -197,8 +197,7 @@ const TicketData& Ticket::GetData() const {
|
||||
if (const auto* ticket = std::get_if<ECDSATicket>(&data)) {
|
||||
return ticket->data;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
throw std::bad_variant_access{};
|
||||
}
|
||||
|
||||
u64 Ticket::GetSize() const {
|
||||
@@ -569,6 +568,11 @@ KeyManager::KeyManager() {
|
||||
// Initialize keys
|
||||
const std::string hactool_keys_dir = Common::FS::GetHactoolConfigurationPath();
|
||||
const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir);
|
||||
|
||||
if (!Common::FS::Exists(yuzu_keys_dir)) {
|
||||
Common::FS::CreateDir(yuzu_keys_dir);
|
||||
}
|
||||
|
||||
if (Settings::values.use_dev_keys) {
|
||||
dev_mode = true;
|
||||
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false);
|
||||
|
||||
@@ -43,17 +43,17 @@ static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size.");
|
||||
struct IVFCHeader {
|
||||
u32_le magic;
|
||||
u32_le magic_number;
|
||||
INSERT_UNION_PADDING_BYTES(8);
|
||||
INSERT_PADDING_BYTES_NOINIT(8);
|
||||
std::array<IVFCLevel, 6> levels;
|
||||
INSERT_UNION_PADDING_BYTES(64);
|
||||
INSERT_PADDING_BYTES_NOINIT(64);
|
||||
};
|
||||
static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size.");
|
||||
|
||||
struct NCASectionHeaderBlock {
|
||||
INSERT_UNION_PADDING_BYTES(3);
|
||||
INSERT_PADDING_BYTES_NOINIT(3);
|
||||
NCASectionFilesystemType filesystem_type;
|
||||
NCASectionCryptoType crypto_type;
|
||||
INSERT_UNION_PADDING_BYTES(3);
|
||||
INSERT_PADDING_BYTES_NOINIT(3);
|
||||
};
|
||||
static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size.");
|
||||
|
||||
@@ -61,7 +61,7 @@ struct NCASectionRaw {
|
||||
NCASectionHeaderBlock header;
|
||||
std::array<u8, 0x138> block_data;
|
||||
std::array<u8, 0x8> section_ctr;
|
||||
INSERT_UNION_PADDING_BYTES(0xB8);
|
||||
INSERT_PADDING_BYTES_NOINIT(0xB8);
|
||||
};
|
||||
static_assert(sizeof(NCASectionRaw) == 0x200, "NCASectionRaw has incorrect size.");
|
||||
|
||||
@@ -69,19 +69,19 @@ struct PFS0Superblock {
|
||||
NCASectionHeaderBlock header_block;
|
||||
std::array<u8, 0x20> hash;
|
||||
u32_le size;
|
||||
INSERT_UNION_PADDING_BYTES(4);
|
||||
INSERT_PADDING_BYTES_NOINIT(4);
|
||||
u64_le hash_table_offset;
|
||||
u64_le hash_table_size;
|
||||
u64_le pfs0_header_offset;
|
||||
u64_le pfs0_size;
|
||||
INSERT_UNION_PADDING_BYTES(0x1B0);
|
||||
INSERT_PADDING_BYTES_NOINIT(0x1B0);
|
||||
};
|
||||
static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size.");
|
||||
|
||||
struct RomFSSuperblock {
|
||||
NCASectionHeaderBlock header_block;
|
||||
IVFCHeader ivfc;
|
||||
INSERT_UNION_PADDING_BYTES(0x118);
|
||||
INSERT_PADDING_BYTES_NOINIT(0x118);
|
||||
};
|
||||
static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size.");
|
||||
|
||||
@@ -89,19 +89,19 @@ struct BKTRHeader {
|
||||
u64_le offset;
|
||||
u64_le size;
|
||||
u32_le magic;
|
||||
INSERT_UNION_PADDING_BYTES(0x4);
|
||||
INSERT_PADDING_BYTES_NOINIT(0x4);
|
||||
u32_le number_entries;
|
||||
INSERT_UNION_PADDING_BYTES(0x4);
|
||||
INSERT_PADDING_BYTES_NOINIT(0x4);
|
||||
};
|
||||
static_assert(sizeof(BKTRHeader) == 0x20, "BKTRHeader has incorrect size.");
|
||||
|
||||
struct BKTRSuperblock {
|
||||
NCASectionHeaderBlock header_block;
|
||||
IVFCHeader ivfc;
|
||||
INSERT_UNION_PADDING_BYTES(0x18);
|
||||
INSERT_PADDING_BYTES_NOINIT(0x18);
|
||||
BKTRHeader relocation;
|
||||
BKTRHeader subsection;
|
||||
INSERT_UNION_PADDING_BYTES(0xC0);
|
||||
INSERT_PADDING_BYTES_NOINIT(0xC0);
|
||||
};
|
||||
static_assert(sizeof(BKTRSuperblock) == 0x200, "BKTRSuperblock has incorrect size.");
|
||||
|
||||
|
||||
@@ -51,8 +51,8 @@ std::pair<std::size_t, std::size_t> SearchBucketEntry(u64 offset, const BlockTyp
|
||||
low = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
UNREACHABLE_MSG("Offset could not be found in BKTR block.");
|
||||
return {0, 0};
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
|
||||
@@ -105,7 +105,8 @@ ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
|
||||
// TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal.
|
||||
return ContentRecordType::HtmlDocument;
|
||||
default:
|
||||
UNREACHABLE_MSG("Invalid NCAContentType={:02X}", static_cast<u8>(type));
|
||||
UNREACHABLE_MSG("Invalid NCAContentType={:02X}", type);
|
||||
return ContentRecordType{};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,18 +67,18 @@ public:
|
||||
virtual void Refresh() = 0;
|
||||
|
||||
virtual bool HasEntry(u64 title_id, ContentRecordType type) const = 0;
|
||||
virtual bool HasEntry(ContentProviderEntry entry) const;
|
||||
bool HasEntry(ContentProviderEntry entry) const;
|
||||
|
||||
virtual std::optional<u32> GetEntryVersion(u64 title_id) const = 0;
|
||||
|
||||
virtual VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const = 0;
|
||||
virtual VirtualFile GetEntryUnparsed(ContentProviderEntry entry) const;
|
||||
VirtualFile GetEntryUnparsed(ContentProviderEntry entry) const;
|
||||
|
||||
virtual VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const = 0;
|
||||
virtual VirtualFile GetEntryRaw(ContentProviderEntry entry) const;
|
||||
VirtualFile GetEntryRaw(ContentProviderEntry entry) const;
|
||||
|
||||
virtual std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const = 0;
|
||||
virtual std::unique_ptr<NCA> GetEntry(ContentProviderEntry entry) const;
|
||||
std::unique_ptr<NCA> GetEntry(ContentProviderEntry entry) const;
|
||||
|
||||
virtual std::vector<ContentProviderEntry> ListEntries() const;
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ struct SaveDataAttribute {
|
||||
SaveDataType type;
|
||||
SaveDataRank rank;
|
||||
u16 index;
|
||||
INSERT_PADDING_BYTES(4);
|
||||
INSERT_PADDING_BYTES_NOINIT(4);
|
||||
u64 zero_1;
|
||||
u64 zero_2;
|
||||
u64 zero_3;
|
||||
@@ -72,7 +72,7 @@ struct SaveDataExtraData {
|
||||
u64 owner_id;
|
||||
s64 timestamp;
|
||||
SaveDataFlags flags;
|
||||
INSERT_PADDING_BYTES(4);
|
||||
INSERT_PADDING_BYTES_NOINIT(4);
|
||||
s64 available_size;
|
||||
s64 journal_size;
|
||||
s64 commit_id;
|
||||
|
||||
@@ -133,8 +133,11 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
|
||||
}
|
||||
|
||||
cache.erase(old_path);
|
||||
file->Open(new_path, "r+b");
|
||||
cache.insert_or_assign(new_path, std::move(file));
|
||||
if (file->Open(new_path, "r+b")) {
|
||||
cache.insert_or_assign(new_path, std::move(file));
|
||||
} else {
|
||||
LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", new_path);
|
||||
}
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
@@ -214,9 +217,12 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
|
||||
}
|
||||
|
||||
auto file = cached.lock();
|
||||
file->Open(file_new_path, "r+b");
|
||||
cache.erase(file_old_path);
|
||||
cache.insert_or_assign(std::move(file_new_path), std::move(file));
|
||||
if (file->Open(file_new_path, "r+b")) {
|
||||
cache.insert_or_assign(std::move(file_new_path), std::move(file));
|
||||
} else {
|
||||
LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", file_new_path);
|
||||
}
|
||||
}
|
||||
|
||||
return OpenDirectory(new_path, Mode::ReadWrite);
|
||||
|
||||
@@ -31,6 +31,7 @@ struct ControllerParameters {
|
||||
bool allow_dual_joycons{};
|
||||
bool allow_left_joycon{};
|
||||
bool allow_right_joycon{};
|
||||
bool allow_gamecube_controller{};
|
||||
};
|
||||
|
||||
class ControllerApplet {
|
||||
|
||||
@@ -21,21 +21,18 @@ public:
|
||||
|
||||
std::mutex mutex;
|
||||
|
||||
bool touch_pressed = false; ///< True if touchpad area is currently pressed, otherwise false
|
||||
|
||||
float touch_x = 0.0f; ///< Touchpad X-position
|
||||
float touch_y = 0.0f; ///< Touchpad Y-position
|
||||
Input::TouchStatus status;
|
||||
|
||||
private:
|
||||
class Device : public Input::TouchDevice {
|
||||
public:
|
||||
explicit Device(std::weak_ptr<TouchState>&& touch_state) : touch_state(touch_state) {}
|
||||
std::tuple<float, float, bool> GetStatus() const override {
|
||||
Input::TouchStatus GetStatus() const override {
|
||||
if (auto state = touch_state.lock()) {
|
||||
std::lock_guard guard{state->mutex};
|
||||
return std::make_tuple(state->touch_x, state->touch_y, state->touch_pressed);
|
||||
return state->status;
|
||||
}
|
||||
return std::make_tuple(0.0f, 0.0f, false);
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -79,36 +76,44 @@ std::tuple<unsigned, unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsi
|
||||
return std::make_tuple(new_x, new_y);
|
||||
}
|
||||
|
||||
void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
|
||||
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
|
||||
void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id) {
|
||||
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) {
|
||||
return;
|
||||
}
|
||||
if (id >= touch_state->status.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard guard{touch_state->mutex};
|
||||
touch_state->touch_x =
|
||||
const float x =
|
||||
static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) /
|
||||
static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left);
|
||||
touch_state->touch_y =
|
||||
const float y =
|
||||
static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) /
|
||||
static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top);
|
||||
|
||||
touch_state->touch_pressed = true;
|
||||
touch_state->status[id] = std::make_tuple(x, y, true);
|
||||
}
|
||||
|
||||
void EmuWindow::TouchReleased() {
|
||||
void EmuWindow::TouchReleased(std::size_t id) {
|
||||
if (id >= touch_state->status.size()) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard guard{touch_state->mutex};
|
||||
touch_state->touch_pressed = false;
|
||||
touch_state->touch_x = 0;
|
||||
touch_state->touch_y = 0;
|
||||
touch_state->status[id] = std::make_tuple(0.0f, 0.0f, false);
|
||||
}
|
||||
|
||||
void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
|
||||
if (!touch_state->touch_pressed)
|
||||
void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id) {
|
||||
if (id >= touch_state->status.size()) {
|
||||
return;
|
||||
}
|
||||
if (!std::get<2>(touch_state->status[id]))
|
||||
return;
|
||||
|
||||
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
|
||||
std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y);
|
||||
|
||||
TouchPressed(framebuffer_x, framebuffer_y);
|
||||
TouchPressed(framebuffer_x, framebuffer_y, id);
|
||||
}
|
||||
|
||||
void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) {
|
||||
|
||||
@@ -117,18 +117,23 @@ public:
|
||||
* Signal that a touch pressed event has occurred (e.g. mouse click pressed)
|
||||
* @param framebuffer_x Framebuffer x-coordinate that was pressed
|
||||
* @param framebuffer_y Framebuffer y-coordinate that was pressed
|
||||
* @param id Touch event ID
|
||||
*/
|
||||
void TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y);
|
||||
void TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id);
|
||||
|
||||
/// Signal that a touch released event has occurred (e.g. mouse click released)
|
||||
void TouchReleased();
|
||||
/**
|
||||
* Signal that a touch released event has occurred (e.g. mouse click released)
|
||||
* @param id Touch event ID
|
||||
*/
|
||||
void TouchReleased(std::size_t id);
|
||||
|
||||
/**
|
||||
* Signal that a touch movement event has occurred (e.g. mouse was moved over the emu window)
|
||||
* @param framebuffer_x Framebuffer x-coordinate
|
||||
* @param framebuffer_y Framebuffer y-coordinate
|
||||
* @param id Touch event ID
|
||||
*/
|
||||
void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
|
||||
void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id);
|
||||
|
||||
/**
|
||||
* Returns currently active configuration.
|
||||
|
||||
@@ -21,6 +21,11 @@ enum class AnalogDirection : u8 {
|
||||
UP,
|
||||
DOWN,
|
||||
};
|
||||
struct AnalogProperties {
|
||||
float deadzone;
|
||||
float range;
|
||||
float threshold;
|
||||
};
|
||||
|
||||
/// An abstract class template for an input device (a button, an analog input, etc.).
|
||||
template <typename StatusType>
|
||||
@@ -30,6 +35,12 @@ public:
|
||||
virtual StatusType GetStatus() const {
|
||||
return {};
|
||||
}
|
||||
virtual StatusType GetRawStatus() const {
|
||||
return GetStatus();
|
||||
}
|
||||
virtual AnalogProperties GetAnalogProperties() const {
|
||||
return {};
|
||||
}
|
||||
virtual bool GetAnalogDirectionStatus([[maybe_unused]] AnalogDirection direction) const {
|
||||
return {};
|
||||
}
|
||||
@@ -163,10 +174,11 @@ using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common
|
||||
using MotionDevice = InputDevice<MotionStatus>;
|
||||
|
||||
/**
|
||||
* A touch status is an object that returns a tuple of two floats and a bool. The floats are
|
||||
* x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is pressed.
|
||||
* A touch status is an object that returns an array of 16 tuple elements of two floats and a bool.
|
||||
* The floats are x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is
|
||||
* pressed.
|
||||
*/
|
||||
using TouchStatus = std::tuple<float, float, bool>;
|
||||
using TouchStatus = std::array<std::tuple<float, float, bool>, 16>;
|
||||
|
||||
/**
|
||||
* A touch device is an input device that returns a touch status object
|
||||
|
||||