Compare commits
946 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f510f95b71 | ||
|
|
128bdd127b | ||
|
|
2da91ec75b | ||
|
|
b9eee1c539 | ||
|
|
23ca1eb82e | ||
|
|
fa22695705 | ||
|
|
859deda3bb | ||
|
|
b023413c98 | ||
|
|
00358e2098 | ||
|
|
48b6d41f1b | ||
|
|
63325cafbe | ||
|
|
bd0e1d3a25 | ||
|
|
bb29dcb7f2 | ||
|
|
456adb95ff | ||
|
|
bd1a764827 | ||
|
|
928b64d2ce | ||
|
|
268b5764c7 | ||
|
|
f183668a87 | ||
|
|
156ea746a3 | ||
|
|
e3688f0627 | ||
|
|
a3f80a97a3 | ||
|
|
cc8ac112fc | ||
|
|
42d8e08f78 | ||
|
|
d20c5ac720 | ||
|
|
8ba551e1cd | ||
|
|
e1a92db519 | ||
|
|
7e846be376 | ||
|
|
346149dcf9 | ||
|
|
a1cb453470 | ||
|
|
9a7d2e3659 | ||
|
|
f10dc35dd0 | ||
|
|
7b39215c8a | ||
|
|
edb9c72e26 | ||
|
|
be16d92060 | ||
|
|
d5bf597436 | ||
|
|
1fb158ce90 | ||
|
|
705f111058 | ||
|
|
2868d4ba84 | ||
|
|
d16a337d98 | ||
|
|
6ab0c6a808 | ||
|
|
ffe553edbf | ||
|
|
e71f78d04c | ||
|
|
f56d0db5bd | ||
|
|
3e26141483 | ||
|
|
381aacdbb1 | ||
|
|
d20bcb7faf | ||
|
|
3b4d427993 | ||
|
|
30f0b7cf31 | ||
|
|
db32c3762b | ||
|
|
a086ee6a00 | ||
|
|
c439fc9be9 | ||
|
|
5ab8053511 | ||
|
|
b2862e4772 | ||
|
|
a75d70fa90 | ||
|
|
5665d05547 | ||
|
|
15c0c213b1 | ||
|
|
7cf0958b06 | ||
|
|
9143fe5d3a | ||
|
|
47f13a9df4 | ||
|
|
2c7fdee7a7 | ||
|
|
7530594602 | ||
|
|
0334b9b776 | ||
|
|
335de3fdf5 | ||
|
|
ba3d230421 | ||
|
|
275db94bb8 | ||
|
|
6ca8ed9e58 | ||
|
|
21ff0a3d6e | ||
|
|
db07ca6c7f | ||
|
|
a98f14e9b0 | ||
|
|
8c9febe8f7 | ||
|
|
23b3333f72 | ||
|
|
5acf020389 | ||
|
|
b185567a03 | ||
|
|
7ac99bb127 | ||
|
|
75e7f54fb0 | ||
|
|
548bac8989 | ||
|
|
1e6c5d323d | ||
|
|
124e3b4819 | ||
|
|
f771d92e44 | ||
|
|
d05e6003f0 | ||
|
|
5593a3716e | ||
|
|
d923ec5805 | ||
|
|
1bb46b7d64 | ||
|
|
3b006f4fe2 | ||
|
|
92887a65f0 | ||
|
|
6053f2e1fe | ||
|
|
353be2306c | ||
|
|
c4cd82fa7c | ||
|
|
ab206d6378 | ||
|
|
5da97c57cd | ||
|
|
2717e79c74 | ||
|
|
e2c42ec5e2 | ||
|
|
ef29ed75b0 | ||
|
|
3109d1c3db | ||
|
|
a24224e274 | ||
|
|
a6359fe9ae | ||
|
|
d7cd316a6c | ||
|
|
00e100de08 | ||
|
|
f8964dd89a | ||
|
|
8c82c594f0 | ||
|
|
ec56a17acd | ||
|
|
075a744e38 | ||
|
|
296728ec46 | ||
|
|
c27ddb44de | ||
|
|
e490ddf327 | ||
|
|
90f3678ada | ||
|
|
f5f04cce01 | ||
|
|
b9b48aee7d | ||
|
|
d6c799494c | ||
|
|
7511f65e31 | ||
|
|
1a94b3f452 | ||
|
|
75050c788c | ||
|
|
c6991fa900 | ||
|
|
6c432d5349 | ||
|
|
5f9a4817a5 | ||
|
|
c0f99558fb | ||
|
|
de0b89792c | ||
|
|
3d97f1e6cf | ||
|
|
7490117fa4 | ||
|
|
c05bbf375d | ||
|
|
b2b3fcdccd | ||
|
|
4afc2de129 | ||
|
|
771dcb2a56 | ||
|
|
27ed6e7c2b | ||
|
|
3e7813e49d | ||
|
|
c2915d9f2f | ||
|
|
06ca911621 | ||
|
|
0b67df1f7c | ||
|
|
89ad9df0e9 | ||
|
|
66a0cedba3 | ||
|
|
09fb41dc63 | ||
|
|
f6f0383b49 | ||
|
|
c09557acd8 | ||
|
|
09d6cc9943 | ||
|
|
7e272d3cd8 | ||
|
|
b5c3cb8763 | ||
|
|
98b26b6e12 | ||
|
|
9dd35b7b66 | ||
|
|
c2aaf51370 | ||
|
|
e7c30f33fe | ||
|
|
e6a0ca5f2c | ||
|
|
84b9c42642 | ||
|
|
c80ae87b4e | ||
|
|
9dfbc9bdce | ||
|
|
2c6e274b39 | ||
|
|
2656020608 | ||
|
|
d8b00fd863 | ||
|
|
b11c81cc13 | ||
|
|
eb61824ea5 | ||
|
|
7f13104c17 | ||
|
|
a55ff22900 | ||
|
|
f6796cad9c | ||
|
|
594ea29015 | ||
|
|
a741513e65 | ||
|
|
3c6d440015 | ||
|
|
8381490a04 | ||
|
|
258f35515d | ||
|
|
4a82450c81 | ||
|
|
56478bc9ac | ||
|
|
c9528282d9 | ||
|
|
56c30dd9e0 | ||
|
|
79d2684261 | ||
|
|
e1ed218b41 | ||
|
|
fc7bed21b5 | ||
|
|
bf2956d77a | ||
|
|
94af0a00f6 | ||
|
|
8c166c68d4 | ||
|
|
49946cf780 | ||
|
|
4e4b8775b5 | ||
|
|
41493fbe89 | ||
|
|
2235a51b5d | ||
|
|
8390286a89 | ||
|
|
be54aad1c4 | ||
|
|
18fb9bdfa8 | ||
|
|
41c6cb70f9 | ||
|
|
11f04f1022 | ||
|
|
55233c2861 | ||
|
|
7277d7fe96 | ||
|
|
57f222c56e | ||
|
|
dbee32d302 | ||
|
|
57171b23f9 | ||
|
|
8722668b3c | ||
|
|
1b27a2b597 | ||
|
|
2e5af95541 | ||
|
|
b9069c7891 | ||
|
|
b21bf79bd2 | ||
|
|
fba6bd92d4 | ||
|
|
5643a909bc | ||
|
|
65daec8b75 | ||
|
|
8289eb108f | ||
|
|
f94f0be521 | ||
|
|
57a8921e01 | ||
|
|
5b2b0634a1 | ||
|
|
fb9b1787f8 | ||
|
|
1152d66ddd | ||
|
|
00fa09dc45 | ||
|
|
f7352411f0 | ||
|
|
8612b5fec5 | ||
|
|
8a3427a4c8 | ||
|
|
7dafa96ab5 | ||
|
|
4f052a1f39 | ||
|
|
ecd6b4356b | ||
|
|
395bed3a0a | ||
|
|
257d2aab74 | ||
|
|
fb166b5ff4 | ||
|
|
d8d5501459 | ||
|
|
04ef2160f9 | ||
|
|
97e80dda55 | ||
|
|
27ca8a0e13 | ||
|
|
4397053d5c | ||
|
|
bc6e399ae3 | ||
|
|
a7536825df | ||
|
|
808ef97a08 | ||
|
|
3877918e96 | ||
|
|
cbce9ddd4a | ||
|
|
3a2dd1b483 | ||
|
|
9bd0531384 | ||
|
|
f5db8c7440 | ||
|
|
218dedca1f | ||
|
|
1c648f176c | ||
|
|
1d182fc0f5 | ||
|
|
df9b7e18f5 | ||
|
|
cfbc85839d | ||
|
|
838d7e4ca5 | ||
|
|
9e066dcb15 | ||
|
|
fcff19e0fa | ||
|
|
a0365217f5 | ||
|
|
0cd08b3e72 | ||
|
|
59fead3a47 | ||
|
|
b5e78607ad | ||
|
|
ca67077ca8 | ||
|
|
ccbd24fe00 | ||
|
|
1091995f8e | ||
|
|
374eeda1a3 | ||
|
|
892b8aa2ad | ||
|
|
0ffea97e2e | ||
|
|
cbbca26d18 | ||
|
|
376aa94819 | ||
|
|
69f9b97e7e | ||
|
|
12ef06ba8b | ||
|
|
d36f667bc0 | ||
|
|
c5dfa0b630 | ||
|
|
3b339fbbf6 | ||
|
|
6eea88d614 | ||
|
|
74f683787e | ||
|
|
ae4e452759 | ||
|
|
6c6a451d6a | ||
|
|
a0d0704aff | ||
|
|
5e7b2b9661 | ||
|
|
6aa1bf7b6f | ||
|
|
ff3de0fb6b | ||
|
|
413eb6983f | ||
|
|
39c29664f9 | ||
|
|
427a2596a1 | ||
|
|
7c82f20b52 | ||
|
|
84c86e03cd | ||
|
|
e81c73a874 | ||
|
|
7d89a82a48 | ||
|
|
4759db28d0 | ||
|
|
85399e119d | ||
|
|
e7c8f8911f | ||
|
|
d1a68f7997 | ||
|
|
a926695234 | ||
|
|
14bd73db36 | ||
|
|
6650c4799d | ||
|
|
3f31a547e0 | ||
|
|
8bb8bbf4ae | ||
|
|
c542204113 | ||
|
|
2a504b4765 | ||
|
|
970fc39d98 | ||
|
|
fc0db612ab | ||
|
|
fb839061fb | ||
|
|
258106038e | ||
|
|
465903468e | ||
|
|
421847cf1e | ||
|
|
d41aef03c7 | ||
|
|
35e78d558d | ||
|
|
747b8556a4 | ||
|
|
d12f2b8ccf | ||
|
|
0a0b0a73d8 | ||
|
|
34fdb6471d | ||
|
|
5355568a2d | ||
|
|
a68fabf6d5 | ||
|
|
8d8ce24f20 | ||
|
|
af9696059c | ||
|
|
6577a63d36 | ||
|
|
f4799e8fa1 | ||
|
|
31147ffe69 | ||
|
|
9f3970f837 | ||
|
|
fc29de7d5b | ||
|
|
59576b82a8 | ||
|
|
8c684b3e23 | ||
|
|
c7d085b505 | ||
|
|
68d075d1e8 | ||
|
|
19247ba4fa | ||
|
|
df53046d68 | ||
|
|
3a024b3026 | ||
|
|
b7561226ed | ||
|
|
e10366974e | ||
|
|
14bfb4719a | ||
|
|
4b5a4ea72e | ||
|
|
8ec0028e68 | ||
|
|
9f3ffb996b | ||
|
|
1269a0cf8b | ||
|
|
9ccbd74991 | ||
|
|
68ef3803bf | ||
|
|
e35ffbbeb0 | ||
|
|
770b754afd | ||
|
|
181a4ffdc4 | ||
|
|
57d354b02c | ||
|
|
7df0815117 | ||
|
|
80eec85867 | ||
|
|
1542f31e79 | ||
|
|
005eecffcd | ||
|
|
3047eb6688 | ||
|
|
5fd92780b2 | ||
|
|
697eacd095 | ||
|
|
e4ba755705 | ||
|
|
59a692e9ed | ||
|
|
c9a25855bc | ||
|
|
7619b7d427 | ||
|
|
55e0211a5e | ||
|
|
b98de76ea8 | ||
|
|
8ba814efb2 | ||
|
|
86d4a05cec | ||
|
|
21797efa54 | ||
|
|
453cd25da5 | ||
|
|
f6bbc76336 | ||
|
|
2a71333716 | ||
|
|
6674637853 | ||
|
|
a752ec88d0 | ||
|
|
ed14d31f66 | ||
|
|
d171083d53 | ||
|
|
3d086e6130 | ||
|
|
5399906c26 | ||
|
|
b95716e543 | ||
|
|
67f881e714 | ||
|
|
bd24fa9713 | ||
|
|
3482df1176 | ||
|
|
9cc1b8a873 | ||
|
|
11ba190462 | ||
|
|
e99d01ff53 | ||
|
|
3d9ecbe998 | ||
|
|
df793fc049 | ||
|
|
cdde730219 | ||
|
|
ac7b0ebcb7 | ||
|
|
3064bde415 | ||
|
|
65c6f73e43 | ||
|
|
5e9095ef22 | ||
|
|
53667ddd4e | ||
|
|
ef7bd53f18 | ||
|
|
266a3d60e3 | ||
|
|
0f40b0e61c | ||
|
|
fb75d122a2 | ||
|
|
115c162b9a | ||
|
|
78f5eb90d7 | ||
|
|
e221baccdd | ||
|
|
faf4cd72c5 | ||
|
|
64337f004d | ||
|
|
eaff1030de | ||
|
|
3d822faea1 | ||
|
|
7ac55c2a75 | ||
|
|
8fb2048934 | ||
|
|
dbf7cb9f90 | ||
|
|
94e751f415 | ||
|
|
61cd7dd301 | ||
|
|
373f75d944 | ||
|
|
487057b8d2 | ||
|
|
ba3bdf1d41 | ||
|
|
41cca8b8ad | ||
|
|
5445799260 | ||
|
|
3c125d4134 | ||
|
|
ea038d6653 | ||
|
|
cb78a1b494 | ||
|
|
3025b2f605 | ||
|
|
d554778311 | ||
|
|
d52bacf6f0 | ||
|
|
8554a644df | ||
|
|
cd8427367e | ||
|
|
5befc0bf87 | ||
|
|
60a96c49e5 | ||
|
|
15bdd27cac | ||
|
|
7eaa74ad23 | ||
|
|
b1ed64ac18 | ||
|
|
12fe7210d2 | ||
|
|
cffd4716c5 | ||
|
|
48aad8dc05 | ||
|
|
2a0aeaa3d2 | ||
|
|
c736b9ffab | ||
|
|
f45f7b5c2a | ||
|
|
562af30181 | ||
|
|
ec9a78885e | ||
|
|
b02c78b276 | ||
|
|
8f099af6a8 | ||
|
|
8c954fcaee | ||
|
|
4f8b68fb04 | ||
|
|
79f2fe1a39 | ||
|
|
4a2361a1e2 | ||
|
|
e57ee3b7fd | ||
|
|
46bd362d0d | ||
|
|
d26271b014 | ||
|
|
22f0c4f002 | ||
|
|
5539b13c5a | ||
|
|
cf9f88e5a7 | ||
|
|
ac0f5d2ab6 | ||
|
|
05d41fa9b7 | ||
|
|
5d170de0b5 | ||
|
|
adc43297c5 | ||
|
|
1148a4eac7 | ||
|
|
77372443c3 | ||
|
|
c44b16124f | ||
|
|
916ca74324 | ||
|
|
a7e9756671 | ||
|
|
329dea217d | ||
|
|
99f2c31b64 | ||
|
|
d093522fac | ||
|
|
d738ad4d0b | ||
|
|
eb8464cb3d | ||
|
|
457dda69cc | ||
|
|
627161c38e | ||
|
|
dd39b87b0c | ||
|
|
b659212dbd | ||
|
|
fbf5cdcba0 | ||
|
|
871c9f1ced | ||
|
|
b6c087496b | ||
|
|
56d4a9ebde | ||
|
|
b7764c3a79 | ||
|
|
83db7abae6 | ||
|
|
3b595fe8b2 | ||
|
|
e240a62017 | ||
|
|
8f3043c3cf | ||
|
|
2aa30353b7 | ||
|
|
adb591a757 | ||
|
|
f58f79c85d | ||
|
|
586c785366 | ||
|
|
b9c8814ea9 | ||
|
|
8763cc1ff7 | ||
|
|
a41b2ed391 | ||
|
|
a49532c8eb | ||
|
|
eacf18cce9 | ||
|
|
48aafe0961 | ||
|
|
77ee733c3a | ||
|
|
70c9281fbf | ||
|
|
75fd0079db | ||
|
|
379b305b4b | ||
|
|
ca05a13c62 | ||
|
|
9fbfe7d676 | ||
|
|
4017928213 | ||
|
|
80884e3270 | ||
|
|
c721767bcc | ||
|
|
0794273870 | ||
|
|
7fdf0d7d33 | ||
|
|
e30d4fa976 | ||
|
|
c8414e686f | ||
|
|
3a7ca6a7db | ||
|
|
e565eb361a | ||
|
|
89e341d56a | ||
|
|
77d8c44b68 | ||
|
|
ddf601919f | ||
|
|
1bccb43cbe | ||
|
|
c31521512f | ||
|
|
df406246d9 | ||
|
|
84feabac88 | ||
|
|
6bc54e12a0 | ||
|
|
69b910e9e7 | ||
|
|
7dadb2bef3 | ||
|
|
b382f57b28 | ||
|
|
c07cc9d6a5 | ||
|
|
9e7b6622c2 | ||
|
|
eb15667905 | ||
|
|
781a87175c | ||
|
|
36d040da70 | ||
|
|
3da7b98d37 | ||
|
|
394b96a2fe | ||
|
|
a5d978e91e | ||
|
|
0d7d85c81e | ||
|
|
48d4e26326 | ||
|
|
9ec2303ad6 | ||
|
|
2913ca811e | ||
|
|
54decced92 | ||
|
|
c0e4074721 | ||
|
|
a569ac418e | ||
|
|
164b8c1ec5 | ||
|
|
d5db96386d | ||
|
|
679e7146a7 | ||
|
|
79929be833 | ||
|
|
83cef0426b | ||
|
|
fad139a3e6 | ||
|
|
690b1841e6 | ||
|
|
c5ca4fe451 | ||
|
|
0a54291c9c | ||
|
|
8fdb00a2b5 | ||
|
|
dadd192b30 | ||
|
|
3d0ffc6ad0 | ||
|
|
f79cbbf814 | ||
|
|
291f220be3 | ||
|
|
d957b3a8fe | ||
|
|
b60b3fa113 | ||
|
|
96962c1d3c | ||
|
|
91a3c2c1c0 | ||
|
|
accad56ee7 | ||
|
|
2494dbe183 | ||
|
|
9415c435fc | ||
|
|
12dcb9fcc2 | ||
|
|
4a22942f45 | ||
|
|
3777592ada | ||
|
|
98ed8ff103 | ||
|
|
2e0d56da7e | ||
|
|
85fc7e584e | ||
|
|
e8ed904805 | ||
|
|
0a42277a4f | ||
|
|
c560bf99c2 | ||
|
|
8b7d5912d6 | ||
|
|
3d3ed53511 | ||
|
|
0fa421f82f | ||
|
|
1ee7f8b943 | ||
|
|
9bb3e008c9 | ||
|
|
4bad415bca | ||
|
|
afcb140185 | ||
|
|
fb3ba62b3a | ||
|
|
f1b334b9f9 | ||
|
|
ec6fc5fe78 | ||
|
|
c42a6143a5 | ||
|
|
bee9fb0563 | ||
|
|
e6b4d461d2 | ||
|
|
bf2949df10 | ||
|
|
db2f0f4108 | ||
|
|
3c06293e20 | ||
|
|
f7a2340205 | ||
|
|
d4f9c798d6 | ||
|
|
258f2dec1b | ||
|
|
776ab3ea12 | ||
|
|
38e7b8c805 | ||
|
|
464f13fe0b | ||
|
|
9fb2ea08e8 | ||
|
|
1f3446b47e | ||
|
|
31d402ee74 | ||
|
|
3764750339 | ||
|
|
057dee4856 | ||
|
|
ab5dbe7c29 | ||
|
|
bf5e48ffe4 | ||
|
|
0f88fb5d72 | ||
|
|
d4385c34e3 | ||
|
|
568d813eea | ||
|
|
d54d7de40e | ||
|
|
7ff5851608 | ||
|
|
8c81a20ace | ||
|
|
c917290497 | ||
|
|
70fbede213 | ||
|
|
c4fd6b55bc | ||
|
|
decda4a2c7 | ||
|
|
5b18a12df2 | ||
|
|
3b6a632237 | ||
|
|
3f00a2ad3f | ||
|
|
deda89372f | ||
|
|
0839e46736 | ||
|
|
6237300e36 | ||
|
|
8eb72ff0dc | ||
|
|
80813b1d14 | ||
|
|
ad61b47f80 | ||
|
|
7703d65f23 | ||
|
|
43a448d98d | ||
|
|
99352741af | ||
|
|
45ef62d3ba | ||
|
|
b4953e79ee | ||
|
|
6705f56029 | ||
|
|
3e10709091 | ||
|
|
4502595bc2 | ||
|
|
9f851e3832 | ||
|
|
4de65fbff4 | ||
|
|
6358b0d0c1 | ||
|
|
939dab7120 | ||
|
|
01e18581b9 | ||
|
|
1c9307969c | ||
|
|
934d300246 | ||
|
|
68cc445b8e | ||
|
|
941c6dc740 | ||
|
|
3e841f6441 | ||
|
|
2b04b4d27f | ||
|
|
dc02cb92e4 | ||
|
|
2c81ad8311 | ||
|
|
6fd190d1ae | ||
|
|
c1ba685d9c | ||
|
|
36f1586267 | ||
|
|
56c47951c5 | ||
|
|
a515036604 | ||
|
|
b10cf64c48 | ||
|
|
09dc23f971 | ||
|
|
bfa47539f6 | ||
|
|
b725db8709 | ||
|
|
bed090807a | ||
|
|
ee61ec2c39 | ||
|
|
153a77efee | ||
|
|
7ecc6de56a | ||
|
|
d621e96d0d | ||
|
|
850b08a16c | ||
|
|
fde47152d9 | ||
|
|
fd913bceaf | ||
|
|
d2a0f9d7ad | ||
|
|
2b434b74af | ||
|
|
cfd873275d | ||
|
|
bafe9e35a9 | ||
|
|
d2e811db2e | ||
|
|
4ead714910 | ||
|
|
33bebc3412 | ||
|
|
7b03b97118 | ||
|
|
48a17298d7 | ||
|
|
d5d6778ba5 | ||
|
|
a46d91b1ef | ||
|
|
028f0033bd | ||
|
|
c49d56c931 | ||
|
|
b541f5e5e3 | ||
|
|
da936d6ad8 | ||
|
|
f4b82b8dd7 | ||
|
|
fb14820c86 | ||
|
|
53acdda772 | ||
|
|
c5425b38c1 | ||
|
|
025b20f96a | ||
|
|
ac8835659e | ||
|
|
2f3c3dfc10 | ||
|
|
5ed871398b | ||
|
|
f4ace63957 | ||
|
|
20e86fd615 | ||
|
|
8fda599a31 | ||
|
|
50eb03382e | ||
|
|
0c0ee9d897 | ||
|
|
5b1b06f11e | ||
|
|
57464e3b72 | ||
|
|
d2b54c6e42 | ||
|
|
2dc86372c7 | ||
|
|
7a1f296cda | ||
|
|
155be4a8d3 | ||
|
|
fe25f42403 | ||
|
|
0c7230a606 | ||
|
|
25949b864c | ||
|
|
92a01984e6 | ||
|
|
aece958c2b | ||
|
|
0ace34575c | ||
|
|
21e3382830 | ||
|
|
d10cf55353 | ||
|
|
7a9dc78398 | ||
|
|
427951d6fe | ||
|
|
c8f9772d65 | ||
|
|
75dee55486 | ||
|
|
23182fa59c | ||
|
|
eed6da55b8 | ||
|
|
cc0fcd1b8d | ||
|
|
f66851e376 | ||
|
|
c84bbd9e44 | ||
|
|
050e81500c | ||
|
|
e4d1122082 | ||
|
|
4209828646 | ||
|
|
6944cabb89 | ||
|
|
4bbe530337 | ||
|
|
24cc298660 | ||
|
|
79c2e43fcd | ||
|
|
dd860b684c | ||
|
|
a8d46a5eae | ||
|
|
469f8bb857 | ||
|
|
7018e524f5 | ||
|
|
6325601947 | ||
|
|
5b8afed871 | ||
|
|
2999028976 | ||
|
|
881b33da3b | ||
|
|
21a878237b | ||
|
|
f69d0b91ff | ||
|
|
080857b60e | ||
|
|
04c459fc8d | ||
|
|
f18a6dd1bd | ||
|
|
50f8007172 | ||
|
|
0a0818c025 | ||
|
|
c9e4609d87 | ||
|
|
7cfa403683 | ||
|
|
dbbd4b5496 | ||
|
|
be431f5ed0 | ||
|
|
80940b1706 | ||
|
|
95815a3883 | ||
|
|
f3473c5143 | ||
|
|
e3514bcd6b | ||
|
|
4657cf78fd | ||
|
|
b0f1255c8c | ||
|
|
183855e396 | ||
|
|
34519d3fc6 | ||
|
|
7ae3ea6bee | ||
|
|
416e1b7441 | ||
|
|
d8ec99dada | ||
|
|
ab3831f6cb | ||
|
|
6f4a1c8dcf | ||
|
|
6c512f4bff | ||
|
|
b126987c59 | ||
|
|
a83579b50a | ||
|
|
09165ae189 | ||
|
|
fa75b9b062 | ||
|
|
c070991def | ||
|
|
2597cee85b | ||
|
|
f263760c5a | ||
|
|
a6cef71cc0 | ||
|
|
dd3432d357 | ||
|
|
4b0172f6de | ||
|
|
f712084147 | ||
|
|
2516829e4c | ||
|
|
23b8714732 | ||
|
|
a33014022e | ||
|
|
415c7e46ed | ||
|
|
dfd5341d71 | ||
|
|
2ed80f6b1e | ||
|
|
5c61e860e4 | ||
|
|
c9337a4ae4 | ||
|
|
3db2b3effa | ||
|
|
479ca00071 | ||
|
|
106764a6d5 | ||
|
|
ab543f1821 | ||
|
|
9280cd649a | ||
|
|
1030b612a3 | ||
|
|
e5e79648cf | ||
|
|
2e71e4c5c0 | ||
|
|
d404b871d5 | ||
|
|
1be6705408 | ||
|
|
8cea39b5a6 | ||
|
|
7cb2ab3585 | ||
|
|
094da34456 | ||
|
|
5bfcafa0a2 | ||
|
|
0bb85f6a75 | ||
|
|
5cd3d00167 | ||
|
|
233e39bb7b | ||
|
|
e9a91bc5cc | ||
|
|
56b92bd89c | ||
|
|
ef88552224 | ||
|
|
1f3eb601ac | ||
|
|
dcaf0e9150 | ||
|
|
73cb17f41b | ||
|
|
1d51803169 | ||
|
|
0df7e509db | ||
|
|
20ba0ea0a9 | ||
|
|
bfeeb23ddc | ||
|
|
417fb5d385 | ||
|
|
72daa2a039 | ||
|
|
9e6fe430bd | ||
|
|
ffca21487f | ||
|
|
da6cf2632c | ||
|
|
fc93bc2abd | ||
|
|
85795de99f | ||
|
|
3f594dd86b | ||
|
|
5b3c6d59c2 | ||
|
|
5ed68e83db | ||
|
|
0b26f2b90e | ||
|
|
6ff2e9ba09 | ||
|
|
9a342f5605 | ||
|
|
c4aab5c40e | ||
|
|
ca7ebdc471 | ||
|
|
e7700aad18 | ||
|
|
ed6a1b1a3d | ||
|
|
80df541a08 | ||
|
|
480dc0d5e6 | ||
|
|
baec84247f | ||
|
|
45d547af11 | ||
|
|
595806fb1c | ||
|
|
655f7a570a | ||
|
|
ecb30c9072 | ||
|
|
12f5f32098 | ||
|
|
d819ba4489 | ||
|
|
fd496d0401 | ||
|
|
5ed8f24384 | ||
|
|
bee8188799 | ||
|
|
c3bace756f | ||
|
|
b4a5e767d0 | ||
|
|
d0a529683a | ||
|
|
8771639d1e | ||
|
|
2fc698b040 | ||
|
|
f1dd743731 | ||
|
|
5f22cd89e2 | ||
|
|
eaafd53cfe | ||
|
|
c826220733 | ||
|
|
0c933e20de | ||
|
|
09e1927b70 | ||
|
|
0c4cf3b9eb | ||
|
|
67afdaf566 | ||
|
|
4d0d29fc20 | ||
|
|
cb6fc03e55 | ||
|
|
630273b629 | ||
|
|
d5bfc63088 | ||
|
|
be3e94ae55 | ||
|
|
613b48c4a2 | ||
|
|
2c276ec6eb | ||
|
|
dc1a9a3bed | ||
|
|
7a1c14269e | ||
|
|
9d7422d967 | ||
|
|
b7589fe115 | ||
|
|
514a6b07ee | ||
|
|
b0d5572abf | ||
|
|
55b960a20f | ||
|
|
12783f8105 | ||
|
|
6c51f49632 | ||
|
|
34aba9627a | ||
|
|
39a379632e | ||
|
|
73af0d2e0d | ||
|
|
dbc1e5cde7 | ||
|
|
3c758d9b53 | ||
|
|
cd9f75e223 | ||
|
|
e860870dd2 | ||
|
|
84298ce191 | ||
|
|
51475e21ba | ||
|
|
0e1b213fa7 | ||
|
|
dbd882ddeb | ||
|
|
675a82416d | ||
|
|
cb6039ccea | ||
|
|
f0031babeb | ||
|
|
a806b29cb9 | ||
|
|
cdf0cc3869 | ||
|
|
ec005be99d | ||
|
|
17063d16a3 | ||
|
|
d9c5bd9509 | ||
|
|
b5db38f50e | ||
|
|
742d11c2ad | ||
|
|
981eb6f43b | ||
|
|
f5672777c8 | ||
|
|
fda0835300 | ||
|
|
c7c518e280 | ||
|
|
32c5483beb | ||
|
|
49e87ea8ab | ||
|
|
d3dad6b632 | ||
|
|
83a283fa86 | ||
|
|
8cb9443cb9 | ||
|
|
68a9505d8a | ||
|
|
1d2db78398 | ||
|
|
3d07cef009 | ||
|
|
d40faa1db0 | ||
|
|
a8d8fd40f7 | ||
|
|
f8115a6a9e | ||
|
|
c63cf4fa2e | ||
|
|
2be5c7eff4 | ||
|
|
96b7ced6ec | ||
|
|
e4e1cc11b8 | ||
|
|
56be556eee | ||
|
|
a62f04efab | ||
|
|
8b3b9c3371 | ||
|
|
c858b8ba97 | ||
|
|
112b8f00f0 | ||
|
|
a77e764726 | ||
|
|
27fb97377e | ||
|
|
e10d9c1b8e | ||
|
|
9e213fd861 | ||
|
|
ed6cd3c94a | ||
|
|
28dff6a629 | ||
|
|
76c8a962ac | ||
|
|
e802512d8e | ||
|
|
f91859efd2 | ||
|
|
c97d03efb9 | ||
|
|
eeb1efa2d2 | ||
|
|
260743f371 | ||
|
|
72990df7ba | ||
|
|
3b7fd3ad0f | ||
|
|
32b6c63485 | ||
|
|
8dd0acfaeb | ||
|
|
fa2f6e38f4 | ||
|
|
17a82b56d7 | ||
|
|
71f96fa636 | ||
|
|
b9f7bf4472 | ||
|
|
8d470c2e63 | ||
|
|
2d422b2498 | ||
|
|
ba8c1d2eb4 | ||
|
|
3a63fa0477 | ||
|
|
ab46371247 | ||
|
|
7d6ba5b984 | ||
|
|
924f0a9149 | ||
|
|
5465cb1561 | ||
|
|
d1edc16ba8 | ||
|
|
81f72471e8 | ||
|
|
4006929c98 | ||
|
|
980cafdc27 | ||
|
|
382cba94ed | ||
|
|
c2155f04d4 | ||
|
|
ce9b116cfe | ||
|
|
103b9da4f7 | ||
|
|
e038928616 | ||
|
|
bec7d3111d | ||
|
|
bce0b1dcca | ||
|
|
20390c0548 | ||
|
|
08a9e95905 | ||
|
|
34ac9b4d7e | ||
|
|
a8c41c50d3 | ||
|
|
cc55d28949 | ||
|
|
8810c88b7e | ||
|
|
726625cf50 | ||
|
|
3bc857f2f3 | ||
|
|
622d676202 | ||
|
|
7496bbf758 | ||
|
|
e87a502da2 | ||
|
|
9d6a98d950 | ||
|
|
e44752ddc8 | ||
|
|
18a766b362 | ||
|
|
274897dfd5 | ||
|
|
704c6f353f | ||
|
|
e2bc05b17d | ||
|
|
6db69990da | ||
|
|
85cce78583 | ||
|
|
c67d64365a | ||
|
|
58914796c0 | ||
|
|
4b438f94cf | ||
|
|
3633e43377 | ||
|
|
3a59fffaa1 | ||
|
|
b5d7279d87 | ||
|
|
d5d468cf2c | ||
|
|
1c0b8bca5e | ||
|
|
1b0cf2309c | ||
|
|
cbfb7d182a | ||
|
|
8af9297f09 | ||
|
|
9170200a11 | ||
|
|
2930dccecc | ||
|
|
6dafb08f52 | ||
|
|
da8096e6e3 | ||
|
|
16cb00c521 | ||
|
|
be94ee88d2 | ||
|
|
dc04a50ac2 | ||
|
|
e81739493a | ||
|
|
d24a16045f | ||
|
|
6c4cc0cd06 | ||
|
|
2d48a7b4d0 | ||
|
|
75059c46d6 | ||
|
|
db46f8a70c | ||
|
|
233bf018d6 | ||
|
|
dff438e219 | ||
|
|
346bfb6c47 | ||
|
|
f3db3dcc8d | ||
|
|
185b19fd5b | ||
|
|
6c6e730e9a | ||
|
|
52caa52cc2 | ||
|
|
8d755147d8 | ||
|
|
854c7a3c28 | ||
|
|
ecf3653444 | ||
|
|
24540e0ad9 | ||
|
|
7bd020e030 | ||
|
|
b119363fc2 | ||
|
|
6020723e77 | ||
|
|
fe402d3506 | ||
|
|
015058fadf | ||
|
|
929994132a | ||
|
|
a0c4557557 | ||
|
|
6e2ca7fbee | ||
|
|
ad189488b3 | ||
|
|
2c339a5114 | ||
|
|
240019feca | ||
|
|
598d154f69 | ||
|
|
fd09be5496 | ||
|
|
ef70054367 | ||
|
|
fbb26e6173 |
@@ -18,7 +18,8 @@ cmake .. \
|
||||
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
|
||||
-DENABLE_QT_TRANSLATION=ON \
|
||||
-DUSE_DISCORD_PRESENCE=ON \
|
||||
-DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"}
|
||||
-DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
|
||||
-DYUZU_USE_BUNDLED_FFMPEG=ON
|
||||
|
||||
make -j$(nproc)
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ option(YUZU_USE_BUNDLED_BOOST "Download bundled Boost" OFF)
|
||||
|
||||
option(YUZU_USE_BUNDLED_LIBUSB "Compile bundled libusb" OFF)
|
||||
|
||||
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" ON "WIN32" OFF)
|
||||
option(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" "${WIN32}")
|
||||
|
||||
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
|
||||
|
||||
@@ -496,7 +496,7 @@ endif()
|
||||
# Ensure libusb is properly configured (based on dolphin libusb include)
|
||||
if(NOT APPLE AND NOT YUZU_USE_BUNDLED_LIBUSB)
|
||||
include(FindPkgConfig)
|
||||
if (PKG_CONFIG_FOUND)
|
||||
if (PKG_CONFIG_FOUND AND NOT CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD")
|
||||
pkg_check_modules(LIBUSB QUIET libusb-1.0>=1.0.24)
|
||||
else()
|
||||
find_package(LibUSB)
|
||||
@@ -583,8 +583,32 @@ if (YUZU_USE_BUNDLED_FFMPEG)
|
||||
"${FFmpeg_PREFIX};${FFmpeg_BUILD_DIR}"
|
||||
CACHE PATH "Path to FFmpeg headers" FORCE)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
Include(FindPkgConfig REQUIRED)
|
||||
pkg_check_modules(LIBVA libva)
|
||||
endif()
|
||||
if(LIBVA_FOUND)
|
||||
pkg_check_modules(LIBDRM libdrm REQUIRED)
|
||||
find_package(X11 REQUIRED)
|
||||
pkg_check_modules(LIBVA-DRM libva-drm REQUIRED)
|
||||
pkg_check_modules(LIBVA-X11 libva-x11 REQUIRED)
|
||||
set(FFmpeg_LIBVA_LIBRARIES
|
||||
${LIBDRM_LIBRARIES}
|
||||
${X11_LIBRARIES}
|
||||
${LIBVA-DRM_LIBRARIES}
|
||||
${LIBVA-X11_LIBRARIES}
|
||||
${LIBVA_LIBRARIES})
|
||||
set(FFmpeg_HWACCEL_FLAGS
|
||||
--enable-hwaccel=h264_vaapi
|
||||
--enable-hwaccel=vp9_vaapi
|
||||
--enable-libdrm)
|
||||
message(STATUS "VA-API found")
|
||||
else()
|
||||
set(FFmpeg_HWACCEL_FLAGS --disable-vaapi)
|
||||
endif()
|
||||
|
||||
# `configure` parameters builds only exactly what yuzu needs from FFmpeg
|
||||
# `--disable-{vaapi,vdpau}` is needed to avoid linking issues
|
||||
# `--disable-vdpau` is needed to avoid linking issues
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${FFmpeg_MAKEFILE}
|
||||
@@ -600,13 +624,16 @@ if (YUZU_USE_BUNDLED_FFMPEG)
|
||||
--disable-network
|
||||
--disable-postproc
|
||||
--disable-swresample
|
||||
--disable-vaapi
|
||||
--disable-vdpau
|
||||
--enable-decoder=h264
|
||||
--enable-decoder=vp9
|
||||
--cc="${CMAKE_C_COMPILER}"
|
||||
--cxx="${CMAKE_CXX_COMPILER}"
|
||||
${FFmpeg_HWACCEL_FLAGS}
|
||||
WORKING_DIRECTORY
|
||||
${FFmpeg_BUILD_DIR}
|
||||
)
|
||||
unset(FFmpeg_HWACCEL_FLAGS)
|
||||
|
||||
# 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.
|
||||
@@ -616,9 +643,10 @@ if (YUZU_USE_BUNDLED_FFMPEG)
|
||||
OUTPUT_VARIABLE
|
||||
SYSTEM_THREADS)
|
||||
|
||||
set(FFmpeg_BUILD_LIBRARIES ${FFmpeg_LIBRARIES})
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${FFmpeg_LIBRARIES}
|
||||
${FFmpeg_BUILD_LIBRARIES}
|
||||
COMMAND
|
||||
make -j${SYSTEM_THREADS}
|
||||
WORKING_DIRECTORY
|
||||
@@ -628,7 +656,12 @@ if (YUZU_USE_BUNDLED_FFMPEG)
|
||||
# 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-configure ALL DEPENDS ${FFmpeg_MAKEFILE})
|
||||
add_custom_target(ffmpeg-build ALL DEPENDS ${FFmpeg_LIBRARIES} ffmpeg-configure)
|
||||
add_custom_target(ffmpeg-build ALL DEPENDS ${FFmpeg_BUILD_LIBRARIES} ffmpeg-configure)
|
||||
link_libraries(${FFmpeg_LIBVA_LIBRARIES})
|
||||
set(FFmpeg_LIBRARIES ${FFmpeg_LIBVA_LIBRARIES} ${FFmpeg_BUILD_LIBRARIES}
|
||||
CACHE PATH "Paths to FFmpeg libraries" FORCE)
|
||||
unset(FFmpeg_BUILD_LIBRARIES)
|
||||
unset(FFmpeg_LIBVA_LIBRARIES)
|
||||
|
||||
if (FFmpeg_FOUND)
|
||||
message(STATUS "Found FFmpeg version ${FFmpeg_VERSION}")
|
||||
|
||||
@@ -48,69 +48,6 @@ if (BUILD_REPOSITORY)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# The variable SRC_DIR must be passed into the script (since it uses the current build directory for all values of CMAKE_*_DIR)
|
||||
set(VIDEO_CORE "${SRC_DIR}/src/video_core")
|
||||
set(HASH_FILES
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_arb_decompiler.cpp"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_arb_decompiler.h"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_cache.cpp"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_cache.h"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.h"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.cpp"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.h"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic_half.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic_half_immediate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic_immediate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic_integer.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic_integer_immediate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/bfe.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/bfi.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/conversion.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/ffma.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/float_set.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/float_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/half_set.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/half_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/hfma2.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/image.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/integer_set.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/integer_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/memory.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/texture.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/other.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/predicate_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/predicate_set_register.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/register_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/shift.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/video.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/warp.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/xmad.cpp"
|
||||
"${VIDEO_CORE}/shader/ast.cpp"
|
||||
"${VIDEO_CORE}/shader/ast.h"
|
||||
"${VIDEO_CORE}/shader/compiler_settings.cpp"
|
||||
"${VIDEO_CORE}/shader/compiler_settings.h"
|
||||
"${VIDEO_CORE}/shader/control_flow.cpp"
|
||||
"${VIDEO_CORE}/shader/control_flow.h"
|
||||
"${VIDEO_CORE}/shader/decode.cpp"
|
||||
"${VIDEO_CORE}/shader/expr.cpp"
|
||||
"${VIDEO_CORE}/shader/expr.h"
|
||||
"${VIDEO_CORE}/shader/node.h"
|
||||
"${VIDEO_CORE}/shader/node_helper.cpp"
|
||||
"${VIDEO_CORE}/shader/node_helper.h"
|
||||
"${VIDEO_CORE}/shader/registry.cpp"
|
||||
"${VIDEO_CORE}/shader/registry.h"
|
||||
"${VIDEO_CORE}/shader/shader_ir.cpp"
|
||||
"${VIDEO_CORE}/shader/shader_ir.h"
|
||||
"${VIDEO_CORE}/shader/track.cpp"
|
||||
"${VIDEO_CORE}/shader/transform_feedback.cpp"
|
||||
"${VIDEO_CORE}/shader/transform_feedback.h"
|
||||
)
|
||||
set(COMBINED "")
|
||||
foreach (F IN LISTS HASH_FILES)
|
||||
file(READ ${F} TMP)
|
||||
set(COMBINED "${COMBINED}${TMP}")
|
||||
endforeach()
|
||||
string(MD5 SHADER_CACHE_VERSION "${COMBINED}")
|
||||
# The variable SRC_DIR must be passed into the script
|
||||
# (since it uses the current build directory for all values of CMAKE_*_DIR)
|
||||
configure_file("${SRC_DIR}/src/common/scm_rev.cpp.in" "scm_rev.cpp" @ONLY)
|
||||
|
||||
@@ -35,7 +35,7 @@ It is written in C++ with portability in mind, and we actively maintain builds f
|
||||
|
||||
The emulator is capable of running most commercial games at full speed, provided you meet the [necessary hardware requirements](https://yuzu-emu.org/help/quickstart/#hardware-requirements).
|
||||
|
||||
For a full list of games yuzu support, please visit our [Compatibility page](https://yuzu-emu.org/game/)
|
||||
For a full list of games yuzu support, please visit our [Compatibility page](https://yuzu-emu.org/game/)
|
||||
|
||||
Check out our [website](https://yuzu-emu.org/) for the latest news on exciting features, monthly progress reports, and more!
|
||||
|
||||
@@ -43,7 +43,7 @@ Check out our [website](https://yuzu-emu.org/) for the latest news on exciting f
|
||||
|
||||
Most of the development happens on GitHub. It's also where [our central repository](https://github.com/yuzu-emu/yuzu) is hosted. For development discussion, please join us on [Discord](https://discord.com/invite/u77vRWY).
|
||||
|
||||
If you want to contribute, please take a look at the [Contributor's Guide](https://github.com/yuzu-emu/yuzu/wiki/Contributing) and [Developer Information](https://github.com/yuzu-emu/yuzu/wiki/Developer-Information).
|
||||
If you want to contribute, please take a look at the [Contributor's Guide](https://github.com/yuzu-emu/yuzu/wiki/Contributing) and [Developer Information](https://github.com/yuzu-emu/yuzu/wiki/Developer-Information).
|
||||
You can also contact any of the developers on Discord in order to know about the current state of the emulator.
|
||||
|
||||
If you want to contribute to the user interface translation project, please check out the [yuzu project on transifex](https://www.transifex.com/yuzu-emulator/yuzu). We centralize translation work there, and periodically upstream translations.
|
||||
@@ -78,3 +78,5 @@ If you wish to support us a different way, please join our [Discord](https://dis
|
||||
## License
|
||||
|
||||
yuzu is licensed under the GPLv2 (or any later version). Refer to the [license.txt](https://github.com/yuzu-emu/yuzu/blob/master/license.txt) file.
|
||||
|
||||
The [Skyline-Emulator Team](https://github.com/skyline-emu/skyline) is exempt from GPLv2 for the contributions from all these contributors [FernandoS27](https://github.com/FernandoS27), [lioncash](https://github.com/lioncash), [bunnei](https://github.com/bunnei), [ReinUsesLisp](https://github.com/ReinUsesLisp), [Morph1984](https://github.com/Morph1984), [ogniK5377](https://github.com/ogniK5377), [german77](https://github.com/german77), [ameerj](https://github.com/ameerj), [Kelebek1](https://github.com/Kelebek1) and [lat9nq](https://github.com/lat9nq). They may only use the code from these contributors under Mozilla Public License, version 2.0.
|
||||
|
||||
20
dist/qt_themes/default/style.qss
vendored
20
dist/qt_themes/default/style.qss
vendored
@@ -38,6 +38,26 @@ QPushButton#RendererStatusBarButton:!checked {
|
||||
color: #0066ff;
|
||||
}
|
||||
|
||||
QPushButton#GPUStatusBarButton {
|
||||
color: #656565;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
padding: 0px 3px 0px 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
QPushButton#GPUStatusBarButton:hover {
|
||||
border: 1px solid #76797C;
|
||||
}
|
||||
|
||||
QPushButton#GPUStatusBarButton:checked {
|
||||
color: #b06020;
|
||||
}
|
||||
|
||||
QPushButton#GPUStatusBarButton:!checked {
|
||||
color: #109010;
|
||||
}
|
||||
|
||||
QPushButton#buttonRefreshDevices {
|
||||
min-width: 21px;
|
||||
min-height: 21px;
|
||||
|
||||
21
dist/qt_themes/qdarkstyle/style.qss
vendored
21
dist/qt_themes/qdarkstyle/style.qss
vendored
@@ -1283,6 +1283,27 @@ QPushButton#RendererStatusBarButton:!checked {
|
||||
color: #00ccdd;
|
||||
}
|
||||
|
||||
QPushButton#GPUStatusBarButton {
|
||||
min-width: 0px;
|
||||
color: #656565;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
padding: 0px 3px 0px 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
QPushButton#GPUStatusBarButton:hover {
|
||||
border: 1px solid #76797C;
|
||||
}
|
||||
|
||||
QPushButton#GPUStatusBarButton:checked {
|
||||
color: #ff8040;
|
||||
}
|
||||
|
||||
QPushButton#GPUStatusBarButton:!checked {
|
||||
color: #40dd40;
|
||||
}
|
||||
|
||||
QPushButton#buttonRefreshDevices {
|
||||
min-width: 23px;
|
||||
min-height: 23px;
|
||||
|
||||
@@ -2186,6 +2186,27 @@ QPushButton#RendererStatusBarButton:!checked {
|
||||
color: #00ccdd;
|
||||
}
|
||||
|
||||
QPushButton#GPUStatusBarButton {
|
||||
min-width: 0px;
|
||||
color: #656565;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
padding: 0px 3px 0px 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
QPushButton#GPUStatusBarButton:hover {
|
||||
border: 1px solid #76797C;
|
||||
}
|
||||
|
||||
QPushButton#GPUStatusBarButton:checked {
|
||||
color: #ff8040;
|
||||
}
|
||||
|
||||
QPushButton#GPUStatusBarButton:!checked {
|
||||
color: #40dd40;
|
||||
}
|
||||
|
||||
QPushButton#buttonRefreshDevices {
|
||||
min-width: 19px;
|
||||
min-height: 19px;
|
||||
|
||||
2
externals/Vulkan-Headers
vendored
2
externals/Vulkan-Headers
vendored
Submodule externals/Vulkan-Headers updated: 8188e3fbbc...07c4a37bcf
2
externals/libusb/CMakeLists.txt
vendored
2
externals/libusb/CMakeLists.txt
vendored
@@ -67,6 +67,8 @@ if (MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux") OR APPLE)
|
||||
"${LIBUSB_MAKEFILE}"
|
||||
COMMAND
|
||||
env
|
||||
CC="${CMAKE_C_COMPILER}"
|
||||
CXX="${CMAKE_CXX_COMPILER}"
|
||||
CFLAGS="${LIBUSB_CFLAGS}"
|
||||
sh "${LIBUSB_CONFIGURE}"
|
||||
${LIBUSB_CONFIGURE_ARGS}
|
||||
|
||||
2
externals/sirit
vendored
2
externals/sirit
vendored
Submodule externals/sirit updated: eefca56afd...a39596358a
@@ -142,6 +142,7 @@ add_subdirectory(core)
|
||||
add_subdirectory(audio_core)
|
||||
add_subdirectory(video_core)
|
||||
add_subdirectory(input_common)
|
||||
add_subdirectory(shader_recompiler)
|
||||
add_subdirectory(tests)
|
||||
|
||||
if (ENABLE_SDL2)
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
# Add a custom command to generate a new shader_cache_version hash when any of the following files change
|
||||
# NOTE: This is an approximation of what files affect shader generation, its possible something else
|
||||
# could affect the result, but much more unlikely than the following files. Keeping a list of files
|
||||
# like this allows for much better caching since it doesn't force the user to recompile binary shaders every update
|
||||
set(VIDEO_CORE "${CMAKE_SOURCE_DIR}/src/video_core")
|
||||
if (DEFINED ENV{AZURECIREPO})
|
||||
set(BUILD_REPOSITORY $ENV{AZURECIREPO})
|
||||
endif()
|
||||
@@ -30,64 +25,7 @@ add_custom_command(OUTPUT scm_rev.cpp
|
||||
-DGIT_EXECUTABLE=${GIT_EXECUTABLE}
|
||||
-P ${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake
|
||||
DEPENDS
|
||||
# WARNING! It was too much work to try and make a common location for this list,
|
||||
# so if you need to change it, please update CMakeModules/GenerateSCMRev.cmake as well
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_arb_decompiler.cpp"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_arb_decompiler.h"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_cache.cpp"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_cache.h"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.h"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.cpp"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.h"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic_half.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic_half_immediate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic_immediate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic_integer.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic_integer_immediate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/bfe.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/bfi.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/conversion.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/ffma.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/float_set.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/float_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/half_set.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/half_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/hfma2.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/image.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/integer_set.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/integer_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/memory.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/texture.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/other.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/predicate_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/predicate_set_register.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/register_set_predicate.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/shift.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/video.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/warp.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/xmad.cpp"
|
||||
"${VIDEO_CORE}/shader/ast.cpp"
|
||||
"${VIDEO_CORE}/shader/ast.h"
|
||||
"${VIDEO_CORE}/shader/compiler_settings.cpp"
|
||||
"${VIDEO_CORE}/shader/compiler_settings.h"
|
||||
"${VIDEO_CORE}/shader/control_flow.cpp"
|
||||
"${VIDEO_CORE}/shader/control_flow.h"
|
||||
"${VIDEO_CORE}/shader/decode.cpp"
|
||||
"${VIDEO_CORE}/shader/expr.cpp"
|
||||
"${VIDEO_CORE}/shader/expr.h"
|
||||
"${VIDEO_CORE}/shader/node.h"
|
||||
"${VIDEO_CORE}/shader/node_helper.cpp"
|
||||
"${VIDEO_CORE}/shader/node_helper.h"
|
||||
"${VIDEO_CORE}/shader/registry.cpp"
|
||||
"${VIDEO_CORE}/shader/registry.h"
|
||||
"${VIDEO_CORE}/shader/shader_ir.cpp"
|
||||
"${VIDEO_CORE}/shader/shader_ir.h"
|
||||
"${VIDEO_CORE}/shader/track.cpp"
|
||||
"${VIDEO_CORE}/shader/transform_feedback.cpp"
|
||||
"${VIDEO_CORE}/shader/transform_feedback.h"
|
||||
# and also check that the scm_rev files haven't changed
|
||||
# Check that the scm_rev files haven't changed
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.h"
|
||||
# technically we should regenerate if the git version changed, but its not worth the effort imo
|
||||
@@ -231,7 +169,7 @@ endif()
|
||||
|
||||
create_target_directory_groups(common)
|
||||
|
||||
target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile)
|
||||
target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads)
|
||||
target_link_libraries(common PRIVATE lz4::lz4 xbyak)
|
||||
if (MSVC)
|
||||
target_link_libraries(common PRIVATE zstd::zstd)
|
||||
|
||||
@@ -52,8 +52,12 @@ assert_noinline_call(const Fn& fn) {
|
||||
#define DEBUG_ASSERT(_a_) ASSERT(_a_)
|
||||
#define DEBUG_ASSERT_MSG(_a_, ...) ASSERT_MSG(_a_, __VA_ARGS__)
|
||||
#else // not debug
|
||||
#define DEBUG_ASSERT(_a_)
|
||||
#define DEBUG_ASSERT_MSG(_a_, _desc_, ...)
|
||||
#define DEBUG_ASSERT(_a_) \
|
||||
do { \
|
||||
} while (0)
|
||||
#define DEBUG_ASSERT_MSG(_a_, _desc_, ...) \
|
||||
do { \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define UNIMPLEMENTED() ASSERT_MSG(false, "Unimplemented code!")
|
||||
|
||||
@@ -20,6 +20,10 @@ std::string ToUTF8String(std::u8string_view u8_string) {
|
||||
return std::string{u8_string.begin(), u8_string.end()};
|
||||
}
|
||||
|
||||
std::string BufferToUTF8String(std::span<const u8> buffer) {
|
||||
return std::string{buffer.begin(), std::ranges::find(buffer, u8{0})};
|
||||
}
|
||||
|
||||
std::string PathToUTF8String(const std::filesystem::path& path) {
|
||||
return ToUTF8String(path.u8string());
|
||||
}
|
||||
|
||||
@@ -46,6 +46,17 @@ concept IsChar = std::same_as<T, char>;
|
||||
*/
|
||||
[[nodiscard]] std::string ToUTF8String(std::u8string_view u8_string);
|
||||
|
||||
/**
|
||||
* Converts a buffer of bytes to a UTF8-encoded std::string.
|
||||
* This converts from the start of the buffer until the first encountered null-terminator.
|
||||
* If no null-terminator is found, this converts the entire buffer instead.
|
||||
*
|
||||
* @param buffer Buffer of bytes
|
||||
*
|
||||
* @returns UTF-8 encoded std::string.
|
||||
*/
|
||||
[[nodiscard]] std::string BufferToUTF8String(std::span<const u8> buffer);
|
||||
|
||||
/**
|
||||
* Converts a filesystem path to a UTF-8 encoded std::string.
|
||||
*
|
||||
|
||||
@@ -61,7 +61,7 @@ template <typename ContiguousContainer>
|
||||
return out;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::array<u8, 16> AsArray(const char (&data)[17]) {
|
||||
[[nodiscard]] constexpr std::array<u8, 16> AsArray(const char (&data)[33]) {
|
||||
return HexStringToArray<16>(data);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <windows.h>
|
||||
#include "common/dynamic_library.h"
|
||||
|
||||
#elif defined(__linux__) // ^^^ Windows ^^^ vvv Linux vvv
|
||||
#elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
@@ -343,7 +343,7 @@ private:
|
||||
std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset
|
||||
};
|
||||
|
||||
#elif defined(__linux__) // ^^^ Windows ^^^ vvv Linux vvv
|
||||
#elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
|
||||
|
||||
class HostMemory::Impl {
|
||||
public:
|
||||
@@ -357,7 +357,12 @@ public:
|
||||
});
|
||||
|
||||
// Backing memory initialization
|
||||
#if defined(__FreeBSD__) && __FreeBSD__ < 13
|
||||
// XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30
|
||||
fd = shm_open(SHM_ANON, O_RDWR, 0600);
|
||||
#else
|
||||
fd = memfd_create("HostMemory", 0);
|
||||
#endif
|
||||
if (fd == -1) {
|
||||
LOG_CRITICAL(HW_Memory, "memfd_create failed: {}", strerror(errno));
|
||||
throw std::bad_alloc{};
|
||||
|
||||
@@ -144,6 +144,10 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
||||
SUB(Render, Software) \
|
||||
SUB(Render, OpenGL) \
|
||||
SUB(Render, Vulkan) \
|
||||
CLS(Shader) \
|
||||
SUB(Shader, SPIRV) \
|
||||
SUB(Shader, GLASM) \
|
||||
SUB(Shader, GLSL) \
|
||||
CLS(Audio) \
|
||||
SUB(Audio, DSP) \
|
||||
SUB(Audio, Sink) \
|
||||
|
||||
@@ -114,6 +114,10 @@ enum class Class : u8 {
|
||||
Render_Software, ///< Software renderer backend
|
||||
Render_OpenGL, ///< OpenGL backend
|
||||
Render_Vulkan, ///< Vulkan backend
|
||||
Shader, ///< Shader recompiler
|
||||
Shader_SPIRV, ///< Shader SPIR-V code generation
|
||||
Shader_GLASM, ///< Shader GLASM code generation
|
||||
Shader_GLSL, ///< Shader GLSL code generation
|
||||
Audio, ///< Audio emulation
|
||||
Audio_DSP, ///< The HLE implementation of the DSP
|
||||
Audio_Sink, ///< Emulator audio output backend
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#define BUILD_ID "@BUILD_ID@"
|
||||
#define TITLE_BAR_FORMAT_IDLE "@TITLE_BAR_FORMAT_IDLE@"
|
||||
#define TITLE_BAR_FORMAT_RUNNING "@TITLE_BAR_FORMAT_RUNNING@"
|
||||
#define SHADER_CACHE_VERSION "@SHADER_CACHE_VERSION@"
|
||||
|
||||
namespace Common {
|
||||
|
||||
@@ -28,7 +27,6 @@ const char g_build_version[] = BUILD_VERSION;
|
||||
const char g_build_id[] = BUILD_ID;
|
||||
const char g_title_bar_format_idle[] = TITLE_BAR_FORMAT_IDLE;
|
||||
const char g_title_bar_format_running[] = TITLE_BAR_FORMAT_RUNNING;
|
||||
const char g_shader_cache_version[] = SHADER_CACHE_VERSION;
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
@@ -48,8 +48,8 @@ void LogSettings() {
|
||||
log_setting("Core_UseMultiCore", values.use_multi_core.GetValue());
|
||||
log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue());
|
||||
log_setting("Renderer_UseResolutionFactor", values.resolution_factor.GetValue());
|
||||
log_setting("Renderer_UseFrameLimit", values.use_frame_limit.GetValue());
|
||||
log_setting("Renderer_FrameLimit", values.frame_limit.GetValue());
|
||||
log_setting("Renderer_UseSpeedLimit", values.use_speed_limit.GetValue());
|
||||
log_setting("Renderer_SpeedLimit", values.speed_limit.GetValue());
|
||||
log_setting("Renderer_UseDiskShaderCache", values.use_disk_shader_cache.GetValue());
|
||||
log_setting("Renderer_GPUAccuracyLevel", values.gpu_accuracy.GetValue());
|
||||
log_setting("Renderer_UseAsynchronousGpuEmulation",
|
||||
@@ -57,7 +57,7 @@ void LogSettings() {
|
||||
log_setting("Renderer_UseNvdecEmulation", values.use_nvdec_emulation.GetValue());
|
||||
log_setting("Renderer_AccelerateASTC", values.accelerate_astc.GetValue());
|
||||
log_setting("Renderer_UseVsync", values.use_vsync.GetValue());
|
||||
log_setting("Renderer_UseAssemblyShaders", values.use_assembly_shaders.GetValue());
|
||||
log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue());
|
||||
log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
|
||||
log_setting("Renderer_UseGarbageCollection", values.use_caches_gc.GetValue());
|
||||
log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
|
||||
@@ -132,15 +132,15 @@ void RestoreGlobalState(bool is_powered_on) {
|
||||
values.vulkan_device.SetGlobal(true);
|
||||
values.aspect_ratio.SetGlobal(true);
|
||||
values.max_anisotropy.SetGlobal(true);
|
||||
values.use_frame_limit.SetGlobal(true);
|
||||
values.frame_limit.SetGlobal(true);
|
||||
values.use_speed_limit.SetGlobal(true);
|
||||
values.speed_limit.SetGlobal(true);
|
||||
values.use_disk_shader_cache.SetGlobal(true);
|
||||
values.gpu_accuracy.SetGlobal(true);
|
||||
values.use_asynchronous_gpu_emulation.SetGlobal(true);
|
||||
values.use_nvdec_emulation.SetGlobal(true);
|
||||
values.accelerate_astc.SetGlobal(true);
|
||||
values.use_vsync.SetGlobal(true);
|
||||
values.use_assembly_shaders.SetGlobal(true);
|
||||
values.shader_backend.SetGlobal(true);
|
||||
values.use_asynchronous_shaders.SetGlobal(true);
|
||||
values.use_fast_gpu_time.SetGlobal(true);
|
||||
values.use_caches_gc.SetGlobal(true);
|
||||
|
||||
@@ -24,6 +24,12 @@ enum class RendererBackend : u32 {
|
||||
Vulkan = 1,
|
||||
};
|
||||
|
||||
enum class ShaderBackend : u32 {
|
||||
GLSL = 0,
|
||||
GLASM = 1,
|
||||
SPIRV = 2,
|
||||
};
|
||||
|
||||
enum class GPUAccuracy : u32 {
|
||||
Normal = 0,
|
||||
High = 1,
|
||||
@@ -36,6 +42,11 @@ enum class CPUAccuracy : u32 {
|
||||
Unsafe = 2,
|
||||
};
|
||||
|
||||
enum class FullscreenMode : u32 {
|
||||
Borderless = 0,
|
||||
Exclusive = 1,
|
||||
};
|
||||
|
||||
/** The BasicSetting class is a simple resource manager. It defines a label and default value
|
||||
* alongside the actual value of the setting for simpler and less-error prone use with frontend
|
||||
* configurations. Setting a default value and label is required, though subclasses may deviate from
|
||||
@@ -308,30 +319,35 @@ struct Values {
|
||||
// Renderer
|
||||
Setting<RendererBackend> renderer_backend{RendererBackend::OpenGL, "backend"};
|
||||
BasicSetting<bool> renderer_debug{false, "debug"};
|
||||
BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"};
|
||||
BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
|
||||
BasicSetting<bool> disable_shader_loop_safety_checks{false,
|
||||
"disable_shader_loop_safety_checks"};
|
||||
Setting<int> vulkan_device{0, "vulkan_device"};
|
||||
|
||||
Setting<u16> resolution_factor{1, "resolution_factor"};
|
||||
// *nix platforms may have issues with the borderless windowed fullscreen mode.
|
||||
// Default to exclusive fullscreen on these platforms for now.
|
||||
Setting<int> fullscreen_mode{
|
||||
Setting<FullscreenMode> fullscreen_mode{
|
||||
#ifdef _WIN32
|
||||
0,
|
||||
FullscreenMode::Borderless,
|
||||
#else
|
||||
1,
|
||||
FullscreenMode::Exclusive,
|
||||
#endif
|
||||
"fullscreen_mode"};
|
||||
Setting<int> aspect_ratio{0, "aspect_ratio"};
|
||||
Setting<int> max_anisotropy{0, "max_anisotropy"};
|
||||
Setting<bool> use_frame_limit{true, "use_frame_limit"};
|
||||
Setting<u16> frame_limit{100, "frame_limit"};
|
||||
Setting<bool> use_speed_limit{true, "use_speed_limit"};
|
||||
Setting<u16> speed_limit{100, "speed_limit"};
|
||||
Setting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"};
|
||||
Setting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, "gpu_accuracy"};
|
||||
Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"};
|
||||
Setting<bool> use_nvdec_emulation{true, "use_nvdec_emulation"};
|
||||
Setting<bool> accelerate_astc{true, "accelerate_astc"};
|
||||
Setting<bool> use_vsync{true, "use_vsync"};
|
||||
BasicSetting<u16> fps_cap{1000, "fps_cap"};
|
||||
BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"};
|
||||
Setting<bool> use_assembly_shaders{false, "use_assembly_shaders"};
|
||||
Setting<ShaderBackend> shader_backend{ShaderBackend::GLASM, "shader_backend"};
|
||||
Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
|
||||
Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
|
||||
Setting<bool> use_caches_gc{false, "use_caches_gc"};
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <stop_token>
|
||||
@@ -39,7 +40,7 @@ public:
|
||||
const auto lambda = [this, func](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName(thread_name.c_str());
|
||||
{
|
||||
std::conditional_t<with_state, StateType, int> state{func()};
|
||||
[[maybe_unused]] std::conditional_t<with_state, StateType, int> state{func()};
|
||||
while (!stop_token.stop_requested()) {
|
||||
Task task;
|
||||
{
|
||||
|
||||
@@ -6,10 +6,64 @@
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/uuid.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsHexDigit(char c) {
|
||||
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
|
||||
}
|
||||
|
||||
u8 HexCharToByte(char c) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
return static_cast<u8>(c - '0');
|
||||
}
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
return static_cast<u8>(c - 'a' + 10);
|
||||
}
|
||||
if (c >= 'A' && c <= 'F') {
|
||||
return static_cast<u8>(c - 'A' + 10);
|
||||
}
|
||||
ASSERT_MSG(false, "{} is not a hexadecimal digit!", c);
|
||||
return u8{0};
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
u128 HexStringToU128(std::string_view hex_string) {
|
||||
const size_t length = hex_string.length();
|
||||
|
||||
// Detect "0x" prefix.
|
||||
const bool has_0x_prefix = length > 2 && hex_string[0] == '0' && hex_string[1] == 'x';
|
||||
const size_t offset = has_0x_prefix ? 2 : 0;
|
||||
|
||||
// Check length.
|
||||
if (length > 32 + offset) {
|
||||
ASSERT_MSG(false, "hex_string has more than 32 hexadecimal characters!");
|
||||
return INVALID_UUID;
|
||||
}
|
||||
|
||||
u64 lo = 0;
|
||||
u64 hi = 0;
|
||||
for (size_t i = 0; i < length - offset; ++i) {
|
||||
const char c = hex_string[length - 1 - i];
|
||||
if (!IsHexDigit(c)) {
|
||||
ASSERT_MSG(false, "{} is not a hexadecimal digit!", c);
|
||||
return INVALID_UUID;
|
||||
}
|
||||
if (i < 16) {
|
||||
lo |= u64{HexCharToByte(c)} << (i * 4);
|
||||
}
|
||||
if (i >= 16) {
|
||||
hi |= u64{HexCharToByte(c)} << ((i - 16) * 4);
|
||||
}
|
||||
}
|
||||
return u128{lo, hi};
|
||||
}
|
||||
|
||||
UUID UUID::Generate() {
|
||||
std::random_device device;
|
||||
std::mt19937 gen(device());
|
||||
@@ -18,7 +72,7 @@ UUID UUID::Generate() {
|
||||
}
|
||||
|
||||
std::string UUID::Format() const {
|
||||
return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
|
||||
return fmt::format("{:016x}{:016x}", uuid[1], uuid[0]);
|
||||
}
|
||||
|
||||
std::string UUID::FormatSwitch() const {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
@@ -12,12 +13,30 @@ namespace Common {
|
||||
|
||||
constexpr u128 INVALID_UUID{{0, 0}};
|
||||
|
||||
/**
|
||||
* Converts a hex string to a 128-bit unsigned integer.
|
||||
*
|
||||
* The hex string can be formatted in lowercase or uppercase, with or without the "0x" prefix.
|
||||
*
|
||||
* This function will assert and return INVALID_UUID under the following conditions:
|
||||
* - If the hex string is more than 32 characters long
|
||||
* - If the hex string contains non-hexadecimal characters
|
||||
*
|
||||
* @param hex_string Hexadecimal string
|
||||
*
|
||||
* @returns A 128-bit unsigned integer if successfully converted, INVALID_UUID otherwise.
|
||||
*/
|
||||
[[nodiscard]] u128 HexStringToU128(std::string_view hex_string);
|
||||
|
||||
struct UUID {
|
||||
// UUIDs which are 0 are considered invalid!
|
||||
u128 uuid;
|
||||
UUID() = default;
|
||||
constexpr explicit UUID(const u128& id) : uuid{id} {}
|
||||
constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
|
||||
explicit UUID(std::string_view hex_string) {
|
||||
uuid = HexStringToU128(hex_string);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr explicit operator bool() const {
|
||||
return uuid != INVALID_UUID;
|
||||
@@ -50,3 +69,14 @@ struct UUID {
|
||||
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
|
||||
|
||||
} // namespace Common
|
||||
|
||||
namespace std {
|
||||
|
||||
template <>
|
||||
struct hash<Common::UUID> {
|
||||
size_t operator()(const Common::UUID& uuid) const noexcept {
|
||||
return uuid.uuid[1] ^ uuid.uuid[0];
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
@@ -517,6 +517,8 @@ add_library(core STATIC
|
||||
hle/service/psc/psc.h
|
||||
hle/service/ptm/psm.cpp
|
||||
hle/service/ptm/psm.h
|
||||
hle/service/kernel_helpers.cpp
|
||||
hle/service/kernel_helpers.h
|
||||
hle/service/service.cpp
|
||||
hle/service/service.h
|
||||
hle/service/set/set.cpp
|
||||
|
||||
@@ -411,7 +411,7 @@ struct System::Impl {
|
||||
std::string status_details = "";
|
||||
|
||||
std::unique_ptr<Core::PerfStats> perf_stats;
|
||||
Core::FrameLimiter frame_limiter;
|
||||
Core::SpeedLimiter speed_limiter;
|
||||
|
||||
bool is_multicore{};
|
||||
bool is_async_gpu{};
|
||||
@@ -606,12 +606,12 @@ const Core::PerfStats& System::GetPerfStats() const {
|
||||
return *impl->perf_stats;
|
||||
}
|
||||
|
||||
Core::FrameLimiter& System::FrameLimiter() {
|
||||
return impl->frame_limiter;
|
||||
Core::SpeedLimiter& System::SpeedLimiter() {
|
||||
return impl->speed_limiter;
|
||||
}
|
||||
|
||||
const Core::FrameLimiter& System::FrameLimiter() const {
|
||||
return impl->frame_limiter;
|
||||
const Core::SpeedLimiter& System::SpeedLimiter() const {
|
||||
return impl->speed_limiter;
|
||||
}
|
||||
|
||||
Loader::ResultStatus System::GetGameName(std::string& out) const {
|
||||
|
||||
@@ -94,7 +94,7 @@ class ARM_Interface;
|
||||
class CpuManager;
|
||||
class DeviceMemory;
|
||||
class ExclusiveMonitor;
|
||||
class FrameLimiter;
|
||||
class SpeedLimiter;
|
||||
class PerfStats;
|
||||
class Reporter;
|
||||
class TelemetrySession;
|
||||
@@ -292,11 +292,11 @@ public:
|
||||
/// Provides a constant reference to the internal PerfStats instance.
|
||||
[[nodiscard]] const Core::PerfStats& GetPerfStats() const;
|
||||
|
||||
/// Provides a reference to the frame limiter;
|
||||
[[nodiscard]] Core::FrameLimiter& FrameLimiter();
|
||||
/// Provides a reference to the speed limiter;
|
||||
[[nodiscard]] Core::SpeedLimiter& SpeedLimiter();
|
||||
|
||||
/// Provides a constant referent to the frame limiter
|
||||
[[nodiscard]] const Core::FrameLimiter& FrameLimiter() const;
|
||||
/// Provides a constant reference to the speed limiter
|
||||
[[nodiscard]] const Core::SpeedLimiter& SpeedLimiter() const;
|
||||
|
||||
/// Gets the name of the current game
|
||||
[[nodiscard]] Loader::ResultStatus GetGameName(std::string& out) const;
|
||||
|
||||
@@ -12,9 +12,9 @@ namespace HLE::ApiVersion {
|
||||
|
||||
// Horizon OS version constants.
|
||||
|
||||
constexpr u8 HOS_VERSION_MAJOR = 11;
|
||||
constexpr u8 HOS_VERSION_MINOR = 0;
|
||||
constexpr u8 HOS_VERSION_MICRO = 1;
|
||||
constexpr u8 HOS_VERSION_MAJOR = 12;
|
||||
constexpr u8 HOS_VERSION_MINOR = 1;
|
||||
constexpr u8 HOS_VERSION_MICRO = 0;
|
||||
|
||||
// NintendoSDK version constants.
|
||||
|
||||
@@ -22,15 +22,15 @@ constexpr u8 SDK_REVISION_MAJOR = 1;
|
||||
constexpr u8 SDK_REVISION_MINOR = 0;
|
||||
|
||||
constexpr char PLATFORM_STRING[] = "NX";
|
||||
constexpr char VERSION_HASH[] = "69103fcb2004dace877094c2f8c29e6113be5dbf";
|
||||
constexpr char DISPLAY_VERSION[] = "11.0.1";
|
||||
constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 11.0.1-1.0";
|
||||
constexpr char VERSION_HASH[] = "76b10c2dab7d3aa73fc162f8dff1655e6a21caf4";
|
||||
constexpr char DISPLAY_VERSION[] = "12.1.0";
|
||||
constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 12.1.0-1.0";
|
||||
|
||||
// Atmosphere version constants.
|
||||
|
||||
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MAJOR = 0;
|
||||
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MINOR = 19;
|
||||
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 4;
|
||||
constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 5;
|
||||
|
||||
constexpr u32 GetTargetFirmware() {
|
||||
return u32{HOS_VERSION_MAJOR} << 24 | u32{HOS_VERSION_MINOR} << 16 |
|
||||
|
||||
@@ -58,6 +58,9 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co
|
||||
|
||||
void SessionRequestHandler::ClientConnected(KServerSession* session) {
|
||||
session->ClientConnected(shared_from_this());
|
||||
|
||||
// Ensure our server session is tracked globally.
|
||||
kernel.RegisterServerSession(session);
|
||||
}
|
||||
|
||||
void SessionRequestHandler::ClientDisconnected(KServerSession* session) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/kernel/k_auto_object.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -11,4 +12,12 @@ KAutoObject* KAutoObject::Create(KAutoObject* obj) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
void KAutoObject::RegisterWithKernel() {
|
||||
kernel.RegisterKernelObject(this);
|
||||
}
|
||||
|
||||
void KAutoObject::UnregisterWithKernel() {
|
||||
kernel.UnregisterKernelObject(this);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -85,8 +85,12 @@ private:
|
||||
KERNEL_AUTOOBJECT_TRAITS(KAutoObject, KAutoObject);
|
||||
|
||||
public:
|
||||
explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) {}
|
||||
virtual ~KAutoObject() = default;
|
||||
explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) {
|
||||
RegisterWithKernel();
|
||||
}
|
||||
virtual ~KAutoObject() {
|
||||
UnregisterWithKernel();
|
||||
}
|
||||
|
||||
static KAutoObject* Create(KAutoObject* ptr);
|
||||
|
||||
@@ -166,6 +170,10 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void RegisterWithKernel();
|
||||
void UnregisterWithKernel();
|
||||
|
||||
protected:
|
||||
KernelCore& kernel;
|
||||
std::string name;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/device_memory.h"
|
||||
@@ -43,6 +44,8 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority
|
||||
ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1));
|
||||
|
||||
KThread* thread = KThread::Create(system.Kernel());
|
||||
SCOPE_EXIT({ thread->Close(); });
|
||||
|
||||
ASSERT(KThread::InitializeUserThread(system, thread, entry_point, 0, stack_top, priority,
|
||||
owner_process.GetIdealCoreId(), &owner_process)
|
||||
.IsSuccess());
|
||||
@@ -162,7 +165,7 @@ void KProcess::DecrementThreadCount() {
|
||||
ASSERT(num_threads > 0);
|
||||
|
||||
if (const auto count = --num_threads; count == 0) {
|
||||
UNIMPLEMENTED_MSG("Process termination is not implemented!");
|
||||
LOG_WARNING(Kernel, "Process termination is not fully implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,6 +409,9 @@ void KProcess::Finalize() {
|
||||
resource_limit->Close();
|
||||
}
|
||||
|
||||
// Finalize the handle table and close any open handles.
|
||||
handle_table.Finalize();
|
||||
|
||||
// Perform inherited finalization.
|
||||
KAutoObjectWithSlabHeapAndContainer<KProcess, KSynchronizationObject>::Finalize();
|
||||
}
|
||||
|
||||
@@ -28,7 +28,10 @@ namespace Kernel {
|
||||
|
||||
KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
|
||||
|
||||
KServerSession::~KServerSession() {}
|
||||
KServerSession::~KServerSession() {
|
||||
// Ensure that the global list tracking server sessions does not hold on to a reference.
|
||||
kernel.UnregisterServerSession(this);
|
||||
}
|
||||
|
||||
void KServerSession::Initialize(KSession* parent_session_, std::string&& name_,
|
||||
std::shared_ptr<SessionRequestManager> manager_) {
|
||||
|
||||
@@ -61,6 +61,7 @@ struct KernelCore::Impl {
|
||||
void Initialize(KernelCore& kernel) {
|
||||
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
|
||||
global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
|
||||
global_handle_table->Initialize(KHandleTable::MaxTableSize);
|
||||
|
||||
is_phantom_mode_for_singlecore = false;
|
||||
|
||||
@@ -90,9 +91,39 @@ struct KernelCore::Impl {
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
// Shutdown all processes.
|
||||
if (current_process) {
|
||||
current_process->Finalize();
|
||||
current_process->Close();
|
||||
current_process = nullptr;
|
||||
}
|
||||
process_list.clear();
|
||||
|
||||
// Ensures all service threads gracefully shutdown
|
||||
// Close all open server ports.
|
||||
std::unordered_set<KServerPort*> server_ports_;
|
||||
{
|
||||
std::lock_guard lk(server_ports_lock);
|
||||
server_ports_ = server_ports;
|
||||
server_ports.clear();
|
||||
}
|
||||
for (auto* server_port : server_ports_) {
|
||||
server_port->Close();
|
||||
}
|
||||
// Close all open server sessions.
|
||||
std::unordered_set<KServerSession*> server_sessions_;
|
||||
{
|
||||
std::lock_guard lk(server_sessions_lock);
|
||||
server_sessions_ = server_sessions;
|
||||
server_sessions.clear();
|
||||
}
|
||||
for (auto* server_session : server_sessions_) {
|
||||
server_session->Close();
|
||||
}
|
||||
|
||||
// Ensure that the object list container is finalized and properly shutdown.
|
||||
object_list_container.Finalize();
|
||||
|
||||
// Ensures all service threads gracefully shutdown.
|
||||
service_threads.clear();
|
||||
|
||||
next_object_id = 0;
|
||||
@@ -111,11 +142,7 @@ struct KernelCore::Impl {
|
||||
|
||||
cores.clear();
|
||||
|
||||
if (current_process) {
|
||||
current_process->Close();
|
||||
current_process = nullptr;
|
||||
}
|
||||
|
||||
global_handle_table->Finalize();
|
||||
global_handle_table.reset();
|
||||
|
||||
preemption_event = nullptr;
|
||||
@@ -142,6 +169,16 @@ struct KernelCore::Impl {
|
||||
|
||||
// Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
|
||||
next_host_thread_id = Core::Hardware::NUM_CPU_CORES;
|
||||
|
||||
// Track kernel objects that were not freed on shutdown
|
||||
{
|
||||
std::lock_guard lk(registered_objects_lock);
|
||||
if (registered_objects.size()) {
|
||||
LOG_WARNING(Kernel, "{} kernel objects were dangling on shutdown!",
|
||||
registered_objects.size());
|
||||
registered_objects.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InitializePhysicalCores() {
|
||||
@@ -630,6 +667,21 @@ struct KernelCore::Impl {
|
||||
user_slab_heap_size);
|
||||
}
|
||||
|
||||
KClientPort* CreateNamedServicePort(std::string name) {
|
||||
auto search = service_interface_factory.find(name);
|
||||
if (search == service_interface_factory.end()) {
|
||||
UNIMPLEMENTED();
|
||||
return {};
|
||||
}
|
||||
|
||||
KClientPort* port = &search->second(system.ServiceManager(), system);
|
||||
{
|
||||
std::lock_guard lk(server_ports_lock);
|
||||
server_ports.insert(&port->GetParent()->GetServerPort());
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
std::atomic<u32> next_object_id{0};
|
||||
std::atomic<u64> next_kernel_process_id{KProcess::InitialKIPIDMin};
|
||||
std::atomic<u64> next_user_process_id{KProcess::ProcessIDMin};
|
||||
@@ -656,6 +708,12 @@ struct KernelCore::Impl {
|
||||
/// the ConnectToPort SVC.
|
||||
std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory;
|
||||
NamedPortTable named_ports;
|
||||
std::unordered_set<KServerPort*> server_ports;
|
||||
std::unordered_set<KServerSession*> server_sessions;
|
||||
std::unordered_set<KAutoObject*> registered_objects;
|
||||
std::mutex server_ports_lock;
|
||||
std::mutex server_sessions_lock;
|
||||
std::mutex registered_objects_lock;
|
||||
|
||||
std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
|
||||
std::vector<Kernel::PhysicalCore> cores;
|
||||
@@ -844,12 +902,27 @@ void KernelCore::RegisterNamedService(std::string name, ServiceInterfaceFactory&
|
||||
}
|
||||
|
||||
KClientPort* KernelCore::CreateNamedServicePort(std::string name) {
|
||||
auto search = impl->service_interface_factory.find(name);
|
||||
if (search == impl->service_interface_factory.end()) {
|
||||
UNIMPLEMENTED();
|
||||
return {};
|
||||
}
|
||||
return &search->second(impl->system.ServiceManager(), impl->system);
|
||||
return impl->CreateNamedServicePort(std::move(name));
|
||||
}
|
||||
|
||||
void KernelCore::RegisterServerSession(KServerSession* server_session) {
|
||||
std::lock_guard lk(impl->server_sessions_lock);
|
||||
impl->server_sessions.insert(server_session);
|
||||
}
|
||||
|
||||
void KernelCore::UnregisterServerSession(KServerSession* server_session) {
|
||||
std::lock_guard lk(impl->server_sessions_lock);
|
||||
impl->server_sessions.erase(server_session);
|
||||
}
|
||||
|
||||
void KernelCore::RegisterKernelObject(KAutoObject* object) {
|
||||
std::lock_guard lk(impl->registered_objects_lock);
|
||||
impl->registered_objects.insert(object);
|
||||
}
|
||||
|
||||
void KernelCore::UnregisterKernelObject(KAutoObject* object) {
|
||||
std::lock_guard lk(impl->registered_objects_lock);
|
||||
impl->registered_objects.erase(object);
|
||||
}
|
||||
|
||||
bool KernelCore::IsValidNamedPort(NamedPortTable::const_iterator port) const {
|
||||
|
||||
@@ -45,6 +45,7 @@ class KPort;
|
||||
class KProcess;
|
||||
class KResourceLimit;
|
||||
class KScheduler;
|
||||
class KServerSession;
|
||||
class KSession;
|
||||
class KSharedMemory;
|
||||
class KThread;
|
||||
@@ -185,6 +186,22 @@ public:
|
||||
/// Opens a port to a service previously registered with RegisterNamedService.
|
||||
KClientPort* CreateNamedServicePort(std::string name);
|
||||
|
||||
/// Registers a server session with the gobal emulation state, to be freed on shutdown. This is
|
||||
/// necessary because we do not emulate processes for HLE sessions.
|
||||
void RegisterServerSession(KServerSession* server_session);
|
||||
|
||||
/// Unregisters a server session previously registered with RegisterServerSession when it was
|
||||
/// destroyed during the current emulation session.
|
||||
void UnregisterServerSession(KServerSession* server_session);
|
||||
|
||||
/// Registers all kernel objects with the global emulation state, this is purely for tracking
|
||||
/// leaks after emulation has been shutdown.
|
||||
void RegisterKernelObject(KAutoObject* object);
|
||||
|
||||
/// Unregisters a kernel object previously registered with RegisterKernelObject when it was
|
||||
/// destroyed during the current emulation session.
|
||||
void UnregisterKernelObject(KAutoObject* object);
|
||||
|
||||
/// Determines whether or not the given port is a valid named port.
|
||||
bool IsValidNamedPort(NamedPortTable::const_iterator port) const;
|
||||
|
||||
|
||||
@@ -298,6 +298,7 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out, VAddr po
|
||||
// Create a session.
|
||||
KClientSession* session{};
|
||||
R_TRY(port->CreateSession(std::addressof(session)));
|
||||
port->Close();
|
||||
|
||||
// Register the session in the table, close the extra reference.
|
||||
handle_table.Register(*out, session);
|
||||
@@ -1439,11 +1440,6 @@ static void ExitProcess(Core::System& system) {
|
||||
LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
|
||||
ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running,
|
||||
"Process has already exited");
|
||||
|
||||
current_process->PrepareForTermination();
|
||||
|
||||
// Kill the current thread
|
||||
system.Kernel().CurrentScheduler()->GetCurrentThread()->Exit();
|
||||
}
|
||||
|
||||
static void ExitProcess32(Core::System& system) {
|
||||
|
||||
@@ -292,7 +292,7 @@ public:
|
||||
|
||||
protected:
|
||||
void Get(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
|
||||
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
|
||||
ProfileBase profile_base{};
|
||||
ProfileData data{};
|
||||
if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
|
||||
@@ -301,7 +301,7 @@ protected:
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(profile_base);
|
||||
} else {
|
||||
LOG_ERROR(Service_ACC, "Failed to get profile base and data for user={}",
|
||||
LOG_ERROR(Service_ACC, "Failed to get profile base and data for user=0x{}",
|
||||
user_id.Format());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code
|
||||
@@ -309,14 +309,14 @@ protected:
|
||||
}
|
||||
|
||||
void GetBase(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
|
||||
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
|
||||
ProfileBase profile_base{};
|
||||
if (profile_manager.GetProfileBase(user_id, profile_base)) {
|
||||
IPC::ResponseBuilder rb{ctx, 16};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(profile_base);
|
||||
} else {
|
||||
LOG_ERROR(Service_ACC, "Failed to get profile base for user={}", user_id.Format());
|
||||
LOG_ERROR(Service_ACC, "Failed to get profile base for user=0x{}", user_id.Format());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code
|
||||
}
|
||||
@@ -372,7 +372,7 @@ protected:
|
||||
|
||||
const auto user_data = ctx.ReadBuffer();
|
||||
|
||||
LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid={}",
|
||||
LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
|
||||
Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
|
||||
base.timestamp, base.user_uuid.Format());
|
||||
@@ -405,7 +405,7 @@ protected:
|
||||
const auto user_data = ctx.ReadBuffer();
|
||||
const auto image_data = ctx.ReadBuffer(1);
|
||||
|
||||
LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid={}",
|
||||
LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
|
||||
Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
|
||||
base.timestamp, base.user_uuid.Format());
|
||||
@@ -662,7 +662,7 @@ void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
|
||||
void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
Common::UUID user_id = rp.PopRaw<Common::UUID>();
|
||||
LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
|
||||
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
@@ -693,7 +693,7 @@ void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
|
||||
void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
Common::UUID user_id = rp.PopRaw<Common::UUID>();
|
||||
LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
|
||||
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
@@ -802,7 +802,7 @@ void Module::Interface::GetProfileEditor(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
Common::UUID user_id = rp.PopRaw<Common::UUID>();
|
||||
|
||||
LOG_DEBUG(Service_ACC, "called, user_id={}", user_id.Format());
|
||||
LOG_DEBUG(Service_ACC, "called, user_id=0x{}", user_id.Format());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
@@ -844,7 +844,7 @@ void Module::Interface::StoreSaveDataThumbnailApplication(Kernel::HLERequestCont
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto uuid = rp.PopRaw<Common::UUID>();
|
||||
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called, uuid={}", uuid.Format());
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}", uuid.Format());
|
||||
|
||||
// TODO(ogniK): Check if application ID is zero on acc initialize. As we don't have a reliable
|
||||
// way of confirming things like the TID, we're going to assume a non zero value for the time
|
||||
@@ -858,7 +858,7 @@ void Module::Interface::StoreSaveDataThumbnailSystem(Kernel::HLERequestContext&
|
||||
const auto uuid = rp.PopRaw<Common::UUID>();
|
||||
const auto tid = rp.Pop<u64_le>();
|
||||
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called, uuid={}, tid={:016X}", uuid.Format(), tid);
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}, tid={:016X}", uuid.Format(), tid);
|
||||
StoreSaveDataThumbnail(ctx, uuid, tid);
|
||||
}
|
||||
|
||||
|
||||
@@ -377,7 +377,8 @@ void SoftwareKeyboard::SubmitForTextCheck(std::u16string submitted_text) {
|
||||
|
||||
if (swkbd_config_common.use_utf8) {
|
||||
std::string utf8_submitted_text = Common::UTF16ToUTF8(current_text);
|
||||
const u64 buffer_size = sizeof(u64) + utf8_submitted_text.size();
|
||||
// Include the null terminator in the buffer size.
|
||||
const u64 buffer_size = utf8_submitted_text.size() + 1;
|
||||
|
||||
LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-8 Submitted Text: {}", buffer_size,
|
||||
utf8_submitted_text);
|
||||
@@ -386,7 +387,8 @@ void SoftwareKeyboard::SubmitForTextCheck(std::u16string submitted_text) {
|
||||
std::memcpy(out_data.data() + sizeof(u64), utf8_submitted_text.data(),
|
||||
utf8_submitted_text.size());
|
||||
} else {
|
||||
const u64 buffer_size = sizeof(u64) + current_text.size() * sizeof(char16_t);
|
||||
// Include the null terminator in the buffer size.
|
||||
const u64 buffer_size = (current_text.size() + 1) * sizeof(char16_t);
|
||||
|
||||
LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-16 Submitted Text: {}", buffer_size,
|
||||
Common::UTF16ToUTF8(current_text));
|
||||
|
||||
@@ -158,7 +158,7 @@ private:
|
||||
const auto local_play = rp.Pop<bool>();
|
||||
const auto uuid = rp.PopRaw<Common::UUID>();
|
||||
|
||||
LOG_WARNING(Service_Friend, "(STUBBED) called local_play={} uuid={}", local_play,
|
||||
LOG_WARNING(Service_Friend, "(STUBBED) called, local_play={}, uuid=0x{}", local_play,
|
||||
uuid.Format());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -171,7 +171,7 @@ private:
|
||||
const auto uuid = rp.PopRaw<Common::UUID>();
|
||||
[[maybe_unused]] const auto filter = rp.PopRaw<SizedFriendFilter>();
|
||||
const auto pid = rp.Pop<u64>();
|
||||
LOG_WARNING(Service_Friend, "(STUBBED) called, offset={}, uuid={}, pid={}", friend_offset,
|
||||
LOG_WARNING(Service_Friend, "(STUBBED) called, offset={}, uuid=0x{}, pid={}", friend_offset,
|
||||
uuid.Format(), pid);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
@@ -289,7 +289,7 @@ void Module::Interface::CreateNotificationService(Kernel::HLERequestContext& ctx
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto uuid = rp.PopRaw<Common::UUID>();
|
||||
|
||||
LOG_DEBUG(Service_Friend, "called, uuid={}", uuid.Format());
|
||||
LOG_DEBUG(Service_Friend, "called, uuid=0x{}", uuid.Format());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "core/hle/kernel/k_writable_event.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/service/hid/controllers/npad.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
|
||||
namespace Service::HID {
|
||||
constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
|
||||
@@ -147,7 +148,9 @@ bool Controller_NPad::IsDeviceHandleValid(const DeviceHandle& device_handle) {
|
||||
device_handle.device_index < DeviceIndex::MaxDeviceIndex;
|
||||
}
|
||||
|
||||
Controller_NPad::Controller_NPad(Core::System& system_) : ControllerBase{system_} {
|
||||
Controller_NPad::Controller_NPad(Core::System& system_,
|
||||
KernelHelpers::ServiceContext& service_context_)
|
||||
: ControllerBase{system_}, service_context{service_context_} {
|
||||
latest_vibration_values.fill({DEFAULT_VIBRATION_VALUE, DEFAULT_VIBRATION_VALUE});
|
||||
}
|
||||
|
||||
@@ -251,10 +254,9 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
|
||||
}
|
||||
|
||||
void Controller_NPad::OnInit() {
|
||||
auto& kernel = system.Kernel();
|
||||
for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) {
|
||||
styleset_changed_events[i] = Kernel::KEvent::Create(kernel);
|
||||
styleset_changed_events[i]->Initialize(fmt::format("npad:NpadStyleSetChanged_{}", i));
|
||||
styleset_changed_events[i] =
|
||||
service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
|
||||
}
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
@@ -344,8 +346,7 @@ void Controller_NPad::OnRelease() {
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) {
|
||||
styleset_changed_events[i]->Close();
|
||||
styleset_changed_events[i] = nullptr;
|
||||
service_context.CloseEvent(styleset_changed_events[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,10 @@ class KEvent;
|
||||
class KReadableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Service::KernelHelpers {
|
||||
class ServiceContext;
|
||||
}
|
||||
|
||||
namespace Service::HID {
|
||||
|
||||
constexpr u32 NPAD_HANDHELD = 32;
|
||||
@@ -27,7 +31,8 @@ constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
|
||||
|
||||
class Controller_NPad final : public ControllerBase {
|
||||
public:
|
||||
explicit Controller_NPad(Core::System& system_);
|
||||
explicit Controller_NPad(Core::System& system_,
|
||||
KernelHelpers::ServiceContext& service_context_);
|
||||
~Controller_NPad() override;
|
||||
|
||||
// Called when the controller is initialized
|
||||
@@ -566,6 +571,7 @@ private:
|
||||
std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>,
|
||||
10>;
|
||||
|
||||
KernelHelpers::ServiceContext& service_context;
|
||||
std::mutex mutex;
|
||||
ButtonArray buttons;
|
||||
StickArray sticks;
|
||||
|
||||
@@ -46,8 +46,9 @@ constexpr auto pad_update_ns = std::chrono::nanoseconds{1000 * 1000}; //
|
||||
constexpr auto motion_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.666Hz)
|
||||
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
|
||||
|
||||
IAppletResource::IAppletResource(Core::System& system_)
|
||||
: ServiceFramework{system_, "IAppletResource"} {
|
||||
IAppletResource::IAppletResource(Core::System& system_,
|
||||
KernelHelpers::ServiceContext& service_context_)
|
||||
: ServiceFramework{system_, "IAppletResource"}, service_context{service_context_} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
|
||||
};
|
||||
@@ -63,7 +64,7 @@ IAppletResource::IAppletResource(Core::System& system_)
|
||||
MakeController<Controller_Stubbed>(HidController::CaptureButton);
|
||||
MakeController<Controller_Stubbed>(HidController::InputDetector);
|
||||
MakeController<Controller_Stubbed>(HidController::UniquePad);
|
||||
MakeController<Controller_NPad>(HidController::NPad);
|
||||
MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad);
|
||||
MakeController<Controller_Gesture>(HidController::Gesture);
|
||||
MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor);
|
||||
|
||||
@@ -191,13 +192,14 @@ private:
|
||||
|
||||
std::shared_ptr<IAppletResource> Hid::GetAppletResource() {
|
||||
if (applet_resource == nullptr) {
|
||||
applet_resource = std::make_shared<IAppletResource>(system);
|
||||
applet_resource = std::make_shared<IAppletResource>(system, service_context);
|
||||
}
|
||||
|
||||
return applet_resource;
|
||||
}
|
||||
|
||||
Hid::Hid(Core::System& system_) : ServiceFramework{system_, "hid"} {
|
||||
Hid::Hid(Core::System& system_)
|
||||
: ServiceFramework{system_, "hid"}, service_context{system_, service_name} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &Hid::CreateAppletResource, "CreateAppletResource"},
|
||||
@@ -347,7 +349,7 @@ void Hid::CreateAppletResource(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||
|
||||
if (applet_resource == nullptr) {
|
||||
applet_resource = std::make_shared<IAppletResource>(system);
|
||||
applet_resource = std::make_shared<IAppletResource>(system, service_context);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <chrono>
|
||||
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
@@ -39,7 +40,8 @@ enum class HidController : std::size_t {
|
||||
|
||||
class IAppletResource final : public ServiceFramework<IAppletResource> {
|
||||
public:
|
||||
explicit IAppletResource(Core::System& system_);
|
||||
explicit IAppletResource(Core::System& system_,
|
||||
KernelHelpers::ServiceContext& service_context_);
|
||||
~IAppletResource() override;
|
||||
|
||||
void ActivateController(HidController controller);
|
||||
@@ -60,11 +62,18 @@ private:
|
||||
void MakeController(HidController controller) {
|
||||
controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system);
|
||||
}
|
||||
template <typename T>
|
||||
void MakeControllerWithServiceContext(HidController controller) {
|
||||
controllers[static_cast<std::size_t>(controller)] =
|
||||
std::make_unique<T>(system, service_context);
|
||||
}
|
||||
|
||||
void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
|
||||
void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||
void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||
|
||||
KernelHelpers::ServiceContext& service_context;
|
||||
|
||||
std::shared_ptr<Core::Timing::EventType> pad_update_event;
|
||||
std::shared_ptr<Core::Timing::EventType> motion_update_event;
|
||||
|
||||
@@ -176,6 +185,8 @@ private:
|
||||
static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size.");
|
||||
|
||||
std::shared_ptr<IAppletResource> applet_resource;
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
};
|
||||
|
||||
/// Reload input devices. Used when input configuration changed
|
||||
|
||||
62
src/core/hle/service/kernel_helpers.cpp
Normal file
62
src/core/hle/service/kernel_helpers.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright 2021 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_readable_event.h"
|
||||
#include "core/hle/kernel/k_resource_limit.h"
|
||||
#include "core/hle/kernel/k_scoped_resource_reservation.h"
|
||||
#include "core/hle/kernel/k_writable_event.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
|
||||
namespace Service::KernelHelpers {
|
||||
|
||||
ServiceContext::ServiceContext(Core::System& system_, std::string name_)
|
||||
: kernel(system_.Kernel()) {
|
||||
process = Kernel::KProcess::Create(kernel);
|
||||
ASSERT(Kernel::KProcess::Initialize(process, system_, std::move(name_),
|
||||
Kernel::KProcess::ProcessType::Userland)
|
||||
.IsSuccess());
|
||||
}
|
||||
|
||||
ServiceContext::~ServiceContext() {
|
||||
process->Close();
|
||||
process = nullptr;
|
||||
}
|
||||
|
||||
Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) {
|
||||
// Reserve a new event from the process resource limit
|
||||
Kernel::KScopedResourceReservation event_reservation(process,
|
||||
Kernel::LimitableResource::Events);
|
||||
if (!event_reservation.Succeeded()) {
|
||||
LOG_CRITICAL(Service, "Resource limit reached!");
|
||||
return {};
|
||||
}
|
||||
|
||||
// Create a new event.
|
||||
auto* event = Kernel::KEvent::Create(kernel);
|
||||
if (!event) {
|
||||
LOG_CRITICAL(Service, "Unable to create event!");
|
||||
return {};
|
||||
}
|
||||
|
||||
// Initialize the event.
|
||||
event->Initialize(std::move(name));
|
||||
|
||||
// Commit the thread reservation.
|
||||
event_reservation.Commit();
|
||||
|
||||
// Register the event.
|
||||
Kernel::KEvent::Register(kernel, event);
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
void ServiceContext::CloseEvent(Kernel::KEvent* event) {
|
||||
event->GetReadableEvent().Close();
|
||||
event->GetWritableEvent().Close();
|
||||
}
|
||||
|
||||
} // namespace Service::KernelHelpers
|
||||
35
src/core/hle/service/kernel_helpers.h
Normal file
35
src/core/hle/service/kernel_helpers.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2021 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class KernelCore;
|
||||
class KEvent;
|
||||
class KProcess;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Service::KernelHelpers {
|
||||
|
||||
class ServiceContext {
|
||||
public:
|
||||
ServiceContext(Core::System& system_, std::string name_);
|
||||
~ServiceContext();
|
||||
|
||||
Kernel::KEvent* CreateEvent(std::string&& name);
|
||||
|
||||
void CloseEvent(Kernel::KEvent* event);
|
||||
|
||||
private:
|
||||
Kernel::KernelCore& kernel;
|
||||
Kernel::KProcess* process{};
|
||||
};
|
||||
|
||||
} // namespace Service::KernelHelpers
|
||||
@@ -339,13 +339,16 @@ std::optional<ApplicationLanguage> ConvertToApplicationLanguage(
|
||||
case Set::LanguageCode::FR_CA:
|
||||
return ApplicationLanguage::CanadianFrench;
|
||||
case Set::LanguageCode::PT:
|
||||
case Set::LanguageCode::PT_BR:
|
||||
return ApplicationLanguage::Portuguese;
|
||||
case Set::LanguageCode::RU:
|
||||
return ApplicationLanguage::Russian;
|
||||
case Set::LanguageCode::KO:
|
||||
return ApplicationLanguage::Korean;
|
||||
case Set::LanguageCode::ZH_TW:
|
||||
case Set::LanguageCode::ZH_HANT:
|
||||
return ApplicationLanguage::TraditionalChinese;
|
||||
case Set::LanguageCode::ZH_CN:
|
||||
case Set::LanguageCode::ZH_HANS:
|
||||
return ApplicationLanguage::SimplifiedChinese;
|
||||
default:
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/set/set.h"
|
||||
|
||||
namespace Service::NS {
|
||||
/// This is nn::ns::detail::ApplicationLanguage
|
||||
enum class ApplicationLanguage : u8 {
|
||||
AmericanEnglish = 0,
|
||||
BritishEnglish,
|
||||
Japanese,
|
||||
French,
|
||||
German,
|
||||
LatinAmericanSpanish,
|
||||
Spanish,
|
||||
Italian,
|
||||
Dutch,
|
||||
CanadianFrench,
|
||||
Portuguese,
|
||||
Russian,
|
||||
Korean,
|
||||
TraditionalChinese,
|
||||
SimplifiedChinese,
|
||||
Count
|
||||
};
|
||||
using ApplicationLanguagePriorityList =
|
||||
const std::array<ApplicationLanguage, static_cast<std::size_t>(ApplicationLanguage::Count)>;
|
||||
|
||||
constexpr u32 GetSupportedLanguageFlag(const ApplicationLanguage lang) {
|
||||
return 1U << static_cast<u32>(lang);
|
||||
}
|
||||
|
||||
const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(ApplicationLanguage lang);
|
||||
std::optional<ApplicationLanguage> ConvertToApplicationLanguage(
|
||||
Service::Set::LanguageCode language_code);
|
||||
std::optional<Service::Set::LanguageCode> ConvertToLanguageCode(ApplicationLanguage lang);
|
||||
} // namespace Service::NS
|
||||
@@ -54,7 +54,7 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
|
||||
|
||||
system.GetPerfStats().EndSystemFrame();
|
||||
system.GPU().SwapBuffers(&framebuffer);
|
||||
system.FrameLimiter().DoFrameLimiting(system.CoreTiming().GetGlobalTimeUs());
|
||||
system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs());
|
||||
system.GetPerfStats().BeginSystemFrame();
|
||||
}
|
||||
|
||||
|
||||
@@ -166,8 +166,6 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto
|
||||
LOG_ERROR(Service_NVDRV, "failed to map size={}", object->size);
|
||||
} else {
|
||||
cmd_buffer.map_address = object->dma_map_addr;
|
||||
AddBufferMap(object->dma_map_addr, object->size, object->addr,
|
||||
object->status == nvmap::Object::Status::Allocated);
|
||||
}
|
||||
}
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBuffer));
|
||||
@@ -178,30 +176,11 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto
|
||||
}
|
||||
|
||||
NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlMapBuffer params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer));
|
||||
std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
|
||||
SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
|
||||
|
||||
auto& gpu = system.GPU();
|
||||
|
||||
for (auto& cmd_buffer : cmd_buffer_handles) {
|
||||
const auto object{nvmap_dev->GetObject(cmd_buffer.map_handle)};
|
||||
if (!object) {
|
||||
LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmd_buffer.map_handle);
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return NvResult::InvalidState;
|
||||
}
|
||||
if (const auto size{RemoveBufferMap(object->dma_map_addr)}; size) {
|
||||
gpu.MemoryManager().Unmap(object->dma_map_addr, *size);
|
||||
} else {
|
||||
// This occurs quite frequently, however does not seem to impact functionality
|
||||
LOG_DEBUG(Service_NVDRV, "invalid offset=0x{:X} dma=0x{:X}", object->addr,
|
||||
object->dma_map_addr);
|
||||
}
|
||||
object->dma_map_addr = 0;
|
||||
}
|
||||
// This is intntionally stubbed.
|
||||
// Skip unmapping buffers here, as to not break the continuity of the VP9 reference frame
|
||||
// addresses, and risk invalidating data before the async GPU thread is done with it
|
||||
std::memset(output.data(), 0, output.size());
|
||||
LOG_DEBUG(Service_NVDRV, "(STUBBED) called");
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
@@ -212,33 +191,4 @@ NvResult nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input,
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
std::optional<nvhost_nvdec_common::BufferMap> nvhost_nvdec_common::FindBufferMap(
|
||||
GPUVAddr gpu_addr) const {
|
||||
const auto it = std::find_if(
|
||||
buffer_mappings.begin(), buffer_mappings.upper_bound(gpu_addr), [&](const auto& entry) {
|
||||
return (gpu_addr >= entry.second.StartAddr() && gpu_addr < entry.second.EndAddr());
|
||||
});
|
||||
|
||||
ASSERT(it != buffer_mappings.end());
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void nvhost_nvdec_common::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr,
|
||||
bool is_allocated) {
|
||||
buffer_mappings.insert_or_assign(gpu_addr, BufferMap{gpu_addr, size, cpu_addr, is_allocated});
|
||||
}
|
||||
|
||||
std::optional<std::size_t> nvhost_nvdec_common::RemoveBufferMap(GPUVAddr gpu_addr) {
|
||||
const auto iter{buffer_mappings.find(gpu_addr)};
|
||||
if (iter == buffer_mappings.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
std::size_t size = 0;
|
||||
if (iter->second.IsAllocated()) {
|
||||
size = iter->second.Size();
|
||||
}
|
||||
buffer_mappings.erase(iter);
|
||||
return size;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -23,45 +23,6 @@ public:
|
||||
~nvhost_nvdec_common() override;
|
||||
|
||||
protected:
|
||||
class BufferMap final {
|
||||
public:
|
||||
constexpr BufferMap() = default;
|
||||
|
||||
constexpr BufferMap(GPUVAddr start_addr_, std::size_t size_)
|
||||
: start_addr{start_addr_}, end_addr{start_addr_ + size_} {}
|
||||
|
||||
constexpr BufferMap(GPUVAddr start_addr_, std::size_t size_, VAddr cpu_addr_,
|
||||
bool is_allocated_)
|
||||
: start_addr{start_addr_}, end_addr{start_addr_ + size_}, cpu_addr{cpu_addr_},
|
||||
is_allocated{is_allocated_} {}
|
||||
|
||||
constexpr VAddr StartAddr() const {
|
||||
return start_addr;
|
||||
}
|
||||
|
||||
constexpr VAddr EndAddr() const {
|
||||
return end_addr;
|
||||
}
|
||||
|
||||
constexpr std::size_t Size() const {
|
||||
return end_addr - start_addr;
|
||||
}
|
||||
|
||||
constexpr VAddr CpuAddr() const {
|
||||
return cpu_addr;
|
||||
}
|
||||
|
||||
constexpr bool IsAllocated() const {
|
||||
return is_allocated;
|
||||
}
|
||||
|
||||
private:
|
||||
GPUVAddr start_addr{};
|
||||
GPUVAddr end_addr{};
|
||||
VAddr cpu_addr{};
|
||||
bool is_allocated{};
|
||||
};
|
||||
|
||||
struct IoctlSetNvmapFD {
|
||||
s32_le nvmap_fd{};
|
||||
};
|
||||
@@ -154,17 +115,11 @@ protected:
|
||||
NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
|
||||
std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const;
|
||||
void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated);
|
||||
std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr);
|
||||
|
||||
s32_le nvmap_fd{};
|
||||
u32_le submit_timeout{};
|
||||
std::shared_ptr<nvmap> nvmap_dev;
|
||||
SyncpointManager& syncpoint_manager;
|
||||
std::array<u32, MaxSyncPoints> device_syncpoints{};
|
||||
// This is expected to be ordered, therefore we must use a map, not unordered_map
|
||||
std::map<GPUVAddr, BufferMap> buffer_mappings;
|
||||
};
|
||||
}; // namespace Devices
|
||||
} // namespace Service::Nvidia
|
||||
|
||||
@@ -39,11 +39,11 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger
|
||||
nvflinger.SetNVDrvInstance(module_);
|
||||
}
|
||||
|
||||
Module::Module(Core::System& system) : syncpoint_manager{system.GPU()} {
|
||||
auto& kernel = system.Kernel();
|
||||
Module::Module(Core::System& system)
|
||||
: syncpoint_manager{system.GPU()}, service_context{system, "nvdrv"} {
|
||||
for (u32 i = 0; i < MaxNvEvents; i++) {
|
||||
events_interface.events[i].event = Kernel::KEvent::Create(kernel);
|
||||
events_interface.events[i].event->Initialize(fmt::format("NVDRV::NvEvent_{}", i));
|
||||
events_interface.events[i].event =
|
||||
service_context.CreateEvent(fmt::format("NVDRV::NvEvent_{}", i));
|
||||
events_interface.status[i] = EventState::Free;
|
||||
events_interface.registered[i] = false;
|
||||
}
|
||||
@@ -65,8 +65,7 @@ Module::Module(Core::System& system) : syncpoint_manager{system.GPU()} {
|
||||
|
||||
Module::~Module() {
|
||||
for (u32 i = 0; i < MaxNvEvents; i++) {
|
||||
events_interface.events[i].event->Close();
|
||||
events_interface.events[i].event = nullptr;
|
||||
service_context.CloseEvent(events_interface.events[i].event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
#include "core/hle/service/nvdrv/syncpoint_manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
@@ -154,6 +155,8 @@ private:
|
||||
std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices;
|
||||
|
||||
EventInterface events_interface;
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
};
|
||||
|
||||
/// Registers all NVDRV services with the specified service manager.
|
||||
|
||||
@@ -307,11 +307,12 @@ void NVFlinger::Compose() {
|
||||
}
|
||||
|
||||
s64 NVFlinger::GetNextTicks() const {
|
||||
if (Settings::values.disable_fps_limit.GetValue()) {
|
||||
return 0;
|
||||
}
|
||||
constexpr s64 max_hertz = 120LL;
|
||||
return (1000000000 * (1LL << swap_interval)) / max_hertz;
|
||||
static constexpr s64 max_hertz = 120LL;
|
||||
|
||||
const auto& settings = Settings::values;
|
||||
const bool unlocked_fps = settings.disable_fps_limit.GetValue();
|
||||
const s64 fps_cap = unlocked_fps ? static_cast<s64>(settings.fps_cap.GetValue()) : 1;
|
||||
return (1000000000 * (1LL << swap_interval)) / (max_hertz * fps_cap);
|
||||
}
|
||||
|
||||
} // namespace Service::NVFlinger
|
||||
|
||||
@@ -104,23 +104,22 @@ ServiceFrameworkBase::~ServiceFrameworkBase() {
|
||||
void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) {
|
||||
const auto guard = LockService();
|
||||
|
||||
ASSERT(!port_installed);
|
||||
ASSERT(!service_registered);
|
||||
|
||||
auto port = service_manager.RegisterService(service_name, max_sessions).Unwrap();
|
||||
port->SetSessionHandler(shared_from_this());
|
||||
port_installed = true;
|
||||
service_manager.RegisterService(service_name, max_sessions, shared_from_this());
|
||||
service_registered = true;
|
||||
}
|
||||
|
||||
Kernel::KClientPort& ServiceFrameworkBase::CreatePort() {
|
||||
const auto guard = LockService();
|
||||
|
||||
ASSERT(!port_installed);
|
||||
ASSERT(!service_registered);
|
||||
|
||||
auto* port = Kernel::KPort::Create(kernel);
|
||||
port->Initialize(max_sessions, false, service_name);
|
||||
port->GetServerPort().SetSessionHandler(shared_from_this());
|
||||
|
||||
port_installed = true;
|
||||
service_registered = true;
|
||||
|
||||
return port->GetClientPort();
|
||||
}
|
||||
|
||||
@@ -96,6 +96,9 @@ protected:
|
||||
/// System context that the service operates under.
|
||||
Core::System& system;
|
||||
|
||||
/// Identifier string used to connect to the service.
|
||||
std::string service_name;
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
friend class ServiceFramework;
|
||||
@@ -117,14 +120,12 @@ private:
|
||||
void RegisterHandlersBaseTipc(const FunctionInfoBase* functions, std::size_t n);
|
||||
void ReportUnimplementedFunction(Kernel::HLERequestContext& ctx, const FunctionInfoBase* info);
|
||||
|
||||
/// Identifier string used to connect to the service.
|
||||
std::string service_name;
|
||||
/// Maximum number of concurrent sessions that this service can handle.
|
||||
u32 max_sessions;
|
||||
|
||||
/// Flag to store if a port was already create/installed to detect multiple install attempts,
|
||||
/// which is not supported.
|
||||
bool port_installed = false;
|
||||
bool service_registered = false;
|
||||
|
||||
/// Function used to safely up-cast pointers to the derived class before invoking a handler.
|
||||
InvokerFn* handler_invoker;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
namespace Service::Set {
|
||||
namespace {
|
||||
constexpr std::array<LanguageCode, 17> available_language_codes = {{
|
||||
constexpr std::array<LanguageCode, 18> available_language_codes = {{
|
||||
LanguageCode::JA,
|
||||
LanguageCode::EN_US,
|
||||
LanguageCode::FR,
|
||||
@@ -30,6 +30,7 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{
|
||||
LanguageCode::ES_419,
|
||||
LanguageCode::ZH_HANS,
|
||||
LanguageCode::ZH_HANT,
|
||||
LanguageCode::PT_BR,
|
||||
}};
|
||||
|
||||
enum class KeyboardLayout : u64 {
|
||||
@@ -50,7 +51,7 @@ enum class KeyboardLayout : u64 {
|
||||
ChineseTraditional = 14,
|
||||
};
|
||||
|
||||
constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 17> language_to_layout{{
|
||||
constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 18> language_to_layout{{
|
||||
{LanguageCode::JA, KeyboardLayout::Japanese},
|
||||
{LanguageCode::EN_US, KeyboardLayout::EnglishUs},
|
||||
{LanguageCode::FR, KeyboardLayout::French},
|
||||
@@ -68,10 +69,11 @@ constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 17> language_to_la
|
||||
{LanguageCode::ES_419, KeyboardLayout::SpanishLatin},
|
||||
{LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified},
|
||||
{LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional},
|
||||
{LanguageCode::PT_BR, KeyboardLayout::Portuguese},
|
||||
}};
|
||||
|
||||
constexpr std::size_t pre4_0_0_max_entries = 15;
|
||||
constexpr std::size_t post4_0_0_max_entries = 17;
|
||||
constexpr std::size_t PRE_4_0_0_MAX_ENTRIES = 0xF;
|
||||
constexpr std::size_t POST_4_0_0_MAX_ENTRIES = 0x40;
|
||||
|
||||
constexpr ResultCode ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625};
|
||||
|
||||
@@ -81,9 +83,10 @@ void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t num_la
|
||||
rb.Push(static_cast<u32>(num_language_codes));
|
||||
}
|
||||
|
||||
void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_size) {
|
||||
void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_entries) {
|
||||
const std::size_t requested_amount = ctx.GetWriteBufferSize() / sizeof(LanguageCode);
|
||||
const std::size_t copy_amount = std::min(requested_amount, max_size);
|
||||
const std::size_t max_amount = std::min(requested_amount, max_entries);
|
||||
const std::size_t copy_amount = std::min(available_language_codes.size(), max_amount);
|
||||
const std::size_t copy_size = copy_amount * sizeof(LanguageCode);
|
||||
|
||||
ctx.WriteBuffer(available_language_codes.data(), copy_size);
|
||||
@@ -118,7 +121,7 @@ LanguageCode GetLanguageCodeFromIndex(std::size_t index) {
|
||||
void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SET, "called");
|
||||
|
||||
GetAvailableLanguageCodesImpl(ctx, pre4_0_0_max_entries);
|
||||
GetAvailableLanguageCodesImpl(ctx, PRE_4_0_0_MAX_ENTRIES);
|
||||
}
|
||||
|
||||
void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) {
|
||||
@@ -140,19 +143,19 @@ void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) {
|
||||
void SET::GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SET, "called");
|
||||
|
||||
GetAvailableLanguageCodesImpl(ctx, post4_0_0_max_entries);
|
||||
GetAvailableLanguageCodesImpl(ctx, POST_4_0_0_MAX_ENTRIES);
|
||||
}
|
||||
|
||||
void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SET, "called");
|
||||
|
||||
PushResponseLanguageCode(ctx, pre4_0_0_max_entries);
|
||||
PushResponseLanguageCode(ctx, PRE_4_0_0_MAX_ENTRIES);
|
||||
}
|
||||
|
||||
void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SET, "called");
|
||||
|
||||
PushResponseLanguageCode(ctx, post4_0_0_max_entries);
|
||||
PushResponseLanguageCode(ctx, POST_4_0_0_MAX_ENTRIES);
|
||||
}
|
||||
|
||||
void SET::GetQuestFlag(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
@@ -31,6 +31,7 @@ enum class LanguageCode : u64 {
|
||||
ES_419 = 0x00003931342D7365,
|
||||
ZH_HANS = 0x00736E61482D687A,
|
||||
ZH_HANT = 0x00746E61482D687A,
|
||||
PT_BR = 0x00000052422D7470,
|
||||
};
|
||||
LanguageCode GetLanguageCodeFromIndex(std::size_t idx);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <tuple>
|
||||
#include "common/assert.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/k_client_port.h"
|
||||
@@ -40,17 +41,13 @@ static ResultCode ValidateServiceName(const std::string& name) {
|
||||
}
|
||||
|
||||
Kernel::KClientPort& ServiceManager::InterfaceFactory(ServiceManager& self, Core::System& system) {
|
||||
ASSERT(self.sm_interface.expired());
|
||||
|
||||
auto sm = std::make_shared<SM>(self, system);
|
||||
self.sm_interface = sm;
|
||||
self.sm_interface = std::make_shared<SM>(self, system);
|
||||
self.controller_interface = std::make_unique<Controller>(system);
|
||||
|
||||
return sm->CreatePort();
|
||||
return self.sm_interface->CreatePort();
|
||||
}
|
||||
|
||||
ResultVal<Kernel::KServerPort*> ServiceManager::RegisterService(std::string name,
|
||||
u32 max_sessions) {
|
||||
ResultCode ServiceManager::RegisterService(std::string name, u32 max_sessions,
|
||||
Kernel::SessionRequestHandlerPtr handler) {
|
||||
|
||||
CASCADE_CODE(ValidateServiceName(name));
|
||||
|
||||
@@ -59,12 +56,9 @@ ResultVal<Kernel::KServerPort*> ServiceManager::RegisterService(std::string name
|
||||
return ERR_ALREADY_REGISTERED;
|
||||
}
|
||||
|
||||
auto* port = Kernel::KPort::Create(kernel);
|
||||
port->Initialize(max_sessions, false, name);
|
||||
registered_services.emplace(std::move(name), handler);
|
||||
|
||||
registered_services.emplace(std::move(name), port);
|
||||
|
||||
return MakeResult(&port->GetServerPort());
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode ServiceManager::UnregisterService(const std::string& name) {
|
||||
@@ -76,14 +70,11 @@ ResultCode ServiceManager::UnregisterService(const std::string& name) {
|
||||
return ERR_SERVICE_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
iter->second->Close();
|
||||
|
||||
registered_services.erase(iter);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name) {
|
||||
|
||||
CASCADE_CODE(ValidateServiceName(name));
|
||||
auto it = registered_services.find(name);
|
||||
if (it == registered_services.end()) {
|
||||
@@ -91,10 +82,13 @@ ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name
|
||||
return ERR_SERVICE_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
return MakeResult(it->second);
|
||||
}
|
||||
auto* port = Kernel::KPort::Create(kernel);
|
||||
port->Initialize(ServerSessionCountMax, false, name);
|
||||
auto handler = it->second;
|
||||
port->GetServerPort().SetSessionHandler(std::move(handler));
|
||||
|
||||
SM::~SM() = default;
|
||||
return MakeResult(port);
|
||||
}
|
||||
|
||||
/**
|
||||
* SM::Initialize service function
|
||||
@@ -156,11 +150,15 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext&
|
||||
LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, port_result.Code().raw);
|
||||
return port_result.Code();
|
||||
}
|
||||
auto& port = port_result.Unwrap()->GetClientPort();
|
||||
auto& port = port_result.Unwrap();
|
||||
SCOPE_EXIT({ port->GetClientPort().Close(); });
|
||||
|
||||
server_ports.emplace_back(&port->GetServerPort());
|
||||
|
||||
// Create a new session.
|
||||
Kernel::KClientSession* session{};
|
||||
if (const auto result = port.CreateSession(std::addressof(session)); result.IsError()) {
|
||||
if (const auto result = port->GetClientPort().CreateSession(std::addressof(session));
|
||||
result.IsError()) {
|
||||
LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw);
|
||||
return result;
|
||||
}
|
||||
@@ -180,20 +178,21 @@ void SM::RegisterService(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SM, "called with name={}, max_session_count={}, is_light={}", name,
|
||||
max_session_count, is_light);
|
||||
|
||||
auto handle = service_manager.RegisterService(name, max_session_count);
|
||||
if (handle.Failed()) {
|
||||
LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}",
|
||||
handle.Code().raw);
|
||||
if (const auto result = service_manager.RegisterService(name, max_session_count, nullptr);
|
||||
result.IsError()) {
|
||||
LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}", result.raw);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(handle.Code());
|
||||
rb.Push(result);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(handle.Code());
|
||||
auto* port = Kernel::KPort::Create(kernel);
|
||||
port->Initialize(ServerSessionCountMax, is_light, name);
|
||||
SCOPE_EXIT({ port->GetClientPort().Close(); });
|
||||
|
||||
auto server_port = handle.Unwrap();
|
||||
rb.PushMoveObjects(server_port);
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushMoveObjects(port->GetServerPort());
|
||||
}
|
||||
|
||||
void SM::UnregisterService(Kernel::HLERequestContext& ctx) {
|
||||
@@ -225,4 +224,10 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_)
|
||||
});
|
||||
}
|
||||
|
||||
SM::~SM() {
|
||||
for (auto& server_port : server_ports) {
|
||||
server_port->Close();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::SM
|
||||
|
||||
@@ -49,6 +49,7 @@ private:
|
||||
ServiceManager& service_manager;
|
||||
bool is_initialized{};
|
||||
Kernel::KernelCore& kernel;
|
||||
std::vector<Kernel::KServerPort*> server_ports;
|
||||
};
|
||||
|
||||
class ServiceManager {
|
||||
@@ -58,7 +59,8 @@ public:
|
||||
explicit ServiceManager(Kernel::KernelCore& kernel_);
|
||||
~ServiceManager();
|
||||
|
||||
ResultVal<Kernel::KServerPort*> RegisterService(std::string name, u32 max_sessions);
|
||||
ResultCode RegisterService(std::string name, u32 max_sessions,
|
||||
Kernel::SessionRequestHandlerPtr handler);
|
||||
ResultCode UnregisterService(const std::string& name);
|
||||
ResultVal<Kernel::KPort*> GetServicePort(const std::string& name);
|
||||
|
||||
@@ -69,21 +71,17 @@ public:
|
||||
LOG_DEBUG(Service, "Can't find service: {}", service_name);
|
||||
return nullptr;
|
||||
}
|
||||
auto* port = service->second;
|
||||
if (port == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::static_pointer_cast<T>(port->GetServerPort().GetSessionRequestHandler());
|
||||
return std::static_pointer_cast<T>(service->second);
|
||||
}
|
||||
|
||||
void InvokeControlRequest(Kernel::HLERequestContext& context);
|
||||
|
||||
private:
|
||||
std::weak_ptr<SM> sm_interface;
|
||||
std::shared_ptr<SM> sm_interface;
|
||||
std::unique_ptr<Controller> controller_interface;
|
||||
|
||||
/// Map of registered services, retrieved using GetServicePort.
|
||||
std::unordered_map<std::string, Kernel::KPort*> registered_services;
|
||||
std::unordered_map<std::string, Kernel::SessionRequestHandlerPtr> registered_services;
|
||||
|
||||
/// Kernel context
|
||||
Kernel::KernelCore& kernel;
|
||||
|
||||
@@ -570,7 +570,7 @@ std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message,
|
||||
ASSERT(flags == 0);
|
||||
|
||||
const sockaddr* to = nullptr;
|
||||
const int tolen = addr ? 0 : sizeof(sockaddr);
|
||||
const int tolen = addr ? sizeof(sockaddr) : 0;
|
||||
sockaddr host_addr_in;
|
||||
|
||||
if (addr) {
|
||||
|
||||
@@ -127,15 +127,15 @@ double PerfStats::GetLastFrameTimeScale() const {
|
||||
return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH;
|
||||
}
|
||||
|
||||
void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
|
||||
if (!Settings::values.use_frame_limit.GetValue() ||
|
||||
void SpeedLimiter::DoSpeedLimiting(microseconds current_system_time_us) {
|
||||
if (!Settings::values.use_speed_limit.GetValue() ||
|
||||
Settings::values.use_multi_core.GetValue()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto now = Clock::now();
|
||||
|
||||
const double sleep_scale = Settings::values.frame_limit.GetValue() / 100.0;
|
||||
const double sleep_scale = Settings::values.speed_limit.GetValue() / 100.0;
|
||||
|
||||
// Max lag caused by slow frames. Shouldn't be more than the length of a frame at the current
|
||||
// speed percent or it will clamp too much and prevent this from properly limiting to that
|
||||
@@ -143,17 +143,17 @@ void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
|
||||
// limiting
|
||||
const microseconds max_lag_time_us = duration_cast<microseconds>(
|
||||
std::chrono::duration<double, std::chrono::microseconds::period>(25ms / sleep_scale));
|
||||
frame_limiting_delta_err += duration_cast<microseconds>(
|
||||
speed_limiting_delta_err += duration_cast<microseconds>(
|
||||
std::chrono::duration<double, std::chrono::microseconds::period>(
|
||||
(current_system_time_us - previous_system_time_us) / sleep_scale));
|
||||
frame_limiting_delta_err -= duration_cast<microseconds>(now - previous_walltime);
|
||||
frame_limiting_delta_err =
|
||||
std::clamp(frame_limiting_delta_err, -max_lag_time_us, max_lag_time_us);
|
||||
speed_limiting_delta_err -= duration_cast<microseconds>(now - previous_walltime);
|
||||
speed_limiting_delta_err =
|
||||
std::clamp(speed_limiting_delta_err, -max_lag_time_us, max_lag_time_us);
|
||||
|
||||
if (frame_limiting_delta_err > microseconds::zero()) {
|
||||
std::this_thread::sleep_for(frame_limiting_delta_err);
|
||||
if (speed_limiting_delta_err > microseconds::zero()) {
|
||||
std::this_thread::sleep_for(speed_limiting_delta_err);
|
||||
auto now_after_sleep = Clock::now();
|
||||
frame_limiting_delta_err -= duration_cast<microseconds>(now_after_sleep - now);
|
||||
speed_limiting_delta_err -= duration_cast<microseconds>(now_after_sleep - now);
|
||||
now = now_after_sleep;
|
||||
}
|
||||
|
||||
|
||||
@@ -85,11 +85,11 @@ private:
|
||||
double previous_fps = 0;
|
||||
};
|
||||
|
||||
class FrameLimiter {
|
||||
class SpeedLimiter {
|
||||
public:
|
||||
using Clock = std::chrono::high_resolution_clock;
|
||||
|
||||
void DoFrameLimiting(std::chrono::microseconds current_system_time_us);
|
||||
void DoSpeedLimiting(std::chrono::microseconds current_system_time_us);
|
||||
|
||||
private:
|
||||
/// Emulated system time (in microseconds) at the last limiter invocation
|
||||
@@ -98,7 +98,7 @@ private:
|
||||
Clock::time_point previous_walltime = Clock::now();
|
||||
|
||||
/// Accumulated difference between walltime and emulated time
|
||||
std::chrono::microseconds frame_limiting_delta_err{0};
|
||||
std::chrono::microseconds speed_limiting_delta_err{0};
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -62,7 +62,6 @@ json GetYuzuVersionData() {
|
||||
{"build_date", std::string(Common::g_build_date)},
|
||||
{"build_fullname", std::string(Common::g_build_fullname)},
|
||||
{"build_version", std::string(Common::g_build_version)},
|
||||
{"shader_cache_version", std::string(Common::g_shader_cache_version)},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -221,8 +221,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
|
||||
TranslateRenderer(Settings::values.renderer_backend.GetValue()));
|
||||
AddField(field_type, "Renderer_ResolutionFactor",
|
||||
Settings::values.resolution_factor.GetValue());
|
||||
AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit.GetValue());
|
||||
AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit.GetValue());
|
||||
AddField(field_type, "Renderer_UseSpeedLimit", Settings::values.use_speed_limit.GetValue());
|
||||
AddField(field_type, "Renderer_SpeedLimit", Settings::values.speed_limit.GetValue());
|
||||
AddField(field_type, "Renderer_UseDiskShaderCache",
|
||||
Settings::values.use_disk_shader_cache.GetValue());
|
||||
AddField(field_type, "Renderer_GPUAccuracyLevel",
|
||||
@@ -233,8 +233,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
|
||||
Settings::values.use_nvdec_emulation.GetValue());
|
||||
AddField(field_type, "Renderer_AccelerateASTC", Settings::values.accelerate_astc.GetValue());
|
||||
AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue());
|
||||
AddField(field_type, "Renderer_UseAssemblyShaders",
|
||||
Settings::values.use_assembly_shaders.GetValue());
|
||||
AddField(field_type, "Renderer_ShaderBackend",
|
||||
static_cast<u32>(Settings::values.shader_backend.GetValue()));
|
||||
AddField(field_type, "Renderer_UseAsynchronousShaders",
|
||||
Settings::values.use_asynchronous_shaders.GetValue());
|
||||
AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode.GetValue());
|
||||
|
||||
@@ -304,10 +304,10 @@ std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers([
|
||||
}
|
||||
|
||||
std::string GenerateKeyboardParam(int key_code) {
|
||||
Common::ParamPackage param{
|
||||
{"engine", "keyboard"},
|
||||
{"code", std::to_string(key_code)},
|
||||
};
|
||||
Common::ParamPackage param;
|
||||
param.Set("engine", "keyboard");
|
||||
param.Set("code", key_code);
|
||||
param.Set("toggle", false);
|
||||
return param.Serialize();
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ Common::ParamPackage MouseButtonFactory::GetNextInput() const {
|
||||
if (pad.button != MouseInput::MouseButton::Undefined) {
|
||||
params.Set("engine", "mouse");
|
||||
params.Set("button", static_cast<u16>(pad.button));
|
||||
params.Set("toggle", false);
|
||||
return params;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,12 @@ public:
|
||||
state.buttons.insert_or_assign(button, value);
|
||||
}
|
||||
|
||||
void PreSetButton(int button) {
|
||||
if (!state.buttons.contains(button)) {
|
||||
SetButton(button, false);
|
||||
}
|
||||
}
|
||||
|
||||
void SetMotion(SDL_ControllerSensorEvent event) {
|
||||
constexpr float gravity_constant = 9.80665f;
|
||||
std::lock_guard lock{mutex};
|
||||
@@ -155,9 +161,16 @@ public:
|
||||
state.axes.insert_or_assign(axis, value);
|
||||
}
|
||||
|
||||
float GetAxis(int axis, float range) const {
|
||||
void PreSetAxis(int axis) {
|
||||
if (!state.axes.contains(axis)) {
|
||||
SetAxis(axis, 0);
|
||||
}
|
||||
}
|
||||
|
||||
float GetAxis(int axis, float range, float offset) const {
|
||||
std::lock_guard lock{mutex};
|
||||
return static_cast<float>(state.axes.at(axis)) / (32767.0f * range);
|
||||
const float value = static_cast<float>(state.axes.at(axis)) / 32767.0f;
|
||||
return (value + offset) / range;
|
||||
}
|
||||
|
||||
bool RumblePlay(u16 amp_low, u16 amp_high) {
|
||||
@@ -174,9 +187,10 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range) const {
|
||||
float x = GetAxis(axis_x, range);
|
||||
float y = GetAxis(axis_y, range);
|
||||
std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range, float offset_x,
|
||||
float offset_y) const {
|
||||
float x = GetAxis(axis_x, range, offset_x);
|
||||
float y = GetAxis(axis_y, range, offset_y);
|
||||
y = -y; // 3DS uses an y-axis inverse from SDL
|
||||
|
||||
// Make sure the coordinates are in the unit circle,
|
||||
@@ -483,7 +497,7 @@ public:
|
||||
trigger_if_greater(trigger_if_greater_) {}
|
||||
|
||||
bool GetStatus() const override {
|
||||
const float axis_value = joystick->GetAxis(axis, 1.0f);
|
||||
const float axis_value = joystick->GetAxis(axis, 1.0f, 0.0f);
|
||||
if (trigger_if_greater) {
|
||||
return axis_value > threshold;
|
||||
}
|
||||
@@ -500,12 +514,14 @@ private:
|
||||
class SDLAnalog final : public Input::AnalogDevice {
|
||||
public:
|
||||
explicit SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_,
|
||||
bool invert_x_, bool invert_y_, float deadzone_, float range_)
|
||||
bool invert_x_, bool invert_y_, float deadzone_, float range_,
|
||||
float offset_x_, float offset_y_)
|
||||
: joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_),
|
||||
invert_y(invert_y_), deadzone(deadzone_), range(range_) {}
|
||||
invert_y(invert_y_), deadzone(deadzone_), range(range_), offset_x(offset_x_),
|
||||
offset_y(offset_y_) {}
|
||||
|
||||
std::tuple<float, float> GetStatus() const override {
|
||||
auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range);
|
||||
auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range, offset_x, offset_y);
|
||||
const float r = std::sqrt((x * x) + (y * y));
|
||||
if (invert_x) {
|
||||
x = -x;
|
||||
@@ -522,8 +538,8 @@ public:
|
||||
}
|
||||
|
||||
std::tuple<float, float> GetRawStatus() const override {
|
||||
const float x = joystick->GetAxis(axis_x, range);
|
||||
const float y = joystick->GetAxis(axis_y, range);
|
||||
const float x = joystick->GetAxis(axis_x, range, offset_x);
|
||||
const float y = joystick->GetAxis(axis_y, range, offset_y);
|
||||
return {x, -y};
|
||||
}
|
||||
|
||||
@@ -555,6 +571,8 @@ private:
|
||||
const bool invert_y;
|
||||
const float deadzone;
|
||||
const float range;
|
||||
const float offset_x;
|
||||
const float offset_y;
|
||||
};
|
||||
|
||||
class SDLVibration final : public Input::VibrationDevice {
|
||||
@@ -621,7 +639,7 @@ public:
|
||||
trigger_if_greater(trigger_if_greater_) {}
|
||||
|
||||
Input::MotionStatus GetStatus() const override {
|
||||
const float axis_value = joystick->GetAxis(axis, 1.0f);
|
||||
const float axis_value = joystick->GetAxis(axis, 1.0f, 0.0f);
|
||||
bool trigger = axis_value < threshold;
|
||||
if (trigger_if_greater) {
|
||||
trigger = axis_value > threshold;
|
||||
@@ -707,7 +725,8 @@ public:
|
||||
|
||||
if (params.Has("axis")) {
|
||||
const int axis = params.Get("axis", 0);
|
||||
const float threshold = params.Get("threshold", 0.5f);
|
||||
// Convert range from (0.0, 1.0) to (-1.0, 1.0)
|
||||
const float threshold = (params.Get("threshold", 0.5f) - 0.5f) * 2.0f;
|
||||
const std::string direction_name = params.Get("direction", "");
|
||||
bool trigger_if_greater;
|
||||
if (direction_name == "+") {
|
||||
@@ -719,13 +738,13 @@ public:
|
||||
LOG_ERROR(Input, "Unknown direction {}", direction_name);
|
||||
}
|
||||
// This is necessary so accessing GetAxis with axis won't crash
|
||||
joystick->SetAxis(axis, 0);
|
||||
joystick->PreSetAxis(axis);
|
||||
return std::make_unique<SDLAxisButton>(joystick, axis, threshold, trigger_if_greater);
|
||||
}
|
||||
|
||||
const int button = params.Get("button", 0);
|
||||
// This is necessary so accessing GetButton with button won't crash
|
||||
joystick->SetButton(button, false);
|
||||
joystick->PreSetButton(button);
|
||||
return std::make_unique<SDLButton>(joystick, button, toggle);
|
||||
}
|
||||
|
||||
@@ -756,13 +775,15 @@ public:
|
||||
const std::string invert_y_value = params.Get("invert_y", "+");
|
||||
const bool invert_x = invert_x_value == "-";
|
||||
const bool invert_y = invert_y_value == "-";
|
||||
const float offset_x = params.Get("offset_x", 0.0f);
|
||||
const float offset_y = params.Get("offset_y", 0.0f);
|
||||
auto joystick = state.GetSDLJoystickByGUID(guid, port);
|
||||
|
||||
// This is necessary so accessing GetAxis with axis_x and axis_y won't crash
|
||||
joystick->SetAxis(axis_x, 0);
|
||||
joystick->SetAxis(axis_y, 0);
|
||||
joystick->PreSetAxis(axis_x);
|
||||
joystick->PreSetAxis(axis_y);
|
||||
return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, invert_x, invert_y, deadzone,
|
||||
range);
|
||||
range, offset_x, offset_y);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -843,13 +864,13 @@ public:
|
||||
LOG_ERROR(Input, "Unknown direction {}", direction_name);
|
||||
}
|
||||
// This is necessary so accessing GetAxis with axis won't crash
|
||||
joystick->SetAxis(axis, 0);
|
||||
joystick->PreSetAxis(axis);
|
||||
return std::make_unique<SDLAxisMotion>(joystick, axis, threshold, trigger_if_greater);
|
||||
}
|
||||
|
||||
const int button = params.Get("button", 0);
|
||||
// This is necessary so accessing GetButton with button won't crash
|
||||
joystick->SetButton(button, false);
|
||||
joystick->PreSetButton(button);
|
||||
return std::make_unique<SDLButtonMotion>(joystick, button);
|
||||
}
|
||||
|
||||
@@ -980,12 +1001,11 @@ Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid
|
||||
params.Set("port", port);
|
||||
params.Set("guid", std::move(guid));
|
||||
params.Set("axis", axis);
|
||||
params.Set("threshold", "0.5");
|
||||
if (value > 0) {
|
||||
params.Set("direction", "+");
|
||||
params.Set("threshold", "0.5");
|
||||
} else {
|
||||
params.Set("direction", "-");
|
||||
params.Set("threshold", "-0.5");
|
||||
}
|
||||
return params;
|
||||
}
|
||||
@@ -995,6 +1015,7 @@ Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid
|
||||
params.Set("port", port);
|
||||
params.Set("guid", std::move(guid));
|
||||
params.Set("button", button);
|
||||
params.Set("toggle", false);
|
||||
return params;
|
||||
}
|
||||
|
||||
@@ -1134,13 +1155,15 @@ Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& gu
|
||||
}
|
||||
|
||||
Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x,
|
||||
int axis_y) {
|
||||
int axis_y, float offset_x, float offset_y) {
|
||||
Common::ParamPackage params;
|
||||
params.Set("engine", "sdl");
|
||||
params.Set("port", port);
|
||||
params.Set("guid", guid);
|
||||
params.Set("axis_x", axis_x);
|
||||
params.Set("axis_y", axis_y);
|
||||
params.Set("offset_x", offset_x);
|
||||
params.Set("offset_y", offset_y);
|
||||
params.Set("invert_x", "+");
|
||||
params.Set("invert_y", "+");
|
||||
return params;
|
||||
@@ -1342,24 +1365,39 @@ AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& pa
|
||||
const auto& binding_left_y =
|
||||
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
|
||||
if (params.Has("guid2")) {
|
||||
joystick2->PreSetAxis(binding_left_x.value.axis);
|
||||
joystick2->PreSetAxis(binding_left_y.value.axis);
|
||||
const auto left_offset_x = -joystick2->GetAxis(binding_left_x.value.axis, 1.0f, 0);
|
||||
const auto left_offset_y = -joystick2->GetAxis(binding_left_y.value.axis, 1.0f, 0);
|
||||
mapping.insert_or_assign(
|
||||
Settings::NativeAnalog::LStick,
|
||||
BuildParamPackageForAnalog(joystick2->GetPort(), joystick2->GetGUID(),
|
||||
binding_left_x.value.axis, binding_left_y.value.axis));
|
||||
binding_left_x.value.axis, binding_left_y.value.axis,
|
||||
left_offset_x, left_offset_y));
|
||||
} else {
|
||||
joystick->PreSetAxis(binding_left_x.value.axis);
|
||||
joystick->PreSetAxis(binding_left_y.value.axis);
|
||||
const auto left_offset_x = -joystick->GetAxis(binding_left_x.value.axis, 1.0f, 0);
|
||||
const auto left_offset_y = -joystick->GetAxis(binding_left_y.value.axis, 1.0f, 0);
|
||||
mapping.insert_or_assign(
|
||||
Settings::NativeAnalog::LStick,
|
||||
BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
|
||||
binding_left_x.value.axis, binding_left_y.value.axis));
|
||||
binding_left_x.value.axis, binding_left_y.value.axis,
|
||||
left_offset_x, left_offset_y));
|
||||
}
|
||||
const auto& binding_right_x =
|
||||
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
|
||||
const auto& binding_right_y =
|
||||
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
|
||||
joystick->PreSetAxis(binding_right_x.value.axis);
|
||||
joystick->PreSetAxis(binding_right_y.value.axis);
|
||||
const auto right_offset_x = -joystick->GetAxis(binding_right_x.value.axis, 1.0f, 0);
|
||||
const auto right_offset_y = -joystick->GetAxis(binding_right_y.value.axis, 1.0f, 0);
|
||||
mapping.insert_or_assign(Settings::NativeAnalog::RStick,
|
||||
BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
|
||||
binding_right_x.value.axis,
|
||||
binding_right_y.value.axis));
|
||||
binding_right_y.value.axis, right_offset_x,
|
||||
right_offset_y));
|
||||
return mapping;
|
||||
}
|
||||
|
||||
@@ -1563,8 +1601,9 @@ public:
|
||||
}
|
||||
|
||||
if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
|
||||
// Set offset to zero since the joystick is not on center
|
||||
auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
|
||||
first_axis, axis);
|
||||
first_axis, axis, 0, 0);
|
||||
first_axis = -1;
|
||||
return params;
|
||||
}
|
||||
|
||||
268
src/shader_recompiler/CMakeLists.txt
Normal file
268
src/shader_recompiler/CMakeLists.txt
Normal file
@@ -0,0 +1,268 @@
|
||||
add_library(shader_recompiler STATIC
|
||||
backend/bindings.h
|
||||
backend/glasm/emit_context.cpp
|
||||
backend/glasm/emit_context.h
|
||||
backend/glasm/emit_glasm.cpp
|
||||
backend/glasm/emit_glasm.h
|
||||
backend/glasm/emit_glasm_barriers.cpp
|
||||
backend/glasm/emit_glasm_bitwise_conversion.cpp
|
||||
backend/glasm/emit_glasm_composite.cpp
|
||||
backend/glasm/emit_glasm_context_get_set.cpp
|
||||
backend/glasm/emit_glasm_control_flow.cpp
|
||||
backend/glasm/emit_glasm_convert.cpp
|
||||
backend/glasm/emit_glasm_floating_point.cpp
|
||||
backend/glasm/emit_glasm_image.cpp
|
||||
backend/glasm/emit_glasm_instructions.h
|
||||
backend/glasm/emit_glasm_integer.cpp
|
||||
backend/glasm/emit_glasm_logical.cpp
|
||||
backend/glasm/emit_glasm_memory.cpp
|
||||
backend/glasm/emit_glasm_not_implemented.cpp
|
||||
backend/glasm/emit_glasm_select.cpp
|
||||
backend/glasm/emit_glasm_shared_memory.cpp
|
||||
backend/glasm/emit_glasm_special.cpp
|
||||
backend/glasm/emit_glasm_undefined.cpp
|
||||
backend/glasm/emit_glasm_warp.cpp
|
||||
backend/glasm/reg_alloc.cpp
|
||||
backend/glasm/reg_alloc.h
|
||||
backend/glsl/emit_context.cpp
|
||||
backend/glsl/emit_context.h
|
||||
backend/glsl/emit_glsl.cpp
|
||||
backend/glsl/emit_glsl.h
|
||||
backend/glsl/emit_glsl_atomic.cpp
|
||||
backend/glsl/emit_glsl_barriers.cpp
|
||||
backend/glsl/emit_glsl_bitwise_conversion.cpp
|
||||
backend/glsl/emit_glsl_composite.cpp
|
||||
backend/glsl/emit_glsl_context_get_set.cpp
|
||||
backend/glsl/emit_glsl_control_flow.cpp
|
||||
backend/glsl/emit_glsl_convert.cpp
|
||||
backend/glsl/emit_glsl_floating_point.cpp
|
||||
backend/glsl/emit_glsl_image.cpp
|
||||
backend/glsl/emit_glsl_instructions.h
|
||||
backend/glsl/emit_glsl_integer.cpp
|
||||
backend/glsl/emit_glsl_logical.cpp
|
||||
backend/glsl/emit_glsl_memory.cpp
|
||||
backend/glsl/emit_glsl_not_implemented.cpp
|
||||
backend/glsl/emit_glsl_select.cpp
|
||||
backend/glsl/emit_glsl_shared_memory.cpp
|
||||
backend/glsl/emit_glsl_special.cpp
|
||||
backend/glsl/emit_glsl_undefined.cpp
|
||||
backend/glsl/emit_glsl_warp.cpp
|
||||
backend/glsl/var_alloc.cpp
|
||||
backend/glsl/var_alloc.h
|
||||
backend/spirv/emit_context.cpp
|
||||
backend/spirv/emit_context.h
|
||||
backend/spirv/emit_spirv.cpp
|
||||
backend/spirv/emit_spirv.h
|
||||
backend/spirv/emit_spirv_atomic.cpp
|
||||
backend/spirv/emit_spirv_barriers.cpp
|
||||
backend/spirv/emit_spirv_bitwise_conversion.cpp
|
||||
backend/spirv/emit_spirv_composite.cpp
|
||||
backend/spirv/emit_spirv_context_get_set.cpp
|
||||
backend/spirv/emit_spirv_control_flow.cpp
|
||||
backend/spirv/emit_spirv_convert.cpp
|
||||
backend/spirv/emit_spirv_floating_point.cpp
|
||||
backend/spirv/emit_spirv_image.cpp
|
||||
backend/spirv/emit_spirv_image_atomic.cpp
|
||||
backend/spirv/emit_spirv_instructions.h
|
||||
backend/spirv/emit_spirv_integer.cpp
|
||||
backend/spirv/emit_spirv_logical.cpp
|
||||
backend/spirv/emit_spirv_memory.cpp
|
||||
backend/spirv/emit_spirv_select.cpp
|
||||
backend/spirv/emit_spirv_shared_memory.cpp
|
||||
backend/spirv/emit_spirv_special.cpp
|
||||
backend/spirv/emit_spirv_undefined.cpp
|
||||
backend/spirv/emit_spirv_warp.cpp
|
||||
environment.h
|
||||
exception.h
|
||||
frontend/ir/abstract_syntax_list.h
|
||||
frontend/ir/attribute.cpp
|
||||
frontend/ir/attribute.h
|
||||
frontend/ir/basic_block.cpp
|
||||
frontend/ir/basic_block.h
|
||||
frontend/ir/breadth_first_search.h
|
||||
frontend/ir/condition.cpp
|
||||
frontend/ir/condition.h
|
||||
frontend/ir/flow_test.cpp
|
||||
frontend/ir/flow_test.h
|
||||
frontend/ir/ir_emitter.cpp
|
||||
frontend/ir/ir_emitter.h
|
||||
frontend/ir/microinstruction.cpp
|
||||
frontend/ir/modifiers.h
|
||||
frontend/ir/opcodes.cpp
|
||||
frontend/ir/opcodes.h
|
||||
frontend/ir/opcodes.inc
|
||||
frontend/ir/patch.cpp
|
||||
frontend/ir/patch.h
|
||||
frontend/ir/post_order.cpp
|
||||
frontend/ir/post_order.h
|
||||
frontend/ir/pred.h
|
||||
frontend/ir/program.cpp
|
||||
frontend/ir/program.h
|
||||
frontend/ir/reg.h
|
||||
frontend/ir/type.cpp
|
||||
frontend/ir/type.h
|
||||
frontend/ir/value.cpp
|
||||
frontend/ir/value.h
|
||||
frontend/maxwell/control_flow.cpp
|
||||
frontend/maxwell/control_flow.h
|
||||
frontend/maxwell/decode.cpp
|
||||
frontend/maxwell/decode.h
|
||||
frontend/maxwell/indirect_branch_table_track.cpp
|
||||
frontend/maxwell/indirect_branch_table_track.h
|
||||
frontend/maxwell/instruction.h
|
||||
frontend/maxwell/location.h
|
||||
frontend/maxwell/maxwell.inc
|
||||
frontend/maxwell/opcodes.cpp
|
||||
frontend/maxwell/opcodes.h
|
||||
frontend/maxwell/structured_control_flow.cpp
|
||||
frontend/maxwell/structured_control_flow.h
|
||||
frontend/maxwell/translate/impl/atomic_operations_global_memory.cpp
|
||||
frontend/maxwell/translate/impl/atomic_operations_shared_memory.cpp
|
||||
frontend/maxwell/translate/impl/attribute_memory_to_physical.cpp
|
||||
frontend/maxwell/translate/impl/barrier_operations.cpp
|
||||
frontend/maxwell/translate/impl/bitfield_extract.cpp
|
||||
frontend/maxwell/translate/impl/bitfield_insert.cpp
|
||||
frontend/maxwell/translate/impl/branch_indirect.cpp
|
||||
frontend/maxwell/translate/impl/common_encoding.h
|
||||
frontend/maxwell/translate/impl/common_funcs.cpp
|
||||
frontend/maxwell/translate/impl/common_funcs.h
|
||||
frontend/maxwell/translate/impl/condition_code_set.cpp
|
||||
frontend/maxwell/translate/impl/double_add.cpp
|
||||
frontend/maxwell/translate/impl/double_compare_and_set.cpp
|
||||
frontend/maxwell/translate/impl/double_fused_multiply_add.cpp
|
||||
frontend/maxwell/translate/impl/double_min_max.cpp
|
||||
frontend/maxwell/translate/impl/double_multiply.cpp
|
||||
frontend/maxwell/translate/impl/double_set_predicate.cpp
|
||||
frontend/maxwell/translate/impl/exit_program.cpp
|
||||
frontend/maxwell/translate/impl/find_leading_one.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_add.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_compare.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_compare_and_set.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_conversion_floating_point.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_fused_multiply_add.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_min_max.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_multi_function.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_multiply.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_range_reduction.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_set_predicate.cpp
|
||||
frontend/maxwell/translate/impl/floating_point_swizzled_add.cpp
|
||||
frontend/maxwell/translate/impl/half_floating_point_add.cpp
|
||||
frontend/maxwell/translate/impl/half_floating_point_fused_multiply_add.cpp
|
||||
frontend/maxwell/translate/impl/half_floating_point_helper.cpp
|
||||
frontend/maxwell/translate/impl/half_floating_point_helper.h
|
||||
frontend/maxwell/translate/impl/half_floating_point_multiply.cpp
|
||||
frontend/maxwell/translate/impl/half_floating_point_set.cpp
|
||||
frontend/maxwell/translate/impl/half_floating_point_set_predicate.cpp
|
||||
frontend/maxwell/translate/impl/impl.cpp
|
||||
frontend/maxwell/translate/impl/impl.h
|
||||
frontend/maxwell/translate/impl/integer_add.cpp
|
||||
frontend/maxwell/translate/impl/integer_add_three_input.cpp
|
||||
frontend/maxwell/translate/impl/integer_compare.cpp
|
||||
frontend/maxwell/translate/impl/integer_compare_and_set.cpp
|
||||
frontend/maxwell/translate/impl/integer_floating_point_conversion.cpp
|
||||
frontend/maxwell/translate/impl/integer_funnel_shift.cpp
|
||||
frontend/maxwell/translate/impl/integer_minimum_maximum.cpp
|
||||
frontend/maxwell/translate/impl/integer_popcount.cpp
|
||||
frontend/maxwell/translate/impl/integer_scaled_add.cpp
|
||||
frontend/maxwell/translate/impl/integer_set_predicate.cpp
|
||||
frontend/maxwell/translate/impl/integer_shift_left.cpp
|
||||
frontend/maxwell/translate/impl/integer_shift_right.cpp
|
||||
frontend/maxwell/translate/impl/integer_short_multiply_add.cpp
|
||||
frontend/maxwell/translate/impl/integer_to_integer_conversion.cpp
|
||||
frontend/maxwell/translate/impl/internal_stage_buffer_entry_read.cpp
|
||||
frontend/maxwell/translate/impl/load_constant.cpp
|
||||
frontend/maxwell/translate/impl/load_constant.h
|
||||
frontend/maxwell/translate/impl/load_effective_address.cpp
|
||||
frontend/maxwell/translate/impl/load_store_attribute.cpp
|
||||
frontend/maxwell/translate/impl/load_store_local_shared.cpp
|
||||
frontend/maxwell/translate/impl/load_store_memory.cpp
|
||||
frontend/maxwell/translate/impl/logic_operation.cpp
|
||||
frontend/maxwell/translate/impl/logic_operation_three_input.cpp
|
||||
frontend/maxwell/translate/impl/move_predicate_to_register.cpp
|
||||
frontend/maxwell/translate/impl/move_register.cpp
|
||||
frontend/maxwell/translate/impl/move_register_to_predicate.cpp
|
||||
frontend/maxwell/translate/impl/move_special_register.cpp
|
||||
frontend/maxwell/translate/impl/not_implemented.cpp
|
||||
frontend/maxwell/translate/impl/output_geometry.cpp
|
||||
frontend/maxwell/translate/impl/pixel_load.cpp
|
||||
frontend/maxwell/translate/impl/predicate_set_predicate.cpp
|
||||
frontend/maxwell/translate/impl/predicate_set_register.cpp
|
||||
frontend/maxwell/translate/impl/select_source_with_predicate.cpp
|
||||
frontend/maxwell/translate/impl/surface_atomic_operations.cpp
|
||||
frontend/maxwell/translate/impl/surface_load_store.cpp
|
||||
frontend/maxwell/translate/impl/texture_fetch.cpp
|
||||
frontend/maxwell/translate/impl/texture_fetch_swizzled.cpp
|
||||
frontend/maxwell/translate/impl/texture_gather.cpp
|
||||
frontend/maxwell/translate/impl/texture_gather_swizzled.cpp
|
||||
frontend/maxwell/translate/impl/texture_gradient.cpp
|
||||
frontend/maxwell/translate/impl/texture_load.cpp
|
||||
frontend/maxwell/translate/impl/texture_load_swizzled.cpp
|
||||
frontend/maxwell/translate/impl/texture_mipmap_level.cpp
|
||||
frontend/maxwell/translate/impl/texture_query.cpp
|
||||
frontend/maxwell/translate/impl/video_helper.cpp
|
||||
frontend/maxwell/translate/impl/video_helper.h
|
||||
frontend/maxwell/translate/impl/video_minimum_maximum.cpp
|
||||
frontend/maxwell/translate/impl/video_multiply_add.cpp
|
||||
frontend/maxwell/translate/impl/video_set_predicate.cpp
|
||||
frontend/maxwell/translate/impl/vote.cpp
|
||||
frontend/maxwell/translate/impl/warp_shuffle.cpp
|
||||
frontend/maxwell/translate/translate.cpp
|
||||
frontend/maxwell/translate/translate.h
|
||||
frontend/maxwell/translate_program.cpp
|
||||
frontend/maxwell/translate_program.h
|
||||
host_translate_info.h
|
||||
ir_opt/collect_shader_info_pass.cpp
|
||||
ir_opt/constant_propagation_pass.cpp
|
||||
ir_opt/dead_code_elimination_pass.cpp
|
||||
ir_opt/dual_vertex_pass.cpp
|
||||
ir_opt/global_memory_to_storage_buffer_pass.cpp
|
||||
ir_opt/identity_removal_pass.cpp
|
||||
ir_opt/lower_fp16_to_fp32.cpp
|
||||
ir_opt/lower_int64_to_int32.cpp
|
||||
ir_opt/passes.h
|
||||
ir_opt/ssa_rewrite_pass.cpp
|
||||
ir_opt/texture_pass.cpp
|
||||
ir_opt/verification_pass.cpp
|
||||
object_pool.h
|
||||
profile.h
|
||||
program_header.h
|
||||
runtime_info.h
|
||||
shader_info.h
|
||||
varying_state.h
|
||||
)
|
||||
|
||||
target_link_libraries(shader_recompiler PUBLIC common fmt::fmt sirit)
|
||||
|
||||
if (MSVC)
|
||||
target_compile_options(shader_recompiler PRIVATE
|
||||
/W4
|
||||
/WX
|
||||
/we4018 # 'expression' : signed/unsigned mismatch
|
||||
/we4244 # 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point)
|
||||
/we4245 # 'conversion' : conversion from 'type1' to 'type2', signed/unsigned mismatch
|
||||
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
||||
/we4267 # 'var' : conversion from 'size_t' to 'type', possible loss of data
|
||||
/we4305 # 'context' : truncation from 'type1' to 'type2'
|
||||
/we4800 # Implicit conversion from 'type' to bool. Possible information loss
|
||||
/we4826 # Conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior.
|
||||
)
|
||||
else()
|
||||
target_compile_options(shader_recompiler PRIVATE
|
||||
-Werror
|
||||
-Werror=conversion
|
||||
-Werror=ignored-qualifiers
|
||||
-Werror=implicit-fallthrough
|
||||
-Werror=shadow
|
||||
-Werror=sign-compare
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
|
||||
-Werror=unused-variable
|
||||
|
||||
# Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6.
|
||||
# And this in turns limits the size of a std::array.
|
||||
$<$<CXX_COMPILER_ID:Clang>:-fbracket-depth=1024>
|
||||
)
|
||||
endif()
|
||||
|
||||
create_target_directory_groups(shader_recompiler)
|
||||
19
src/shader_recompiler/backend/bindings.h
Normal file
19
src/shader_recompiler/backend/bindings.h
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Shader::Backend {
|
||||
|
||||
struct Bindings {
|
||||
u32 unified{};
|
||||
u32 uniform_buffer{};
|
||||
u32 storage_buffer{};
|
||||
u32 texture{};
|
||||
u32 image{};
|
||||
};
|
||||
|
||||
} // namespace Shader::Backend
|
||||
154
src/shader_recompiler/backend/glasm/emit_context.cpp
Normal file
154
src/shader_recompiler/backend/glasm/emit_context.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/bindings.h"
|
||||
#include "shader_recompiler/backend/glasm/emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
namespace {
|
||||
std::string_view InterpDecorator(Interpolation interp) {
|
||||
switch (interp) {
|
||||
case Interpolation::Smooth:
|
||||
return "";
|
||||
case Interpolation::Flat:
|
||||
return "FLAT ";
|
||||
case Interpolation::NoPerspective:
|
||||
return "NOPERSPECTIVE ";
|
||||
}
|
||||
throw InvalidArgument("Invalid interpolation {}", interp);
|
||||
}
|
||||
|
||||
bool IsInputArray(Stage stage) {
|
||||
return stage == Stage::Geometry || stage == Stage::TessellationControl ||
|
||||
stage == Stage::TessellationEval;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
|
||||
const RuntimeInfo& runtime_info_)
|
||||
: info{program.info}, profile{profile_}, runtime_info{runtime_info_} {
|
||||
// FIXME: Temporary partial implementation
|
||||
u32 cbuf_index{};
|
||||
for (const auto& desc : info.constant_buffer_descriptors) {
|
||||
if (desc.count != 1) {
|
||||
throw NotImplementedException("Constant buffer descriptor array");
|
||||
}
|
||||
Add("CBUFFER c{}[]={{program.buffer[{}]}};", desc.index, cbuf_index);
|
||||
++cbuf_index;
|
||||
}
|
||||
u32 ssbo_index{};
|
||||
for (const auto& desc : info.storage_buffers_descriptors) {
|
||||
if (desc.count != 1) {
|
||||
throw NotImplementedException("Storage buffer descriptor array");
|
||||
}
|
||||
if (runtime_info.glasm_use_storage_buffers) {
|
||||
Add("STORAGE ssbo{}[]={{program.storage[{}]}};", ssbo_index, bindings.storage_buffer);
|
||||
++bindings.storage_buffer;
|
||||
++ssbo_index;
|
||||
}
|
||||
}
|
||||
if (!runtime_info.glasm_use_storage_buffers) {
|
||||
if (const size_t num = info.storage_buffers_descriptors.size(); num > 0) {
|
||||
Add("PARAM c[{}]={{program.local[0..{}]}};", num, num - 1);
|
||||
}
|
||||
}
|
||||
stage = program.stage;
|
||||
switch (program.stage) {
|
||||
case Stage::VertexA:
|
||||
case Stage::VertexB:
|
||||
stage_name = "vertex";
|
||||
attrib_name = "vertex";
|
||||
break;
|
||||
case Stage::TessellationControl:
|
||||
case Stage::TessellationEval:
|
||||
stage_name = "primitive";
|
||||
attrib_name = "primitive";
|
||||
break;
|
||||
case Stage::Geometry:
|
||||
stage_name = "primitive";
|
||||
attrib_name = "vertex";
|
||||
break;
|
||||
case Stage::Fragment:
|
||||
stage_name = "fragment";
|
||||
attrib_name = "fragment";
|
||||
break;
|
||||
case Stage::Compute:
|
||||
stage_name = "invocation";
|
||||
break;
|
||||
}
|
||||
const std::string_view attr_stage{stage == Stage::Fragment ? "fragment" : "vertex"};
|
||||
const VaryingState loads{info.loads.mask | info.passthrough.mask};
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (loads.Generic(index)) {
|
||||
Add("{}ATTRIB in_attr{}[]={{{}.attrib[{}..{}]}};",
|
||||
InterpDecorator(info.interpolation[index]), index, attr_stage, index, index);
|
||||
}
|
||||
}
|
||||
if (IsInputArray(stage) && loads.AnyComponent(IR::Attribute::PositionX)) {
|
||||
Add("ATTRIB vertex_position=vertex.position;");
|
||||
}
|
||||
if (info.uses_invocation_id) {
|
||||
Add("ATTRIB primitive_invocation=primitive.invocation;");
|
||||
}
|
||||
if (info.stores_tess_level_outer) {
|
||||
Add("OUTPUT result_patch_tessouter[]={{result.patch.tessouter[0..3]}};");
|
||||
}
|
||||
if (info.stores_tess_level_inner) {
|
||||
Add("OUTPUT result_patch_tessinner[]={{result.patch.tessinner[0..1]}};");
|
||||
}
|
||||
if (info.stores.ClipDistances()) {
|
||||
Add("OUTPUT result_clip[]={{result.clip[0..7]}};");
|
||||
}
|
||||
for (size_t index = 0; index < info.uses_patches.size(); ++index) {
|
||||
if (!info.uses_patches[index]) {
|
||||
continue;
|
||||
}
|
||||
if (stage == Stage::TessellationControl) {
|
||||
Add("OUTPUT result_patch_attrib{}[]={{result.patch.attrib[{}..{}]}};"
|
||||
"ATTRIB primitive_out_patch_attrib{}[]={{primitive.out.patch.attrib[{}..{}]}};",
|
||||
index, index, index, index, index, index);
|
||||
} else {
|
||||
Add("ATTRIB primitive_patch_attrib{}[]={{primitive.patch.attrib[{}..{}]}};", index,
|
||||
index, index);
|
||||
}
|
||||
}
|
||||
if (stage == Stage::Fragment) {
|
||||
Add("OUTPUT frag_color0=result.color;");
|
||||
for (size_t index = 1; index < info.stores_frag_color.size(); ++index) {
|
||||
Add("OUTPUT frag_color{}=result.color[{}];", index, index);
|
||||
}
|
||||
}
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (info.stores.Generic(index)) {
|
||||
Add("OUTPUT out_attr{}[]={{result.attrib[{}..{}]}};", index, index, index);
|
||||
}
|
||||
}
|
||||
image_buffer_bindings.reserve(info.image_buffer_descriptors.size());
|
||||
for (const auto& desc : info.image_buffer_descriptors) {
|
||||
image_buffer_bindings.push_back(bindings.image);
|
||||
bindings.image += desc.count;
|
||||
}
|
||||
image_bindings.reserve(info.image_descriptors.size());
|
||||
for (const auto& desc : info.image_descriptors) {
|
||||
image_bindings.push_back(bindings.image);
|
||||
bindings.image += desc.count;
|
||||
}
|
||||
texture_buffer_bindings.reserve(info.texture_buffer_descriptors.size());
|
||||
for (const auto& desc : info.texture_buffer_descriptors) {
|
||||
texture_buffer_bindings.push_back(bindings.texture);
|
||||
bindings.texture += desc.count;
|
||||
}
|
||||
texture_bindings.reserve(info.texture_descriptors.size());
|
||||
for (const auto& desc : info.texture_descriptors) {
|
||||
texture_bindings.push_back(bindings.texture);
|
||||
bindings.texture += desc.count;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
80
src/shader_recompiler/backend/glasm/emit_context.h
Normal file
80
src/shader_recompiler/backend/glasm/emit_context.h
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/backend/glasm/reg_alloc.h"
|
||||
#include "shader_recompiler/stage.h"
|
||||
|
||||
namespace Shader {
|
||||
struct Info;
|
||||
struct Profile;
|
||||
struct RuntimeInfo;
|
||||
} // namespace Shader
|
||||
|
||||
namespace Shader::Backend {
|
||||
struct Bindings;
|
||||
}
|
||||
|
||||
namespace Shader::IR {
|
||||
class Inst;
|
||||
struct Program;
|
||||
} // namespace Shader::IR
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
class EmitContext {
|
||||
public:
|
||||
explicit EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
|
||||
const RuntimeInfo& runtime_info_);
|
||||
|
||||
template <typename... Args>
|
||||
void Add(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
code += fmt::format(fmt::runtime(format_str), reg_alloc.Define(inst),
|
||||
std::forward<Args>(args)...);
|
||||
// TODO: Remove this
|
||||
code += '\n';
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void LongAdd(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
code += fmt::format(fmt::runtime(format_str), reg_alloc.LongDefine(inst),
|
||||
std::forward<Args>(args)...);
|
||||
// TODO: Remove this
|
||||
code += '\n';
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void Add(const char* format_str, Args&&... args) {
|
||||
code += fmt::format(fmt::runtime(format_str), std::forward<Args>(args)...);
|
||||
// TODO: Remove this
|
||||
code += '\n';
|
||||
}
|
||||
|
||||
std::string code;
|
||||
RegAlloc reg_alloc{};
|
||||
const Info& info;
|
||||
const Profile& profile;
|
||||
const RuntimeInfo& runtime_info;
|
||||
|
||||
std::vector<u32> texture_buffer_bindings;
|
||||
std::vector<u32> image_buffer_bindings;
|
||||
std::vector<u32> texture_bindings;
|
||||
std::vector<u32> image_bindings;
|
||||
|
||||
Stage stage{};
|
||||
std::string_view stage_name = "invalid";
|
||||
std::string_view attrib_name = "invalid";
|
||||
|
||||
u32 num_safety_loop_vars{};
|
||||
bool uses_y_direction{};
|
||||
};
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
492
src/shader_recompiler/backend/glasm/emit_glasm.cpp
Normal file
492
src/shader_recompiler/backend/glasm/emit_glasm.cpp
Normal file
@@ -0,0 +1,492 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#include "common/div_ceil.h"
|
||||
#include "common/settings.h"
|
||||
#include "shader_recompiler/backend/bindings.h"
|
||||
#include "shader_recompiler/backend/glasm/emit_context.h"
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm.h"
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/frontend/ir/ir_emitter.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
namespace {
|
||||
template <class Func>
|
||||
struct FuncTraits {};
|
||||
|
||||
template <class ReturnType_, class... Args>
|
||||
struct FuncTraits<ReturnType_ (*)(Args...)> {
|
||||
using ReturnType = ReturnType_;
|
||||
|
||||
static constexpr size_t NUM_ARGS = sizeof...(Args);
|
||||
|
||||
template <size_t I>
|
||||
using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Identity {
|
||||
Identity(T data_) : data{data_} {}
|
||||
|
||||
T Extract() {
|
||||
return data;
|
||||
}
|
||||
|
||||
T data;
|
||||
};
|
||||
|
||||
template <bool scalar>
|
||||
class RegWrapper {
|
||||
public:
|
||||
RegWrapper(EmitContext& ctx, const IR::Value& ir_value) : reg_alloc{ctx.reg_alloc} {
|
||||
const Value value{reg_alloc.Peek(ir_value)};
|
||||
if (value.type == Type::Register) {
|
||||
inst = ir_value.InstRecursive();
|
||||
reg = Register{value};
|
||||
} else {
|
||||
reg = value.type == Type::U64 ? reg_alloc.AllocLongReg() : reg_alloc.AllocReg();
|
||||
}
|
||||
switch (value.type) {
|
||||
case Type::Register:
|
||||
case Type::Void:
|
||||
break;
|
||||
case Type::U32:
|
||||
ctx.Add("MOV.U {}.x,{};", reg, value.imm_u32);
|
||||
break;
|
||||
case Type::U64:
|
||||
ctx.Add("MOV.U64 {}.x,{};", reg, value.imm_u64);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto Extract() {
|
||||
if (inst) {
|
||||
reg_alloc.Unref(*inst);
|
||||
} else {
|
||||
reg_alloc.FreeReg(reg);
|
||||
}
|
||||
return std::conditional_t<scalar, ScalarRegister, Register>{Value{reg}};
|
||||
}
|
||||
|
||||
private:
|
||||
RegAlloc& reg_alloc;
|
||||
IR::Inst* inst{};
|
||||
Register reg{};
|
||||
};
|
||||
|
||||
template <typename ArgType>
|
||||
class ValueWrapper {
|
||||
public:
|
||||
ValueWrapper(EmitContext& ctx, const IR::Value& ir_value_)
|
||||
: reg_alloc{ctx.reg_alloc}, ir_value{ir_value_}, value{reg_alloc.Peek(ir_value)} {}
|
||||
|
||||
ArgType Extract() {
|
||||
if (!ir_value.IsImmediate()) {
|
||||
reg_alloc.Unref(*ir_value.InstRecursive());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
RegAlloc& reg_alloc;
|
||||
const IR::Value& ir_value;
|
||||
ArgType value;
|
||||
};
|
||||
|
||||
template <typename ArgType>
|
||||
auto Arg(EmitContext& ctx, const IR::Value& arg) {
|
||||
if constexpr (std::is_same_v<ArgType, Register>) {
|
||||
return RegWrapper<false>{ctx, arg};
|
||||
} else if constexpr (std::is_same_v<ArgType, ScalarRegister>) {
|
||||
return RegWrapper<true>{ctx, arg};
|
||||
} else if constexpr (std::is_base_of_v<Value, ArgType>) {
|
||||
return ValueWrapper<ArgType>{ctx, arg};
|
||||
} else if constexpr (std::is_same_v<ArgType, const IR::Value&>) {
|
||||
return Identity<const IR::Value&>{arg};
|
||||
} else if constexpr (std::is_same_v<ArgType, u32>) {
|
||||
return Identity{arg.U32()};
|
||||
} else if constexpr (std::is_same_v<ArgType, IR::Attribute>) {
|
||||
return Identity{arg.Attribute()};
|
||||
} else if constexpr (std::is_same_v<ArgType, IR::Patch>) {
|
||||
return Identity{arg.Patch()};
|
||||
} else if constexpr (std::is_same_v<ArgType, IR::Reg>) {
|
||||
return Identity{arg.Reg()};
|
||||
}
|
||||
}
|
||||
|
||||
template <auto func, bool is_first_arg_inst>
|
||||
struct InvokeCall {
|
||||
template <typename... Args>
|
||||
InvokeCall(EmitContext& ctx, IR::Inst* inst, Args&&... args) {
|
||||
if constexpr (is_first_arg_inst) {
|
||||
func(ctx, *inst, args.Extract()...);
|
||||
} else {
|
||||
func(ctx, args.Extract()...);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <auto func, bool is_first_arg_inst, size_t... I>
|
||||
void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
|
||||
using Traits = FuncTraits<decltype(func)>;
|
||||
if constexpr (is_first_arg_inst) {
|
||||
InvokeCall<func, is_first_arg_inst>{
|
||||
ctx, inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...};
|
||||
} else {
|
||||
InvokeCall<func, is_first_arg_inst>{
|
||||
ctx, inst, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...};
|
||||
}
|
||||
}
|
||||
|
||||
template <auto func>
|
||||
void Invoke(EmitContext& ctx, IR::Inst* inst) {
|
||||
using Traits = FuncTraits<decltype(func)>;
|
||||
static_assert(Traits::NUM_ARGS >= 1, "Insufficient arguments");
|
||||
if constexpr (Traits::NUM_ARGS == 1) {
|
||||
Invoke<func, false>(ctx, inst, std::make_index_sequence<0>{});
|
||||
} else {
|
||||
using FirstArgType = typename Traits::template ArgType<1>;
|
||||
static constexpr bool is_first_arg_inst = std::is_same_v<FirstArgType, IR::Inst&>;
|
||||
using Indices = std::make_index_sequence<Traits::NUM_ARGS - (is_first_arg_inst ? 2 : 1)>;
|
||||
Invoke<func, is_first_arg_inst>(ctx, inst, Indices{});
|
||||
}
|
||||
}
|
||||
|
||||
void EmitInst(EmitContext& ctx, IR::Inst* inst) {
|
||||
switch (inst->GetOpcode()) {
|
||||
#define OPCODE(name, result_type, ...) \
|
||||
case IR::Opcode::name: \
|
||||
return Invoke<&Emit##name>(ctx, inst);
|
||||
#include "shader_recompiler/frontend/ir/opcodes.inc"
|
||||
#undef OPCODE
|
||||
}
|
||||
throw LogicError("Invalid opcode {}", inst->GetOpcode());
|
||||
}
|
||||
|
||||
bool IsReference(IR::Inst& inst) {
|
||||
return inst.GetOpcode() == IR::Opcode::Reference;
|
||||
}
|
||||
|
||||
void PrecolorInst(IR::Inst& phi) {
|
||||
// Insert phi moves before references to avoid overwritting other phis
|
||||
const size_t num_args{phi.NumArgs()};
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
IR::Block& phi_block{*phi.PhiBlock(i)};
|
||||
auto it{std::find_if_not(phi_block.rbegin(), phi_block.rend(), IsReference).base()};
|
||||
IR::IREmitter ir{phi_block, it};
|
||||
const IR::Value arg{phi.Arg(i)};
|
||||
if (arg.IsImmediate()) {
|
||||
ir.PhiMove(phi, arg);
|
||||
} else {
|
||||
ir.PhiMove(phi, IR::Value{&RegAlloc::AliasInst(*arg.Inst())});
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
IR::IREmitter{*phi.PhiBlock(i)}.Reference(IR::Value{&phi});
|
||||
}
|
||||
}
|
||||
|
||||
void Precolor(const IR::Program& program) {
|
||||
for (IR::Block* const block : program.blocks) {
|
||||
for (IR::Inst& phi : block->Instructions()) {
|
||||
if (!IR::IsPhi(phi)) {
|
||||
break;
|
||||
}
|
||||
PrecolorInst(phi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmitCode(EmitContext& ctx, const IR::Program& program) {
|
||||
const auto eval{
|
||||
[&](const IR::U1& cond) { return ScalarS32{ctx.reg_alloc.Consume(IR::Value{cond})}; }};
|
||||
for (const IR::AbstractSyntaxNode& node : program.syntax_list) {
|
||||
switch (node.type) {
|
||||
case IR::AbstractSyntaxNode::Type::Block:
|
||||
for (IR::Inst& inst : node.data.block->Instructions()) {
|
||||
EmitInst(ctx, &inst);
|
||||
}
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::If:
|
||||
ctx.Add("MOV.S.CC RC,{};"
|
||||
"IF NE.x;",
|
||||
eval(node.data.if_node.cond));
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::EndIf:
|
||||
ctx.Add("ENDIF;");
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Loop:
|
||||
ctx.Add("REP;");
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Repeat:
|
||||
if (!Settings::values.disable_shader_loop_safety_checks) {
|
||||
const u32 loop_index{ctx.num_safety_loop_vars++};
|
||||
const u32 vector_index{loop_index / 4};
|
||||
const char component{"xyzw"[loop_index % 4]};
|
||||
ctx.Add("SUB.S.CC loop{}.{},loop{}.{},1;"
|
||||
"BRK(LT.{});",
|
||||
vector_index, component, vector_index, component, component);
|
||||
}
|
||||
if (node.data.repeat.cond.IsImmediate()) {
|
||||
if (node.data.repeat.cond.U1()) {
|
||||
ctx.Add("ENDREP;");
|
||||
} else {
|
||||
ctx.Add("BRK;"
|
||||
"ENDREP;");
|
||||
}
|
||||
} else {
|
||||
ctx.Add("MOV.S.CC RC,{};"
|
||||
"BRK(EQ.x);"
|
||||
"ENDREP;",
|
||||
eval(node.data.repeat.cond));
|
||||
}
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Break:
|
||||
if (node.data.break_node.cond.IsImmediate()) {
|
||||
if (node.data.break_node.cond.U1()) {
|
||||
ctx.Add("BRK;");
|
||||
}
|
||||
} else {
|
||||
ctx.Add("MOV.S.CC RC,{};"
|
||||
"BRK (NE.x);",
|
||||
eval(node.data.break_node.cond));
|
||||
}
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Return:
|
||||
case IR::AbstractSyntaxNode::Type::Unreachable:
|
||||
ctx.Add("RET;");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ctx.reg_alloc.IsEmpty()) {
|
||||
LOG_WARNING(Shader_GLASM, "Register leak after generating code");
|
||||
}
|
||||
}
|
||||
|
||||
void SetupOptions(const IR::Program& program, const Profile& profile,
|
||||
const RuntimeInfo& runtime_info, std::string& header) {
|
||||
const Info& info{program.info};
|
||||
const Stage stage{program.stage};
|
||||
|
||||
// TODO: Track the shared atomic ops
|
||||
header += "OPTION NV_internal;"
|
||||
"OPTION NV_shader_storage_buffer;"
|
||||
"OPTION NV_gpu_program_fp64;";
|
||||
if (info.uses_int64_bit_atomics) {
|
||||
header += "OPTION NV_shader_atomic_int64;";
|
||||
}
|
||||
if (info.uses_atomic_f32_add) {
|
||||
header += "OPTION NV_shader_atomic_float;";
|
||||
}
|
||||
if (info.uses_atomic_f16x2_add || info.uses_atomic_f16x2_min || info.uses_atomic_f16x2_max) {
|
||||
header += "OPTION NV_shader_atomic_fp16_vector;";
|
||||
}
|
||||
if (info.uses_subgroup_invocation_id || info.uses_subgroup_mask || info.uses_subgroup_vote ||
|
||||
info.uses_fswzadd) {
|
||||
header += "OPTION NV_shader_thread_group;";
|
||||
}
|
||||
if (info.uses_subgroup_shuffles) {
|
||||
header += "OPTION NV_shader_thread_shuffle;";
|
||||
}
|
||||
if (info.uses_sparse_residency) {
|
||||
header += "OPTION EXT_sparse_texture2;";
|
||||
}
|
||||
const bool stores_viewport_layer{info.stores[IR::Attribute::ViewportIndex] ||
|
||||
info.stores[IR::Attribute::Layer]};
|
||||
if ((stage != Stage::Geometry && stores_viewport_layer) ||
|
||||
info.stores[IR::Attribute::ViewportMask]) {
|
||||
if (profile.support_viewport_index_layer_non_geometry) {
|
||||
header += "OPTION NV_viewport_array2;";
|
||||
}
|
||||
}
|
||||
if (program.is_geometry_passthrough && profile.support_geometry_shader_passthrough) {
|
||||
header += "OPTION NV_geometry_shader_passthrough;";
|
||||
}
|
||||
if (info.uses_typeless_image_reads && profile.support_typeless_image_loads) {
|
||||
header += "OPTION EXT_shader_image_load_formatted;";
|
||||
}
|
||||
if (profile.support_derivative_control) {
|
||||
header += "OPTION ARB_derivative_control;";
|
||||
}
|
||||
if (stage == Stage::Fragment && runtime_info.force_early_z != 0) {
|
||||
header += "OPTION NV_early_fragment_tests;";
|
||||
}
|
||||
if (stage == Stage::Fragment) {
|
||||
header += "OPTION ARB_draw_buffers;";
|
||||
}
|
||||
}
|
||||
|
||||
std::string_view StageHeader(Stage stage) {
|
||||
switch (stage) {
|
||||
case Stage::VertexA:
|
||||
case Stage::VertexB:
|
||||
return "!!NVvp5.0\n";
|
||||
case Stage::TessellationControl:
|
||||
return "!!NVtcp5.0\n";
|
||||
case Stage::TessellationEval:
|
||||
return "!!NVtep5.0\n";
|
||||
case Stage::Geometry:
|
||||
return "!!NVgp5.0\n";
|
||||
case Stage::Fragment:
|
||||
return "!!NVfp5.0\n";
|
||||
case Stage::Compute:
|
||||
return "!!NVcp5.0\n";
|
||||
}
|
||||
throw InvalidArgument("Invalid stage {}", stage);
|
||||
}
|
||||
|
||||
std::string_view InputPrimitive(InputTopology topology) {
|
||||
switch (topology) {
|
||||
case InputTopology::Points:
|
||||
return "POINTS";
|
||||
case InputTopology::Lines:
|
||||
return "LINES";
|
||||
case InputTopology::LinesAdjacency:
|
||||
return "LINES_ADJACENCY";
|
||||
case InputTopology::Triangles:
|
||||
return "TRIANGLES";
|
||||
case InputTopology::TrianglesAdjacency:
|
||||
return "TRIANGLES_ADJACENCY";
|
||||
}
|
||||
throw InvalidArgument("Invalid input topology {}", topology);
|
||||
}
|
||||
|
||||
std::string_view OutputPrimitive(OutputTopology topology) {
|
||||
switch (topology) {
|
||||
case OutputTopology::PointList:
|
||||
return "POINTS";
|
||||
case OutputTopology::LineStrip:
|
||||
return "LINE_STRIP";
|
||||
case OutputTopology::TriangleStrip:
|
||||
return "TRIANGLE_STRIP";
|
||||
}
|
||||
throw InvalidArgument("Invalid output topology {}", topology);
|
||||
}
|
||||
|
||||
std::string_view GetTessMode(TessPrimitive primitive) {
|
||||
switch (primitive) {
|
||||
case TessPrimitive::Triangles:
|
||||
return "TRIANGLES";
|
||||
case TessPrimitive::Quads:
|
||||
return "QUADS";
|
||||
case TessPrimitive::Isolines:
|
||||
return "ISOLINES";
|
||||
}
|
||||
throw InvalidArgument("Invalid tessellation primitive {}", primitive);
|
||||
}
|
||||
|
||||
std::string_view GetTessSpacing(TessSpacing spacing) {
|
||||
switch (spacing) {
|
||||
case TessSpacing::Equal:
|
||||
return "EQUAL";
|
||||
case TessSpacing::FractionalOdd:
|
||||
return "FRACTIONAL_ODD";
|
||||
case TessSpacing::FractionalEven:
|
||||
return "FRACTIONAL_EVEN";
|
||||
}
|
||||
throw InvalidArgument("Invalid tessellation spacing {}", spacing);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program,
|
||||
Bindings& bindings) {
|
||||
EmitContext ctx{program, bindings, profile, runtime_info};
|
||||
Precolor(program);
|
||||
EmitCode(ctx, program);
|
||||
std::string header{StageHeader(program.stage)};
|
||||
SetupOptions(program, profile, runtime_info, header);
|
||||
switch (program.stage) {
|
||||
case Stage::TessellationControl:
|
||||
header += fmt::format("VERTICES_OUT {};", program.invocations);
|
||||
break;
|
||||
case Stage::TessellationEval:
|
||||
header += fmt::format("TESS_MODE {};"
|
||||
"TESS_SPACING {};"
|
||||
"TESS_VERTEX_ORDER {};",
|
||||
GetTessMode(runtime_info.tess_primitive),
|
||||
GetTessSpacing(runtime_info.tess_spacing),
|
||||
runtime_info.tess_clockwise ? "CW" : "CCW");
|
||||
break;
|
||||
case Stage::Geometry:
|
||||
header += fmt::format("PRIMITIVE_IN {};", InputPrimitive(runtime_info.input_topology));
|
||||
if (program.is_geometry_passthrough) {
|
||||
if (profile.support_geometry_shader_passthrough) {
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (program.info.passthrough.Generic(index)) {
|
||||
header += fmt::format("PASSTHROUGH result.attrib[{}];", index);
|
||||
}
|
||||
}
|
||||
if (program.info.passthrough.AnyComponent(IR::Attribute::PositionX)) {
|
||||
header += "PASSTHROUGH result.position;";
|
||||
}
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLASM, "Passthrough geometry program used but not supported");
|
||||
}
|
||||
} else {
|
||||
header +=
|
||||
fmt::format("VERTICES_OUT {};"
|
||||
"PRIMITIVE_OUT {};",
|
||||
program.output_vertices, OutputPrimitive(program.output_topology));
|
||||
}
|
||||
break;
|
||||
case Stage::Compute:
|
||||
header += fmt::format("GROUP_SIZE {} {} {};", program.workgroup_size[0],
|
||||
program.workgroup_size[1], program.workgroup_size[2]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (program.shared_memory_size > 0) {
|
||||
header += fmt::format("SHARED_MEMORY {};", program.shared_memory_size);
|
||||
header += fmt::format("SHARED shared_mem[]={{program.sharedmem}};");
|
||||
}
|
||||
header += "TEMP ";
|
||||
for (size_t index = 0; index < ctx.reg_alloc.NumUsedRegisters(); ++index) {
|
||||
header += fmt::format("R{},", index);
|
||||
}
|
||||
if (program.local_memory_size > 0) {
|
||||
header += fmt::format("lmem[{}],", program.local_memory_size);
|
||||
}
|
||||
if (program.info.uses_fswzadd) {
|
||||
header += "FSWZA[4],FSWZB[4],";
|
||||
}
|
||||
const u32 num_safety_loop_vectors{Common::DivCeil(ctx.num_safety_loop_vars, 4u)};
|
||||
for (u32 index = 0; index < num_safety_loop_vectors; ++index) {
|
||||
header += fmt::format("loop{},", index);
|
||||
}
|
||||
header += "RC;"
|
||||
"LONG TEMP ";
|
||||
for (size_t index = 0; index < ctx.reg_alloc.NumUsedLongRegisters(); ++index) {
|
||||
header += fmt::format("D{},", index);
|
||||
}
|
||||
header += "DC;";
|
||||
if (program.info.uses_fswzadd) {
|
||||
header += "MOV.F FSWZA[0],-1;"
|
||||
"MOV.F FSWZA[1],1;"
|
||||
"MOV.F FSWZA[2],-1;"
|
||||
"MOV.F FSWZA[3],0;"
|
||||
"MOV.F FSWZB[0],-1;"
|
||||
"MOV.F FSWZB[1],-1;"
|
||||
"MOV.F FSWZB[2],1;"
|
||||
"MOV.F FSWZB[3],-1;";
|
||||
}
|
||||
for (u32 index = 0; index < num_safety_loop_vectors; ++index) {
|
||||
header += fmt::format("MOV.S loop{},{{0x2000,0x2000,0x2000,0x2000}};", index);
|
||||
}
|
||||
if (ctx.uses_y_direction) {
|
||||
header += "PARAM y_direction[1]={state.material.front.ambient};";
|
||||
}
|
||||
ctx.code.insert(0, header);
|
||||
ctx.code += "END";
|
||||
return ctx.code;
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
25
src/shader_recompiler/backend/glasm/emit_glasm.h
Normal file
25
src/shader_recompiler/backend/glasm/emit_glasm.h
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "shader_recompiler/backend/bindings.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
[[nodiscard]] std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||
IR::Program& program, Bindings& bindings);
|
||||
|
||||
[[nodiscard]] inline std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||
IR::Program& program) {
|
||||
Bindings binding;
|
||||
return EmitGLASM(profile, runtime_info, program, binding);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_context.h"
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
static void Alias(IR::Inst& inst, const IR::Value& value) {
|
||||
if (value.IsImmediate()) {
|
||||
return;
|
||||
}
|
||||
IR::Inst& value_inst{RegAlloc::AliasInst(*value.Inst())};
|
||||
value_inst.DestructiveAddUsage(inst.UseCount());
|
||||
value_inst.DestructiveRemoveUsage();
|
||||
inst.SetDefinition(value_inst.Definition<Id>());
|
||||
}
|
||||
|
||||
void EmitIdentity(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitConditionRef(EmitContext& ctx, IR::Inst& inst, const IR::Value& value) {
|
||||
// Fake one usage to get a real register out of the condition
|
||||
inst.DestructiveAddUsage(1);
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
const ScalarS32 input{ctx.reg_alloc.Consume(value)};
|
||||
if (ret != input) {
|
||||
ctx.Add("MOV.S {},{};", ret, input);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitBitCastU16F16(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastU32F32(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastU64F64(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastF16U16(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastF32U32(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCastF64U64(EmitContext&, IR::Inst& inst, const IR::Value& value) {
|
||||
Alias(inst, value);
|
||||
}
|
||||
|
||||
void EmitPackUint2x32(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
ctx.LongAdd("PK64.U {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackUint2x32(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
ctx.Add("UP64.U {}.xy,{}.x;", inst, value);
|
||||
}
|
||||
|
||||
void EmitPackFloat2x16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitUnpackFloat2x16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitPackHalf2x16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
ctx.Add("PK2H {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackHalf2x16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
ctx.Add("UP2H {}.xy,{}.x;", inst, value);
|
||||
}
|
||||
|
||||
void EmitPackDouble2x32(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
ctx.LongAdd("PK64 {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitUnpackDouble2x32(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
ctx.Add("UP64 {}.xy,{}.x;", inst, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
244
src/shader_recompiler/backend/glasm/emit_glasm_composite.cpp
Normal file
244
src/shader_recompiler/backend/glasm/emit_glasm_composite.cpp
Normal file
@@ -0,0 +1,244 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_context.h"
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
namespace {
|
||||
template <auto read_imm, char type, typename... Values>
|
||||
void CompositeConstruct(EmitContext& ctx, IR::Inst& inst, Values&&... elements) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (std::ranges::any_of(std::array{elements...},
|
||||
[](const IR::Value& value) { return value.IsImmediate(); })) {
|
||||
using Type = std::invoke_result_t<decltype(read_imm), IR::Value>;
|
||||
const std::array<Type, 4> values{(elements.IsImmediate() ? (elements.*read_imm)() : 0)...};
|
||||
ctx.Add("MOV.{} {},{{{},{},{},{}}};", type, ret, fmt::to_string(values[0]),
|
||||
fmt::to_string(values[1]), fmt::to_string(values[2]), fmt::to_string(values[3]));
|
||||
}
|
||||
size_t index{};
|
||||
for (const IR::Value& element : {elements...}) {
|
||||
if (!element.IsImmediate()) {
|
||||
const ScalarU32 value{ctx.reg_alloc.Consume(element)};
|
||||
ctx.Add("MOV.{} {}.{},{};", type, ret, "xyzw"[index], value);
|
||||
}
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
void CompositeExtract(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index, char type) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (ret == composite && index == 0) {
|
||||
// No need to do anything here, the source and destination are the same register
|
||||
return;
|
||||
}
|
||||
ctx.Add("MOV.{} {}.x,{}.{};", type, ret, composite, "xyzw"[index]);
|
||||
}
|
||||
|
||||
template <typename ObjectType>
|
||||
void CompositeInsert(EmitContext& ctx, IR::Inst& inst, Register composite, ObjectType object,
|
||||
u32 index, char type) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
const char swizzle{"xyzw"[index]};
|
||||
if (ret != composite && ret == object) {
|
||||
// The object is aliased with the return value, so we have to use a temporary to insert
|
||||
ctx.Add("MOV.{} RC,{};"
|
||||
"MOV.{} RC.{},{};"
|
||||
"MOV.{} {},RC;",
|
||||
type, composite, type, swizzle, object, type, ret);
|
||||
} else if (ret != composite) {
|
||||
// The input composite is not aliased with the return value so we have to copy it before
|
||||
// hand. But the insert object is not aliased with the return value, so we don't have to
|
||||
// worry about that
|
||||
ctx.Add("MOV.{} {},{};"
|
||||
"MOV.{} {}.{},{};",
|
||||
type, ret, composite, type, ret, swizzle, object);
|
||||
} else {
|
||||
// The return value is alised so we can just insert the object, it doesn't matter if it's
|
||||
// aliased
|
||||
ctx.Add("MOV.{} {}.{},{};", type, ret, swizzle, object);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitCompositeConstructU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
|
||||
const IR::Value& e2) {
|
||||
CompositeConstruct<&IR::Value::U32, 'U'>(ctx, inst, e1, e2);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructU32x3(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
|
||||
const IR::Value& e2, const IR::Value& e3) {
|
||||
CompositeConstruct<&IR::Value::U32, 'U'>(ctx, inst, e1, e2, e3);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructU32x4(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
|
||||
const IR::Value& e2, const IR::Value& e3, const IR::Value& e4) {
|
||||
CompositeConstruct<&IR::Value::U32, 'U'>(ctx, inst, e1, e2, e3, e4);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractU32x2(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index) {
|
||||
CompositeExtract(ctx, inst, composite, index, 'U');
|
||||
}
|
||||
|
||||
void EmitCompositeExtractU32x3(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index) {
|
||||
CompositeExtract(ctx, inst, composite, index, 'U');
|
||||
}
|
||||
|
||||
void EmitCompositeExtractU32x4(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index) {
|
||||
CompositeExtract(ctx, inst, composite, index, 'U');
|
||||
}
|
||||
|
||||
void EmitCompositeInsertU32x2([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite,
|
||||
[[maybe_unused]] ScalarU32 object, [[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeInsertU32x3([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite,
|
||||
[[maybe_unused]] ScalarU32 object, [[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeInsertU32x4([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite,
|
||||
[[maybe_unused]] ScalarU32 object, [[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF16x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register e1,
|
||||
[[maybe_unused]] Register e2) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF16x3([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register e1,
|
||||
[[maybe_unused]] Register e2, [[maybe_unused]] Register e3) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF16x4([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register e1,
|
||||
[[maybe_unused]] Register e2, [[maybe_unused]] Register e3,
|
||||
[[maybe_unused]] Register e4) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF16x2([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF16x3([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF16x4([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF16x2([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] Register object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF16x3([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] Register object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF16x4([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] Register object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
|
||||
const IR::Value& e2) {
|
||||
CompositeConstruct<&IR::Value::F32, 'F'>(ctx, inst, e1, e2);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF32x3(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
|
||||
const IR::Value& e2, const IR::Value& e3) {
|
||||
CompositeConstruct<&IR::Value::F32, 'F'>(ctx, inst, e1, e2, e3);
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF32x4(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
|
||||
const IR::Value& e2, const IR::Value& e3, const IR::Value& e4) {
|
||||
CompositeConstruct<&IR::Value::F32, 'F'>(ctx, inst, e1, e2, e3, e4);
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF32x2(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index) {
|
||||
CompositeExtract(ctx, inst, composite, index, 'F');
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF32x3(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index) {
|
||||
CompositeExtract(ctx, inst, composite, index, 'F');
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF32x4(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index) {
|
||||
CompositeExtract(ctx, inst, composite, index, 'F');
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF32x2(EmitContext& ctx, IR::Inst& inst, Register composite,
|
||||
ScalarF32 object, u32 index) {
|
||||
CompositeInsert(ctx, inst, composite, object, index, 'F');
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF32x3(EmitContext& ctx, IR::Inst& inst, Register composite,
|
||||
ScalarF32 object, u32 index) {
|
||||
CompositeInsert(ctx, inst, composite, object, index, 'F');
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF32x4(EmitContext& ctx, IR::Inst& inst, Register composite,
|
||||
ScalarF32 object, u32 index) {
|
||||
CompositeInsert(ctx, inst, composite, object, index, 'F');
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x2([[maybe_unused]] EmitContext& ctx) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x3([[maybe_unused]] EmitContext& ctx) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeConstructF64x4([[maybe_unused]] EmitContext& ctx) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x2([[maybe_unused]] EmitContext& ctx) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x3([[maybe_unused]] EmitContext& ctx) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeExtractF64x4([[maybe_unused]] EmitContext& ctx) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF64x2([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] Register object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF64x3([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] Register object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitCompositeInsertF64x4([[maybe_unused]] EmitContext& ctx,
|
||||
[[maybe_unused]] Register composite, [[maybe_unused]] Register object,
|
||||
[[maybe_unused]] u32 index) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
@@ -0,0 +1,346 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_context.h"
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/shader_info.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
namespace {
|
||||
void GetCbuf(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset,
|
||||
std::string_view size) {
|
||||
if (!binding.IsImmediate()) {
|
||||
throw NotImplementedException("Indirect constant buffer loading");
|
||||
}
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (offset.type == Type::U32) {
|
||||
// Avoid reading arrays out of bounds, matching hardware's behavior
|
||||
if (offset.imm_u32 >= 0x10'000) {
|
||||
ctx.Add("MOV.S {},0;", ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ctx.Add("LDC.{} {},c{}[{}];", size, ret, binding.U32(), offset);
|
||||
}
|
||||
|
||||
bool IsInputArray(Stage stage) {
|
||||
return stage == Stage::Geometry || stage == Stage::TessellationControl ||
|
||||
stage == Stage::TessellationEval;
|
||||
}
|
||||
|
||||
std::string VertexIndex(EmitContext& ctx, ScalarU32 vertex) {
|
||||
return IsInputArray(ctx.stage) ? fmt::format("[{}]", vertex) : "";
|
||||
}
|
||||
|
||||
u32 TexCoordIndex(IR::Attribute attr) {
|
||||
return (static_cast<u32>(attr) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset) {
|
||||
GetCbuf(ctx, inst, binding, offset, "U8");
|
||||
}
|
||||
|
||||
void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset) {
|
||||
GetCbuf(ctx, inst, binding, offset, "S8");
|
||||
}
|
||||
|
||||
void EmitGetCbufU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset) {
|
||||
GetCbuf(ctx, inst, binding, offset, "U16");
|
||||
}
|
||||
|
||||
void EmitGetCbufS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset) {
|
||||
GetCbuf(ctx, inst, binding, offset, "S16");
|
||||
}
|
||||
|
||||
void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset) {
|
||||
GetCbuf(ctx, inst, binding, offset, "U32");
|
||||
}
|
||||
|
||||
void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset) {
|
||||
GetCbuf(ctx, inst, binding, offset, "F32");
|
||||
}
|
||||
|
||||
void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset) {
|
||||
GetCbuf(ctx, inst, binding, offset, "U32X2");
|
||||
}
|
||||
|
||||
void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32 vertex) {
|
||||
const u32 element{static_cast<u32>(attr) % 4};
|
||||
const char swizzle{"xyzw"[element]};
|
||||
if (IR::IsGeneric(attr)) {
|
||||
const u32 index{IR::GenericAttributeIndex(attr)};
|
||||
ctx.Add("MOV.F {}.x,in_attr{}{}[0].{};", inst, index, VertexIndex(ctx, vertex), swizzle);
|
||||
return;
|
||||
}
|
||||
if (attr >= IR::Attribute::FixedFncTexture0S && attr <= IR::Attribute::FixedFncTexture9Q) {
|
||||
const u32 index{TexCoordIndex(attr)};
|
||||
ctx.Add("MOV.F {}.x,{}.texcoord[{}].{};", inst, ctx.attrib_name, index, swizzle);
|
||||
return;
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.Add("MOV.S {}.x,primitive.id;", inst);
|
||||
break;
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
case IR::Attribute::PositionZ:
|
||||
case IR::Attribute::PositionW:
|
||||
if (IsInputArray(ctx.stage)) {
|
||||
ctx.Add("MOV.F {}.x,vertex_position{}.{};", inst, VertexIndex(ctx, vertex), swizzle);
|
||||
} else {
|
||||
ctx.Add("MOV.F {}.x,{}.position.{};", inst, ctx.attrib_name, swizzle);
|
||||
}
|
||||
break;
|
||||
case IR::Attribute::ColorFrontDiffuseR:
|
||||
case IR::Attribute::ColorFrontDiffuseG:
|
||||
case IR::Attribute::ColorFrontDiffuseB:
|
||||
case IR::Attribute::ColorFrontDiffuseA:
|
||||
ctx.Add("MOV.F {}.x,{}.color.{};", inst, ctx.attrib_name, swizzle);
|
||||
break;
|
||||
case IR::Attribute::PointSpriteS:
|
||||
case IR::Attribute::PointSpriteT:
|
||||
ctx.Add("MOV.F {}.x,{}.pointcoord.{};", inst, ctx.attrib_name, swizzle);
|
||||
break;
|
||||
case IR::Attribute::TessellationEvaluationPointU:
|
||||
case IR::Attribute::TessellationEvaluationPointV:
|
||||
ctx.Add("MOV.F {}.x,vertex.tesscoord.{};", inst, swizzle);
|
||||
break;
|
||||
case IR::Attribute::InstanceId:
|
||||
ctx.Add("MOV.S {}.x,{}.instance;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.Add("MOV.S {}.x,{}.id;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::FrontFace:
|
||||
ctx.Add("CMP.S {}.x,{}.facing.x,0,-1;", inst, ctx.attrib_name);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, ScalarF32 value,
|
||||
[[maybe_unused]] ScalarU32 vertex) {
|
||||
const u32 element{static_cast<u32>(attr) % 4};
|
||||
const char swizzle{"xyzw"[element]};
|
||||
if (IR::IsGeneric(attr)) {
|
||||
const u32 index{IR::GenericAttributeIndex(attr)};
|
||||
ctx.Add("MOV.F out_attr{}[0].{},{};", index, swizzle, value);
|
||||
return;
|
||||
}
|
||||
if (attr >= IR::Attribute::FixedFncTexture0S && attr <= IR::Attribute::FixedFncTexture9R) {
|
||||
const u32 index{TexCoordIndex(attr)};
|
||||
ctx.Add("MOV.F result.texcoord[{}].{},{};", index, swizzle, value);
|
||||
return;
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::Layer:
|
||||
if (ctx.stage == Stage::Geometry || ctx.profile.support_viewport_index_layer_non_geometry) {
|
||||
ctx.Add("MOV.F result.layer.x,{};", value);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLASM,
|
||||
"Layer stored outside of geometry shader not supported by device");
|
||||
}
|
||||
break;
|
||||
case IR::Attribute::ViewportIndex:
|
||||
if (ctx.stage == Stage::Geometry || ctx.profile.support_viewport_index_layer_non_geometry) {
|
||||
ctx.Add("MOV.F result.viewport.x,{};", value);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLASM,
|
||||
"Viewport stored outside of geometry shader not supported by device");
|
||||
}
|
||||
break;
|
||||
case IR::Attribute::ViewportMask:
|
||||
// NV_viewport_array2 is required to access result.viewportmask, regardless of shader stage.
|
||||
if (ctx.profile.support_viewport_index_layer_non_geometry) {
|
||||
ctx.Add("MOV.F result.viewportmask[0].x,{};", value);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLASM, "Device does not support storing to ViewportMask");
|
||||
}
|
||||
break;
|
||||
case IR::Attribute::PointSize:
|
||||
ctx.Add("MOV.F result.pointsize.x,{};", value);
|
||||
break;
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
case IR::Attribute::PositionZ:
|
||||
case IR::Attribute::PositionW:
|
||||
ctx.Add("MOV.F result.position.{},{};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ColorFrontDiffuseR:
|
||||
case IR::Attribute::ColorFrontDiffuseG:
|
||||
case IR::Attribute::ColorFrontDiffuseB:
|
||||
case IR::Attribute::ColorFrontDiffuseA:
|
||||
ctx.Add("MOV.F result.color.{},{};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ColorFrontSpecularR:
|
||||
case IR::Attribute::ColorFrontSpecularG:
|
||||
case IR::Attribute::ColorFrontSpecularB:
|
||||
case IR::Attribute::ColorFrontSpecularA:
|
||||
ctx.Add("MOV.F result.color.secondary.{},{};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ColorBackDiffuseR:
|
||||
case IR::Attribute::ColorBackDiffuseG:
|
||||
case IR::Attribute::ColorBackDiffuseB:
|
||||
case IR::Attribute::ColorBackDiffuseA:
|
||||
ctx.Add("MOV.F result.color.back.{},{};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ColorBackSpecularR:
|
||||
case IR::Attribute::ColorBackSpecularG:
|
||||
case IR::Attribute::ColorBackSpecularB:
|
||||
case IR::Attribute::ColorBackSpecularA:
|
||||
ctx.Add("MOV.F result.color.back.secondary.{},{};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::FogCoordinate:
|
||||
ctx.Add("MOV.F result.fogcoord.x,{};", value);
|
||||
break;
|
||||
case IR::Attribute::ClipDistance0:
|
||||
case IR::Attribute::ClipDistance1:
|
||||
case IR::Attribute::ClipDistance2:
|
||||
case IR::Attribute::ClipDistance3:
|
||||
case IR::Attribute::ClipDistance4:
|
||||
case IR::Attribute::ClipDistance5:
|
||||
case IR::Attribute::ClipDistance6:
|
||||
case IR::Attribute::ClipDistance7: {
|
||||
const u32 index{static_cast<u32>(attr) - static_cast<u32>(IR::Attribute::ClipDistance0)};
|
||||
ctx.Add("MOV.F result.clip[{}].x,{};", index, value);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw NotImplementedException("Set attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, ScalarS32 offset, ScalarU32 vertex) {
|
||||
// RC.x = base_index
|
||||
// RC.y = masked_index
|
||||
// RC.z = compare_index
|
||||
ctx.Add("SHR.S RC.x,{},2;"
|
||||
"AND.S RC.y,RC.x,3;"
|
||||
"SHR.S RC.z,{},4;",
|
||||
offset, offset);
|
||||
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
u32 num_endifs{};
|
||||
const auto read{[&](u32 compare_index, const std::array<std::string, 4>& values) {
|
||||
++num_endifs;
|
||||
ctx.Add("SEQ.S.CC RC.w,RC.z,{};" // compare_index
|
||||
"IF NE.w;"
|
||||
// X
|
||||
"SEQ.S.CC RC.w,RC.y,0;"
|
||||
"IF NE.w;"
|
||||
"MOV {}.x,{};"
|
||||
"ELSE;"
|
||||
// Y
|
||||
"SEQ.S.CC RC.w,RC.y,1;"
|
||||
"IF NE.w;"
|
||||
"MOV {}.x,{};"
|
||||
"ELSE;"
|
||||
// Z
|
||||
"SEQ.S.CC RC.w,RC.y,2;"
|
||||
"IF NE.w;"
|
||||
"MOV {}.x,{};"
|
||||
"ELSE;"
|
||||
// W
|
||||
"MOV {}.x,{};"
|
||||
"ENDIF;"
|
||||
"ENDIF;"
|
||||
"ENDIF;"
|
||||
"ELSE;",
|
||||
compare_index, ret, values[0], ret, values[1], ret, values[2], ret, values[3]);
|
||||
}};
|
||||
const auto read_swizzled{[&](u32 compare_index, std::string_view value) {
|
||||
const std::array values{fmt::format("{}.x", value), fmt::format("{}.y", value),
|
||||
fmt::format("{}.z", value), fmt::format("{}.w", value)};
|
||||
read(compare_index, values);
|
||||
}};
|
||||
if (ctx.info.loads.AnyComponent(IR::Attribute::PositionX)) {
|
||||
const u32 index{static_cast<u32>(IR::Attribute::PositionX)};
|
||||
if (IsInputArray(ctx.stage)) {
|
||||
read_swizzled(index, fmt::format("vertex_position{}", VertexIndex(ctx, vertex)));
|
||||
} else {
|
||||
read_swizzled(index, fmt::format("{}.position", ctx.attrib_name));
|
||||
}
|
||||
}
|
||||
for (u32 index = 0; index < static_cast<u32>(IR::NUM_GENERICS); ++index) {
|
||||
if (!ctx.info.loads.Generic(index)) {
|
||||
continue;
|
||||
}
|
||||
read_swizzled(index, fmt::format("in_attr{}{}[0]", index, VertexIndex(ctx, vertex)));
|
||||
}
|
||||
for (u32 i = 0; i < num_endifs; ++i) {
|
||||
ctx.Add("ENDIF;");
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetAttributeIndexed([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] ScalarU32 offset,
|
||||
[[maybe_unused]] ScalarF32 value, [[maybe_unused]] ScalarU32 vertex) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGetPatch(EmitContext& ctx, IR::Inst& inst, IR::Patch patch) {
|
||||
if (!IR::IsGeneric(patch)) {
|
||||
throw NotImplementedException("Non-generic patch load");
|
||||
}
|
||||
const u32 index{IR::GenericPatchIndex(patch)};
|
||||
const u32 element{IR::GenericPatchElement(patch)};
|
||||
const char swizzle{"xyzw"[element]};
|
||||
const std::string_view out{ctx.stage == Stage::TessellationControl ? ".out" : ""};
|
||||
ctx.Add("MOV.F {},primitive{}.patch.attrib[{}].{};", inst, out, index, swizzle);
|
||||
}
|
||||
|
||||
void EmitSetPatch(EmitContext& ctx, IR::Patch patch, ScalarF32 value) {
|
||||
if (IR::IsGeneric(patch)) {
|
||||
const u32 index{IR::GenericPatchIndex(patch)};
|
||||
const u32 element{IR::GenericPatchElement(patch)};
|
||||
ctx.Add("MOV.F result.patch.attrib[{}].{},{};", index, "xyzw"[element], value);
|
||||
return;
|
||||
}
|
||||
switch (patch) {
|
||||
case IR::Patch::TessellationLodLeft:
|
||||
case IR::Patch::TessellationLodRight:
|
||||
case IR::Patch::TessellationLodTop:
|
||||
case IR::Patch::TessellationLodBottom: {
|
||||
const u32 index{static_cast<u32>(patch) - u32(IR::Patch::TessellationLodLeft)};
|
||||
ctx.Add("MOV.F result.patch.tessouter[{}].x,{};", index, value);
|
||||
break;
|
||||
}
|
||||
case IR::Patch::TessellationLodInteriorU:
|
||||
ctx.Add("MOV.F result.patch.tessinner[0].x,{};", value);
|
||||
break;
|
||||
case IR::Patch::TessellationLodInteriorV:
|
||||
ctx.Add("MOV.F result.patch.tessinner[1].x,{};", value);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Patch {}", patch);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, ScalarF32 value) {
|
||||
ctx.Add("MOV.F frag_color{}.{},{};", index, "xyzw"[component], value);
|
||||
}
|
||||
|
||||
void EmitSetSampleMask(EmitContext& ctx, ScalarS32 value) {
|
||||
ctx.Add("MOV.S result.samplemask.x,{};", value);
|
||||
}
|
||||
|
||||
void EmitSetFragDepth(EmitContext& ctx, ScalarF32 value) {
|
||||
ctx.Add("MOV.F result.depth.z,{};", value);
|
||||
}
|
||||
|
||||
void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset) {
|
||||
ctx.Add("MOV.U {},lmem[{}].x;", inst, word_offset);
|
||||
}
|
||||
|
||||
void EmitWriteLocal(EmitContext& ctx, ScalarU32 word_offset, ScalarU32 value) {
|
||||
ctx.Add("MOV.U lmem[{}].x,{};", word_offset, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
231
src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp
Normal file
231
src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp
Normal file
@@ -0,0 +1,231 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_context.h"
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
namespace {
|
||||
std::string_view FpRounding(IR::FpRounding fp_rounding) {
|
||||
switch (fp_rounding) {
|
||||
case IR::FpRounding::DontCare:
|
||||
return "";
|
||||
case IR::FpRounding::RN:
|
||||
return ".ROUND";
|
||||
case IR::FpRounding::RZ:
|
||||
return ".TRUNC";
|
||||
case IR::FpRounding::RM:
|
||||
return ".FLR";
|
||||
case IR::FpRounding::RP:
|
||||
return ".CEIL";
|
||||
}
|
||||
throw InvalidArgument("Invalid floating-point rounding {}", fp_rounding);
|
||||
}
|
||||
|
||||
template <typename InputType>
|
||||
void Convert(EmitContext& ctx, IR::Inst& inst, InputType value, std::string_view dest,
|
||||
std::string_view src, bool is_long_result) {
|
||||
const std::string_view fp_rounding{FpRounding(inst.Flags<IR::FpControl>().rounding)};
|
||||
const auto ret{is_long_result ? ctx.reg_alloc.LongDefine(inst) : ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("CVT.{}.{}{} {}.x,{};", dest, src, fp_rounding, ret, value);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitConvertS16F16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "S16", "F16", false);
|
||||
}
|
||||
|
||||
void EmitConvertS16F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Convert(ctx, inst, value, "S16", "F32", false);
|
||||
}
|
||||
|
||||
void EmitConvertS16F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
Convert(ctx, inst, value, "S16", "F64", false);
|
||||
}
|
||||
|
||||
void EmitConvertS32F16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "S32", "F16", false);
|
||||
}
|
||||
|
||||
void EmitConvertS32F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Convert(ctx, inst, value, "S32", "F32", false);
|
||||
}
|
||||
|
||||
void EmitConvertS32F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
Convert(ctx, inst, value, "S32", "F64", false);
|
||||
}
|
||||
|
||||
void EmitConvertS64F16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "S64", "F16", true);
|
||||
}
|
||||
|
||||
void EmitConvertS64F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Convert(ctx, inst, value, "S64", "F32", true);
|
||||
}
|
||||
|
||||
void EmitConvertS64F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
Convert(ctx, inst, value, "S64", "F64", true);
|
||||
}
|
||||
|
||||
void EmitConvertU16F16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "U16", "F16", false);
|
||||
}
|
||||
|
||||
void EmitConvertU16F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Convert(ctx, inst, value, "U16", "F32", false);
|
||||
}
|
||||
|
||||
void EmitConvertU16F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
Convert(ctx, inst, value, "U16", "F64", false);
|
||||
}
|
||||
|
||||
void EmitConvertU32F16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "U32", "F16", false);
|
||||
}
|
||||
|
||||
void EmitConvertU32F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Convert(ctx, inst, value, "U32", "F32", false);
|
||||
}
|
||||
|
||||
void EmitConvertU32F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
Convert(ctx, inst, value, "U32", "F64", false);
|
||||
}
|
||||
|
||||
void EmitConvertU64F16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "U64", "F16", true);
|
||||
}
|
||||
|
||||
void EmitConvertU64F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Convert(ctx, inst, value, "U64", "F32", true);
|
||||
}
|
||||
|
||||
void EmitConvertU64F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
Convert(ctx, inst, value, "U64", "F64", true);
|
||||
}
|
||||
|
||||
void EmitConvertU64U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value) {
|
||||
Convert(ctx, inst, value, "U64", "U32", true);
|
||||
}
|
||||
|
||||
void EmitConvertU32U64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "U32", "U64", false);
|
||||
}
|
||||
|
||||
void EmitConvertF16F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Convert(ctx, inst, value, "F16", "F32", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32F16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F32", "F16", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
Convert(ctx, inst, value, "F32", "F64", false);
|
||||
}
|
||||
|
||||
void EmitConvertF64F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Convert(ctx, inst, value, "F64", "F32", true);
|
||||
}
|
||||
|
||||
void EmitConvertF16S8(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F16", "S8", false);
|
||||
}
|
||||
|
||||
void EmitConvertF16S16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F16", "S16", false);
|
||||
}
|
||||
|
||||
void EmitConvertF16S32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
Convert(ctx, inst, value, "F16", "S32", false);
|
||||
}
|
||||
|
||||
void EmitConvertF16S64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F16", "S64", false);
|
||||
}
|
||||
|
||||
void EmitConvertF16U8(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F16", "U8", false);
|
||||
}
|
||||
|
||||
void EmitConvertF16U16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F16", "U16", false);
|
||||
}
|
||||
|
||||
void EmitConvertF16U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value) {
|
||||
Convert(ctx, inst, value, "F16", "U32", false);
|
||||
}
|
||||
|
||||
void EmitConvertF16U64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F16", "U64", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32S8(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F32", "S8", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32S16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F32", "S16", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32S32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
Convert(ctx, inst, value, "F32", "S32", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32S64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F32", "S64", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32U8(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F32", "U8", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32U16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F32", "U16", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value) {
|
||||
Convert(ctx, inst, value, "F32", "U32", false);
|
||||
}
|
||||
|
||||
void EmitConvertF32U64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F32", "U64", false);
|
||||
}
|
||||
|
||||
void EmitConvertF64S8(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F64", "S8", true);
|
||||
}
|
||||
|
||||
void EmitConvertF64S16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F64", "S16", true);
|
||||
}
|
||||
|
||||
void EmitConvertF64S32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
Convert(ctx, inst, value, "F64", "S32", true);
|
||||
}
|
||||
|
||||
void EmitConvertF64S64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F64", "S64", true);
|
||||
}
|
||||
|
||||
void EmitConvertF64U8(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F64", "U8", true);
|
||||
}
|
||||
|
||||
void EmitConvertF64U16(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F64", "U16", true);
|
||||
}
|
||||
|
||||
void EmitConvertF64U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value) {
|
||||
Convert(ctx, inst, value, "F64", "U32", true);
|
||||
}
|
||||
|
||||
void EmitConvertF64U64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
Convert(ctx, inst, value, "F64", "U64", true);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
@@ -0,0 +1,414 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_context.h"
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
namespace {
|
||||
template <typename InputType>
|
||||
void Compare(EmitContext& ctx, IR::Inst& inst, InputType lhs, InputType rhs, std::string_view op,
|
||||
std::string_view type, bool ordered, bool inequality = false) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("{}.{} RC.x,{},{};", op, type, lhs, rhs);
|
||||
if (ordered && inequality) {
|
||||
ctx.Add("SEQ.{} RC.y,{},{};"
|
||||
"SEQ.{} RC.z,{},{};"
|
||||
"AND.U RC.x,RC.x,RC.y;"
|
||||
"AND.U RC.x,RC.x,RC.z;"
|
||||
"SNE.S {}.x,RC.x,0;",
|
||||
type, lhs, lhs, type, rhs, rhs, ret);
|
||||
} else if (ordered) {
|
||||
ctx.Add("SNE.S {}.x,RC.x,0;", ret);
|
||||
} else {
|
||||
ctx.Add("SNE.{} RC.y,{},{};"
|
||||
"SNE.{} RC.z,{},{};"
|
||||
"OR.U RC.x,RC.x,RC.y;"
|
||||
"OR.U RC.x,RC.x,RC.z;"
|
||||
"SNE.S {}.x,RC.x,0;",
|
||||
type, lhs, lhs, type, rhs, rhs, ret);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename InputType>
|
||||
void Clamp(EmitContext& ctx, Register ret, InputType value, InputType min_value,
|
||||
InputType max_value, std::string_view type) {
|
||||
// Call MAX first to properly clamp nan to min_value instead
|
||||
ctx.Add("MAX.{} RC.x,{},{};"
|
||||
"MIN.{} {}.x,RC.x,{};",
|
||||
type, min_value, value, type, ret, max_value);
|
||||
}
|
||||
|
||||
std::string_view Precise(IR::Inst& inst) {
|
||||
const bool precise{inst.Flags<IR::FpControl>().no_contraction};
|
||||
return precise ? ".PREC" : "";
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitFPAbs16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPAbs32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("MOV.F {}.x,|{}|;", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPAbs64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
ctx.LongAdd("MOV.F64 {}.x,|{}|;", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPAdd16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] Register a, [[maybe_unused]] Register b) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPAdd32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b) {
|
||||
ctx.Add("ADD.F{} {}.x,{},{};", Precise(inst), ctx.reg_alloc.Define(inst), a, b);
|
||||
}
|
||||
|
||||
void EmitFPAdd64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b) {
|
||||
ctx.Add("ADD.F64{} {}.x,{},{};", Precise(inst), ctx.reg_alloc.LongDefine(inst), a, b);
|
||||
}
|
||||
|
||||
void EmitFPFma16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] Register a, [[maybe_unused]] Register b,
|
||||
[[maybe_unused]] Register c) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPFma32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b, ScalarF32 c) {
|
||||
ctx.Add("MAD.F{} {}.x,{},{},{};", Precise(inst), ctx.reg_alloc.Define(inst), a, b, c);
|
||||
}
|
||||
|
||||
void EmitFPFma64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b, ScalarF64 c) {
|
||||
ctx.Add("MAD.F64{} {}.x,{},{},{};", Precise(inst), ctx.reg_alloc.LongDefine(inst), a, b, c);
|
||||
}
|
||||
|
||||
void EmitFPMax32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b) {
|
||||
ctx.Add("MAX.F {}.x,{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMax64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b) {
|
||||
ctx.LongAdd("MAX.F64 {}.x,{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMin32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b) {
|
||||
ctx.Add("MIN.F {}.x,{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMin64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b) {
|
||||
ctx.LongAdd("MIN.F64 {}.x,{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitFPMul16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] Register a, [[maybe_unused]] Register b) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPMul32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b) {
|
||||
ctx.Add("MUL.F{} {}.x,{},{};", Precise(inst), ctx.reg_alloc.Define(inst), a, b);
|
||||
}
|
||||
|
||||
void EmitFPMul64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b) {
|
||||
ctx.Add("MUL.F64{} {}.x,{},{};", Precise(inst), ctx.reg_alloc.LongDefine(inst), a, b);
|
||||
}
|
||||
|
||||
void EmitFPNeg16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPNeg32(EmitContext& ctx, IR::Inst& inst, ScalarRegister value) {
|
||||
ctx.Add("MOV.F {}.x,-{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPNeg64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
ctx.LongAdd("MOV.F64 {}.x,-{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPSin(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("SIN {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPCos(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("COS {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPExp2(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("EX2 {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPLog2(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("LG2 {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecip32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("RCP {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecip64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPRecipSqrt32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("RSQ {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRecipSqrt64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPSqrt(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("RSQ RC.x,{};RCP {}.x,RC.x;", value, ret);
|
||||
}
|
||||
|
||||
void EmitFPSaturate16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPSaturate32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("MOV.F.SAT {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPSaturate64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPClamp16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value,
|
||||
[[maybe_unused]] Register min_value, [[maybe_unused]] Register max_value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPClamp32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value, ScalarF32 min_value,
|
||||
ScalarF32 max_value) {
|
||||
Clamp(ctx, ctx.reg_alloc.Define(inst), value, min_value, max_value, "F");
|
||||
}
|
||||
|
||||
void EmitFPClamp64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value, ScalarF64 min_value,
|
||||
ScalarF64 max_value) {
|
||||
Clamp(ctx, ctx.reg_alloc.LongDefine(inst), value, min_value, max_value, "F64");
|
||||
}
|
||||
|
||||
void EmitFPRoundEven16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPRoundEven32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("ROUND.F {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPRoundEven64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
ctx.LongAdd("ROUND.F64 {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPFloor16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPFloor32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("FLR.F {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPFloor64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
ctx.LongAdd("FLR.F64 {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPCeil16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPCeil32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("CEIL.F {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPCeil64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
ctx.LongAdd("CEIL.F64 {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPTrunc16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPTrunc32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
ctx.Add("TRUNC.F {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPTrunc64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
ctx.LongAdd("TRUNC.F64 {}.x,{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPOrdEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPOrdEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SEQ", "F", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SEQ", "F64", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPUnordEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SEQ", "F", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SEQ", "F64", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdNotEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPOrdNotEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SNE", "F", true, true);
|
||||
}
|
||||
|
||||
void EmitFPOrdNotEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SNE", "F64", true, true);
|
||||
}
|
||||
|
||||
void EmitFPUnordNotEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPUnordNotEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SNE", "F", false, true);
|
||||
}
|
||||
|
||||
void EmitFPUnordNotEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SNE", "F64", false, true);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SLT", "F", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SLT", "F64", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SLT", "F", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SLT", "F64", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SGT", "F", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SGT", "F64", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SGT", "F", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SGT", "F64", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThanEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SLE", "F", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdLessThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SLE", "F64", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThanEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SLE", "F", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordLessThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SLE", "F64", false);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThanEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SGE", "F", true);
|
||||
}
|
||||
|
||||
void EmitFPOrdGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SGE", "F64", true);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThanEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register lhs,
|
||||
[[maybe_unused]] Register rhs) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SGE", "F", false);
|
||||
}
|
||||
|
||||
void EmitFPUnordGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs) {
|
||||
Compare(ctx, inst, lhs, rhs, "SGE", "F64", false);
|
||||
}
|
||||
|
||||
void EmitFPIsNan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitFPIsNan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value) {
|
||||
Compare(ctx, inst, value, value, "SNE", "F", true, false);
|
||||
}
|
||||
|
||||
void EmitFPIsNan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value) {
|
||||
Compare(ctx, inst, value, value, "SNE", "F64", true, false);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
850
src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
Normal file
850
src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
Normal file
@@ -0,0 +1,850 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_context.h"
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
namespace {
|
||||
struct ScopedRegister {
|
||||
ScopedRegister() = default;
|
||||
ScopedRegister(RegAlloc& reg_alloc_) : reg_alloc{®_alloc_}, reg{reg_alloc->AllocReg()} {}
|
||||
|
||||
~ScopedRegister() {
|
||||
if (reg_alloc) {
|
||||
reg_alloc->FreeReg(reg);
|
||||
}
|
||||
}
|
||||
|
||||
ScopedRegister& operator=(ScopedRegister&& rhs) noexcept {
|
||||
if (reg_alloc) {
|
||||
reg_alloc->FreeReg(reg);
|
||||
}
|
||||
reg_alloc = std::exchange(rhs.reg_alloc, nullptr);
|
||||
reg = rhs.reg;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ScopedRegister(ScopedRegister&& rhs) noexcept
|
||||
: reg_alloc{std::exchange(rhs.reg_alloc, nullptr)}, reg{rhs.reg} {}
|
||||
|
||||
ScopedRegister& operator=(const ScopedRegister&) = delete;
|
||||
ScopedRegister(const ScopedRegister&) = delete;
|
||||
|
||||
RegAlloc* reg_alloc{};
|
||||
Register reg;
|
||||
};
|
||||
|
||||
std::string Texture(EmitContext& ctx, IR::TextureInstInfo info,
|
||||
[[maybe_unused]] const IR::Value& index) {
|
||||
// FIXME: indexed reads
|
||||
if (info.type == TextureType::Buffer) {
|
||||
return fmt::format("texture[{}]", ctx.texture_buffer_bindings.at(info.descriptor_index));
|
||||
} else {
|
||||
return fmt::format("texture[{}]", ctx.texture_bindings.at(info.descriptor_index));
|
||||
}
|
||||
}
|
||||
|
||||
std::string Image(EmitContext& ctx, IR::TextureInstInfo info,
|
||||
[[maybe_unused]] const IR::Value& index) {
|
||||
// FIXME: indexed reads
|
||||
if (info.type == TextureType::Buffer) {
|
||||
return fmt::format("image[{}]", ctx.image_buffer_bindings.at(info.descriptor_index));
|
||||
} else {
|
||||
return fmt::format("image[{}]", ctx.image_bindings.at(info.descriptor_index));
|
||||
}
|
||||
}
|
||||
|
||||
std::string_view TextureType(IR::TextureInstInfo info) {
|
||||
if (info.is_depth) {
|
||||
switch (info.type) {
|
||||
case TextureType::Color1D:
|
||||
return "SHADOW1D";
|
||||
case TextureType::ColorArray1D:
|
||||
return "SHADOWARRAY1D";
|
||||
case TextureType::Color2D:
|
||||
return "SHADOW2D";
|
||||
case TextureType::ColorArray2D:
|
||||
return "SHADOWARRAY2D";
|
||||
case TextureType::Color3D:
|
||||
return "SHADOW3D";
|
||||
case TextureType::ColorCube:
|
||||
return "SHADOWCUBE";
|
||||
case TextureType::ColorArrayCube:
|
||||
return "SHADOWARRAYCUBE";
|
||||
case TextureType::Buffer:
|
||||
return "SHADOWBUFFER";
|
||||
}
|
||||
} else {
|
||||
switch (info.type) {
|
||||
case TextureType::Color1D:
|
||||
return "1D";
|
||||
case TextureType::ColorArray1D:
|
||||
return "ARRAY1D";
|
||||
case TextureType::Color2D:
|
||||
return "2D";
|
||||
case TextureType::ColorArray2D:
|
||||
return "ARRAY2D";
|
||||
case TextureType::Color3D:
|
||||
return "3D";
|
||||
case TextureType::ColorCube:
|
||||
return "CUBE";
|
||||
case TextureType::ColorArrayCube:
|
||||
return "ARRAYCUBE";
|
||||
case TextureType::Buffer:
|
||||
return "BUFFER";
|
||||
}
|
||||
}
|
||||
throw InvalidArgument("Invalid texture type {}", info.type.Value());
|
||||
}
|
||||
|
||||
std::string Offset(EmitContext& ctx, const IR::Value& offset) {
|
||||
if (offset.IsEmpty()) {
|
||||
return "";
|
||||
}
|
||||
return fmt::format(",offset({})", Register{ctx.reg_alloc.Consume(offset)});
|
||||
}
|
||||
|
||||
std::pair<ScopedRegister, ScopedRegister> AllocOffsetsRegs(EmitContext& ctx,
|
||||
const IR::Value& offset2) {
|
||||
if (offset2.IsEmpty()) {
|
||||
return {};
|
||||
} else {
|
||||
return {ctx.reg_alloc, ctx.reg_alloc};
|
||||
}
|
||||
}
|
||||
|
||||
void SwizzleOffsets(EmitContext& ctx, Register off_x, Register off_y, const IR::Value& offset1,
|
||||
const IR::Value& offset2) {
|
||||
const Register offsets_a{ctx.reg_alloc.Consume(offset1)};
|
||||
const Register offsets_b{ctx.reg_alloc.Consume(offset2)};
|
||||
// Input swizzle: [XYXY] [XYXY]
|
||||
// Output swizzle: [XXXX] [YYYY]
|
||||
ctx.Add("MOV {}.x,{}.x;"
|
||||
"MOV {}.y,{}.z;"
|
||||
"MOV {}.z,{}.x;"
|
||||
"MOV {}.w,{}.z;"
|
||||
"MOV {}.x,{}.y;"
|
||||
"MOV {}.y,{}.w;"
|
||||
"MOV {}.z,{}.y;"
|
||||
"MOV {}.w,{}.w;",
|
||||
off_x, offsets_a, off_x, offsets_a, off_x, offsets_b, off_x, offsets_b, off_y,
|
||||
offsets_a, off_y, offsets_a, off_y, offsets_b, off_y, offsets_b);
|
||||
}
|
||||
|
||||
std::string GradOffset(const IR::Value& offset) {
|
||||
if (offset.IsImmediate()) {
|
||||
LOG_WARNING(Shader_GLASM, "Gradient offset is a scalar immediate");
|
||||
return "";
|
||||
}
|
||||
IR::Inst* const vector{offset.InstRecursive()};
|
||||
if (!vector->AreAllArgsImmediates()) {
|
||||
LOG_WARNING(Shader_GLASM, "Gradient offset vector is not immediate");
|
||||
return "";
|
||||
}
|
||||
switch (vector->NumArgs()) {
|
||||
case 1:
|
||||
return fmt::format(",({})", static_cast<s32>(vector->Arg(0).U32()));
|
||||
case 2:
|
||||
return fmt::format(",({},{})", static_cast<s32>(vector->Arg(0).U32()),
|
||||
static_cast<s32>(vector->Arg(1).U32()));
|
||||
default:
|
||||
throw LogicError("Invalid number of gradient offsets {}", vector->NumArgs());
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<std::string, ScopedRegister> Coord(EmitContext& ctx, const IR::Value& coord) {
|
||||
if (coord.IsImmediate()) {
|
||||
ScopedRegister scoped_reg(ctx.reg_alloc);
|
||||
ctx.Add("MOV.U {}.x,{};", scoped_reg.reg, ScalarU32{ctx.reg_alloc.Consume(coord)});
|
||||
return {fmt::to_string(scoped_reg.reg), std::move(scoped_reg)};
|
||||
}
|
||||
std::string coord_vec{fmt::to_string(Register{ctx.reg_alloc.Consume(coord)})};
|
||||
if (coord.InstRecursive()->HasUses()) {
|
||||
// Move non-dead coords to a separate register, although this should never happen because
|
||||
// vectors are only assembled for immediate texture instructions
|
||||
ctx.Add("MOV.F RC,{};", coord_vec);
|
||||
coord_vec = "RC";
|
||||
}
|
||||
return {std::move(coord_vec), ScopedRegister{}};
|
||||
}
|
||||
|
||||
void StoreSparse(EmitContext& ctx, IR::Inst* sparse_inst) {
|
||||
if (!sparse_inst) {
|
||||
return;
|
||||
}
|
||||
const Register sparse_ret{ctx.reg_alloc.Define(*sparse_inst)};
|
||||
ctx.Add("MOV.S {},-1;"
|
||||
"MOV.S {}(NONRESIDENT),0;",
|
||||
sparse_ret, sparse_ret);
|
||||
}
|
||||
|
||||
std::string_view FormatStorage(ImageFormat format) {
|
||||
switch (format) {
|
||||
case ImageFormat::Typeless:
|
||||
return "U";
|
||||
case ImageFormat::R8_UINT:
|
||||
return "U8";
|
||||
case ImageFormat::R8_SINT:
|
||||
return "S8";
|
||||
case ImageFormat::R16_UINT:
|
||||
return "U16";
|
||||
case ImageFormat::R16_SINT:
|
||||
return "S16";
|
||||
case ImageFormat::R32_UINT:
|
||||
return "U32";
|
||||
case ImageFormat::R32G32_UINT:
|
||||
return "U32X2";
|
||||
case ImageFormat::R32G32B32A32_UINT:
|
||||
return "U32X4";
|
||||
}
|
||||
throw InvalidArgument("Invalid image format {}", format);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ImageAtomic(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord, T value,
|
||||
std::string_view op) {
|
||||
const auto info{inst.Flags<IR::TextureInstInfo>()};
|
||||
const std::string_view type{TextureType(info)};
|
||||
const std::string image{Image(ctx, info, index)};
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("ATOMIM.{} {},{},{},{},{};", op, ret, value, coord, image, type);
|
||||
}
|
||||
|
||||
IR::Inst* PrepareSparse(IR::Inst& inst) {
|
||||
const auto sparse_inst{inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
|
||||
if (sparse_inst) {
|
||||
sparse_inst->Invalidate();
|
||||
}
|
||||
return sparse_inst;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
|
||||
const IR::Value& coord, Register bias_lc, const IR::Value& offset) {
|
||||
const auto info{inst.Flags<IR::TextureInstInfo>()};
|
||||
const auto sparse_inst{PrepareSparse(inst)};
|
||||
const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
|
||||
const std::string_view lod_clamp_mod{info.has_lod_clamp ? ".LODCLAMP" : ""};
|
||||
const std::string_view type{TextureType(info)};
|
||||
const std::string texture{Texture(ctx, info, index)};
|
||||
const std::string offset_vec{Offset(ctx, offset)};
|
||||
const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (info.has_bias) {
|
||||
if (info.type == TextureType::ColorArrayCube) {
|
||||
ctx.Add("TXB.F{}{} {},{},{},{},ARRAYCUBE{};", lod_clamp_mod, sparse_mod, ret, coord_vec,
|
||||
bias_lc, texture, offset_vec);
|
||||
} else {
|
||||
if (info.has_lod_clamp) {
|
||||
ctx.Add("MOV.F {}.w,{}.x;"
|
||||
"TXB.F.LODCLAMP{} {},{},{}.y,{},{}{};",
|
||||
coord_vec, bias_lc, sparse_mod, ret, coord_vec, bias_lc, texture, type,
|
||||
offset_vec);
|
||||
} else {
|
||||
ctx.Add("MOV.F {}.w,{}.x;"
|
||||
"TXB.F{} {},{},{},{}{};",
|
||||
coord_vec, bias_lc, sparse_mod, ret, coord_vec, texture, type, offset_vec);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (info.has_lod_clamp && info.type == TextureType::ColorArrayCube) {
|
||||
ctx.Add("TEX.F.LODCLAMP{} {},{},{},{},ARRAYCUBE{};", sparse_mod, ret, coord_vec,
|
||||
bias_lc, texture, offset_vec);
|
||||
} else {
|
||||
ctx.Add("TEX.F{}{} {},{},{},{}{};", lod_clamp_mod, sparse_mod, ret, coord_vec, texture,
|
||||
type, offset_vec);
|
||||
}
|
||||
}
|
||||
StoreSparse(ctx, sparse_inst);
|
||||
}
|
||||
|
||||
void EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
|
||||
const IR::Value& coord, ScalarF32 lod, const IR::Value& offset) {
|
||||
const auto info{inst.Flags<IR::TextureInstInfo>()};
|
||||
const auto sparse_inst{PrepareSparse(inst)};
|
||||
const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
|
||||
const std::string_view type{TextureType(info)};
|
||||
const std::string texture{Texture(ctx, info, index)};
|
||||
const std::string offset_vec{Offset(ctx, offset)};
|
||||
const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (info.type == TextureType::ColorArrayCube) {
|
||||
ctx.Add("TXL.F{} {},{},{},{},ARRAYCUBE{};", sparse_mod, ret, coord_vec, lod, texture,
|
||||
offset_vec);
|
||||
} else {
|
||||
ctx.Add("MOV.F {}.w,{};"
|
||||
"TXL.F{} {},{},{},{}{};",
|
||||
coord_vec, lod, sparse_mod, ret, coord_vec, texture, type, offset_vec);
|
||||
}
|
||||
StoreSparse(ctx, sparse_inst);
|
||||
}
|
||||
|
||||
void EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
|
||||
const IR::Value& coord, const IR::Value& dref,
|
||||
const IR::Value& bias_lc, const IR::Value& offset) {
|
||||
// Allocate early to avoid aliases
|
||||
const auto info{inst.Flags<IR::TextureInstInfo>()};
|
||||
ScopedRegister staging;
|
||||
if (info.type == TextureType::ColorArrayCube) {
|
||||
staging = ScopedRegister{ctx.reg_alloc};
|
||||
}
|
||||
const ScalarF32 dref_val{ctx.reg_alloc.Consume(dref)};
|
||||
const Register bias_lc_vec{ctx.reg_alloc.Consume(bias_lc)};
|
||||
const auto sparse_inst{PrepareSparse(inst)};
|
||||
const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
|
||||
const std::string_view type{TextureType(info)};
|
||||
const std::string texture{Texture(ctx, info, index)};
|
||||
const std::string offset_vec{Offset(ctx, offset)};
|
||||
const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (info.has_bias) {
|
||||
if (info.has_lod_clamp) {
|
||||
switch (info.type) {
|
||||
case TextureType::Color1D:
|
||||
case TextureType::ColorArray1D:
|
||||
case TextureType::Color2D:
|
||||
ctx.Add("MOV.F {}.z,{};"
|
||||
"MOV.F {}.w,{}.x;"
|
||||
"TXB.F.LODCLAMP{} {},{},{}.y,{},{}{};",
|
||||
coord_vec, dref_val, coord_vec, bias_lc_vec, sparse_mod, ret, coord_vec,
|
||||
bias_lc_vec, texture, type, offset_vec);
|
||||
break;
|
||||
case TextureType::ColorArray2D:
|
||||
case TextureType::ColorCube:
|
||||
ctx.Add("MOV.F {}.w,{};"
|
||||
"TXB.F.LODCLAMP{} {},{},{},{},{}{};",
|
||||
coord_vec, dref_val, sparse_mod, ret, coord_vec, bias_lc_vec, texture, type,
|
||||
offset_vec);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Invalid type {} with bias and lod clamp",
|
||||
info.type.Value());
|
||||
}
|
||||
} else {
|
||||
switch (info.type) {
|
||||
case TextureType::Color1D:
|
||||
case TextureType::ColorArray1D:
|
||||
case TextureType::Color2D:
|
||||
ctx.Add("MOV.F {}.z,{};"
|
||||
"MOV.F {}.w,{}.x;"
|
||||
"TXB.F{} {},{},{},{}{};",
|
||||
coord_vec, dref_val, coord_vec, bias_lc_vec, sparse_mod, ret, coord_vec,
|
||||
texture, type, offset_vec);
|
||||
break;
|
||||
case TextureType::ColorArray2D:
|
||||
case TextureType::ColorCube:
|
||||
ctx.Add("MOV.F {}.w,{};"
|
||||
"TXB.F{} {},{},{},{},{}{};",
|
||||
coord_vec, dref_val, sparse_mod, ret, coord_vec, bias_lc_vec, texture, type,
|
||||
offset_vec);
|
||||
break;
|
||||
case TextureType::ColorArrayCube:
|
||||
ctx.Add("MOV.F {}.x,{};"
|
||||
"MOV.F {}.y,{}.x;"
|
||||
"TXB.F{} {},{},{},{},{}{};",
|
||||
staging.reg, dref_val, staging.reg, bias_lc_vec, sparse_mod, ret, coord_vec,
|
||||
staging.reg, texture, type, offset_vec);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Invalid type {}", info.type.Value());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (info.has_lod_clamp) {
|
||||
if (info.type != TextureType::ColorArrayCube) {
|
||||
const bool w_swizzle{info.type == TextureType::ColorArray2D ||
|
||||
info.type == TextureType::ColorCube};
|
||||
const char dref_swizzle{w_swizzle ? 'w' : 'z'};
|
||||
ctx.Add("MOV.F {}.{},{};"
|
||||
"TEX.F.LODCLAMP{} {},{},{},{},{}{};",
|
||||
coord_vec, dref_swizzle, dref_val, sparse_mod, ret, coord_vec, bias_lc_vec,
|
||||
texture, type, offset_vec);
|
||||
} else {
|
||||
ctx.Add("MOV.F {}.x,{};"
|
||||
"MOV.F {}.y,{};"
|
||||
"TEX.F.LODCLAMP{} {},{},{},{},{}{};",
|
||||
staging.reg, dref_val, staging.reg, bias_lc_vec, sparse_mod, ret, coord_vec,
|
||||
staging.reg, texture, type, offset_vec);
|
||||
}
|
||||
} else {
|
||||
if (info.type != TextureType::ColorArrayCube) {
|
||||
const bool w_swizzle{info.type == TextureType::ColorArray2D ||
|
||||
info.type == TextureType::ColorCube};
|
||||
const char dref_swizzle{w_swizzle ? 'w' : 'z'};
|
||||
ctx.Add("MOV.F {}.{},{};"
|
||||
"TEX.F{} {},{},{},{}{};",
|
||||
coord_vec, dref_swizzle, dref_val, sparse_mod, ret, coord_vec, texture,
|
||||
type, offset_vec);
|
||||
} else {
|
||||
ctx.Add("TEX.F{} {},{},{},{},{}{};", sparse_mod, ret, coord_vec, dref_val, texture,
|
||||
type, offset_vec);
|
||||
}
|
||||
}
|
||||
}
|
||||
StoreSparse(ctx, sparse_inst);
|
||||
}
|
||||
|
||||
void EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
|
||||
const IR::Value& coord, const IR::Value& dref,
|
||||
const IR::Value& lod, const IR::Value& offset) {
|
||||
// Allocate early to avoid aliases
|
||||
const auto info{inst.Flags<IR::TextureInstInfo>()};
|
||||
ScopedRegister staging;
|
||||
if (info.type == TextureType::ColorArrayCube) {
|
||||
staging = ScopedRegister{ctx.reg_alloc};
|
||||
}
|
||||
const ScalarF32 dref_val{ctx.reg_alloc.Consume(dref)};
|
||||
const ScalarF32 lod_val{ctx.reg_alloc.Consume(lod)};
|
||||
const auto sparse_inst{PrepareSparse(inst)};
|
||||
const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
|
||||
const std::string_view type{TextureType(info)};
|
||||
const std::string texture{Texture(ctx, info, index)};
|
||||
const std::string offset_vec{Offset(ctx, offset)};
|
||||
const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
switch (info.type) {
|
||||
case TextureType::Color1D:
|
||||
case TextureType::ColorArray1D:
|
||||
case TextureType::Color2D:
|
||||
ctx.Add("MOV.F {}.z,{};"
|
||||
"MOV.F {}.w,{};"
|
||||
"TXL.F{} {},{},{},{}{};",
|
||||
coord_vec, dref_val, coord_vec, lod_val, sparse_mod, ret, coord_vec, texture, type,
|
||||
offset_vec);
|
||||
break;
|
||||
case TextureType::ColorArray2D:
|
||||
case TextureType::ColorCube:
|
||||
ctx.Add("MOV.F {}.w,{};"
|
||||
"TXL.F{} {},{},{},{},{}{};",
|
||||
coord_vec, dref_val, sparse_mod, ret, coord_vec, lod_val, texture, type,
|
||||
offset_vec);
|
||||
break;
|
||||
case TextureType::ColorArrayCube:
|
||||
ctx.Add("MOV.F {}.x,{};"
|
||||
"MOV.F {}.y,{};"
|
||||
"TXL.F{} {},{},{},{},{}{};",
|
||||
staging.reg, dref_val, staging.reg, lod_val, sparse_mod, ret, coord_vec,
|
||||
staging.reg, texture, type, offset_vec);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Invalid type {}", info.type.Value());
|
||||
}
|
||||
StoreSparse(ctx, sparse_inst);
|
||||
}
|
||||
|
||||
void EmitImageGather(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
|
||||
const IR::Value& coord, const IR::Value& offset, const IR::Value& offset2) {
|
||||
// Allocate offsets early so they don't overwrite any consumed register
|
||||
const auto [off_x, off_y]{AllocOffsetsRegs(ctx, offset2)};
|
||||
const auto info{inst.Flags<IR::TextureInstInfo>()};
|
||||
const char comp{"xyzw"[info.gather_component]};
|
||||
const auto sparse_inst{PrepareSparse(inst)};
|
||||
const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
|
||||
const std::string_view type{TextureType(info)};
|
||||
const std::string texture{Texture(ctx, info, index)};
|
||||
const Register coord_vec{ctx.reg_alloc.Consume(coord)};
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (offset2.IsEmpty()) {
|
||||
const std::string offset_vec{Offset(ctx, offset)};
|
||||
ctx.Add("TXG.F{} {},{},{}.{},{}{};", sparse_mod, ret, coord_vec, texture, comp, type,
|
||||
offset_vec);
|
||||
} else {
|
||||
SwizzleOffsets(ctx, off_x.reg, off_y.reg, offset, offset2);
|
||||
ctx.Add("TXGO.F{} {},{},{},{},{}.{},{};", sparse_mod, ret, coord_vec, off_x.reg, off_y.reg,
|
||||
texture, comp, type);
|
||||
}
|
||||
StoreSparse(ctx, sparse_inst);
|
||||
}
|
||||
|
||||
void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
|
||||
const IR::Value& coord, const IR::Value& offset, const IR::Value& offset2,
|
||||
const IR::Value& dref) {
|
||||
// FIXME: This instruction is not working as expected
|
||||
|
||||
// Allocate offsets early so they don't overwrite any consumed register
|
||||
const auto [off_x, off_y]{AllocOffsetsRegs(ctx, offset2)};
|
||||
const auto info{inst.Flags<IR::TextureInstInfo>()};
|
||||
const auto sparse_inst{PrepareSparse(inst)};
|
||||
const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
|
||||
const std::string_view type{TextureType(info)};
|
||||
const std::string texture{Texture(ctx, info, index)};
|
||||
const Register coord_vec{ctx.reg_alloc.Consume(coord)};
|
||||
const ScalarF32 dref_value{ctx.reg_alloc.Consume(dref)};
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
std::string args;
|
||||
switch (info.type) {
|
||||
case TextureType::Color2D:
|
||||
ctx.Add("MOV.F {}.z,{};", coord_vec, dref_value);
|
||||
args = fmt::to_string(coord_vec);
|
||||
break;
|
||||
case TextureType::ColorArray2D:
|
||||
case TextureType::ColorCube:
|
||||
ctx.Add("MOV.F {}.w,{};", coord_vec, dref_value);
|
||||
args = fmt::to_string(coord_vec);
|
||||
break;
|
||||
case TextureType::ColorArrayCube:
|
||||
args = fmt::format("{},{}", coord_vec, dref_value);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Invalid type {}", info.type.Value());
|
||||
}
|
||||
if (offset2.IsEmpty()) {
|
||||
const std::string offset_vec{Offset(ctx, offset)};
|
||||
ctx.Add("TXG.F{} {},{},{},{}{};", sparse_mod, ret, args, texture, type, offset_vec);
|
||||
} else {
|
||||
SwizzleOffsets(ctx, off_x.reg, off_y.reg, offset, offset2);
|
||||
ctx.Add("TXGO.F{} {},{},{},{},{},{};", sparse_mod, ret, args, off_x.reg, off_y.reg, texture,
|
||||
type);
|
||||
}
|
||||
StoreSparse(ctx, sparse_inst);
|
||||
}
|
||||
|
||||
void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
|
||||
const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms) {
|
||||
const auto info{inst.Flags<IR::TextureInstInfo>()};
|
||||
const auto sparse_inst{PrepareSparse(inst)};
|
||||
const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
|
||||
const std::string_view type{TextureType(info)};
|
||||
const std::string texture{Texture(ctx, info, index)};
|
||||
const std::string offset_vec{Offset(ctx, offset)};
|
||||
const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (info.type == TextureType::Buffer) {
|
||||
ctx.Add("TXF.F{} {},{},{},{}{};", sparse_mod, ret, coord_vec, texture, type, offset_vec);
|
||||
} else if (ms.type != Type::Void) {
|
||||
ctx.Add("MOV.S {}.w,{};"
|
||||
"TXFMS.F{} {},{},{},{}{};",
|
||||
coord_vec, ms, sparse_mod, ret, coord_vec, texture, type, offset_vec);
|
||||
} else {
|
||||
ctx.Add("MOV.S {}.w,{};"
|
||||
"TXF.F{} {},{},{},{}{};",
|
||||
coord_vec, lod, sparse_mod, ret, coord_vec, texture, type, offset_vec);
|
||||
}
|
||||
StoreSparse(ctx, sparse_inst);
|
||||
}
|
||||
|
||||
void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
|
||||
ScalarS32 lod) {
|
||||
const auto info{inst.Flags<IR::TextureInstInfo>()};
|
||||
const std::string texture{Texture(ctx, info, index)};
|
||||
const std::string_view type{TextureType(info)};
|
||||
ctx.Add("TXQ {},{},{},{};", inst, lod, texture, type);
|
||||
}
|
||||
|
||||
void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord) {
|
||||
const auto info{inst.Flags<IR::TextureInstInfo>()};
|
||||
const std::string texture{Texture(ctx, info, index)};
|
||||
const std::string_view type{TextureType(info)};
|
||||
ctx.Add("LOD.F {},{},{},{};", inst, coord, texture, type);
|
||||
}
|
||||
|
||||
void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
|
||||
const IR::Value& coord, const IR::Value& derivatives,
|
||||
const IR::Value& offset, const IR::Value& lod_clamp) {
|
||||
const auto info{inst.Flags<IR::TextureInstInfo>()};
|
||||
ScopedRegister dpdx, dpdy;
|
||||
const bool multi_component{info.num_derivates > 1 || info.has_lod_clamp};
|
||||
if (multi_component) {
|
||||
// Allocate this early to avoid aliasing other registers
|
||||
dpdx = ScopedRegister{ctx.reg_alloc};
|
||||
dpdy = ScopedRegister{ctx.reg_alloc};
|
||||
}
|
||||
const auto sparse_inst{PrepareSparse(inst)};
|
||||
const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
|
||||
const std::string_view type{TextureType(info)};
|
||||
const std::string texture{Texture(ctx, info, index)};
|
||||
const std::string offset_vec{GradOffset(offset)};
|
||||
const Register coord_vec{ctx.reg_alloc.Consume(coord)};
|
||||
const Register derivatives_vec{ctx.reg_alloc.Consume(derivatives)};
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (multi_component) {
|
||||
ctx.Add("MOV.F {}.x,{}.x;"
|
||||
"MOV.F {}.y,{}.z;"
|
||||
"MOV.F {}.x,{}.y;"
|
||||
"MOV.F {}.y,{}.w;",
|
||||
dpdx.reg, derivatives_vec, dpdx.reg, derivatives_vec, dpdy.reg, derivatives_vec,
|
||||
dpdy.reg, derivatives_vec);
|
||||
if (info.has_lod_clamp) {
|
||||
const ScalarF32 lod_clamp_value{ctx.reg_alloc.Consume(lod_clamp)};
|
||||
ctx.Add("MOV.F {}.w,{};"
|
||||
"TXD.F.LODCLAMP{} {},{},{},{},{},{}{};",
|
||||
dpdy.reg, lod_clamp_value, sparse_mod, ret, coord_vec, dpdx.reg, dpdy.reg,
|
||||
texture, type, offset_vec);
|
||||
} else {
|
||||
ctx.Add("TXD.F{} {},{},{},{},{},{}{};", sparse_mod, ret, coord_vec, dpdx.reg, dpdy.reg,
|
||||
texture, type, offset_vec);
|
||||
}
|
||||
} else {
|
||||
ctx.Add("TXD.F{} {},{},{}.x,{}.y,{},{}{};", sparse_mod, ret, coord_vec, derivatives_vec,
|
||||
derivatives_vec, texture, type, offset_vec);
|
||||
}
|
||||
StoreSparse(ctx, sparse_inst);
|
||||
}
|
||||
|
||||
void EmitImageRead(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord) {
|
||||
const auto info{inst.Flags<IR::TextureInstInfo>()};
|
||||
const auto sparse_inst{PrepareSparse(inst)};
|
||||
const std::string_view format{FormatStorage(info.image_format)};
|
||||
const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
|
||||
const std::string_view type{TextureType(info)};
|
||||
const std::string image{Image(ctx, info, index)};
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("LOADIM.{}{} {},{},{},{};", format, sparse_mod, ret, coord, image, type);
|
||||
StoreSparse(ctx, sparse_inst);
|
||||
}
|
||||
|
||||
void EmitImageWrite(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
|
||||
Register color) {
|
||||
const auto info{inst.Flags<IR::TextureInstInfo>()};
|
||||
const std::string_view format{FormatStorage(info.image_format)};
|
||||
const std::string_view type{TextureType(info)};
|
||||
const std::string image{Image(ctx, info, index)};
|
||||
ctx.Add("STOREIM.{} {},{},{},{};", format, image, color, coord, type);
|
||||
}
|
||||
|
||||
void EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
|
||||
ScalarU32 value) {
|
||||
ImageAtomic(ctx, inst, index, coord, value, "ADD.U32");
|
||||
}
|
||||
|
||||
void EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
|
||||
ScalarS32 value) {
|
||||
ImageAtomic(ctx, inst, index, coord, value, "MIN.S32");
|
||||
}
|
||||
|
||||
void EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
|
||||
ScalarU32 value) {
|
||||
ImageAtomic(ctx, inst, index, coord, value, "MIN.U32");
|
||||
}
|
||||
|
||||
void EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
|
||||
ScalarS32 value) {
|
||||
ImageAtomic(ctx, inst, index, coord, value, "MAX.S32");
|
||||
}
|
||||
|
||||
void EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
|
||||
ScalarU32 value) {
|
||||
ImageAtomic(ctx, inst, index, coord, value, "MAX.U32");
|
||||
}
|
||||
|
||||
void EmitImageAtomicInc32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
|
||||
ScalarU32 value) {
|
||||
ImageAtomic(ctx, inst, index, coord, value, "IWRAP.U32");
|
||||
}
|
||||
|
||||
void EmitImageAtomicDec32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
|
||||
ScalarU32 value) {
|
||||
ImageAtomic(ctx, inst, index, coord, value, "DWRAP.U32");
|
||||
}
|
||||
|
||||
void EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
|
||||
ScalarU32 value) {
|
||||
ImageAtomic(ctx, inst, index, coord, value, "AND.U32");
|
||||
}
|
||||
|
||||
void EmitImageAtomicOr32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
|
||||
ScalarU32 value) {
|
||||
ImageAtomic(ctx, inst, index, coord, value, "OR.U32");
|
||||
}
|
||||
|
||||
void EmitImageAtomicXor32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
|
||||
ScalarU32 value) {
|
||||
ImageAtomic(ctx, inst, index, coord, value, "XOR.U32");
|
||||
}
|
||||
|
||||
void EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
|
||||
Register coord, ScalarU32 value) {
|
||||
ImageAtomic(ctx, inst, index, coord, value, "EXCH.U32");
|
||||
}
|
||||
|
||||
void EmitBindlessImageSampleImplicitLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBindlessImageSampleExplicitLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBindlessImageSampleDrefImplicitLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBindlessImageSampleDrefExplicitLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBindlessImageGather(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBindlessImageGatherDref(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBindlessImageFetch(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBindlessImageQueryDimensions(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBindlessImageQueryLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBindlessImageGradient(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBindlessImageRead(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBindlessImageWrite(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBoundImageSampleImplicitLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBoundImageSampleExplicitLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBoundImageSampleDrefImplicitLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBoundImageSampleDrefExplicitLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBoundImageGather(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBoundImageGatherDref(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBoundImageFetch(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBoundImageQueryDimensions(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBoundImageQueryLod(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBoundImageGradient(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBoundImageRead(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBoundImageWrite(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBindlessImageAtomicIAdd32(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBindlessImageAtomicSMin32(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBindlessImageAtomicUMin32(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBindlessImageAtomicSMax32(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBindlessImageAtomicUMax32(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBindlessImageAtomicInc32(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBindlessImageAtomicDec32(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBindlessImageAtomicAnd32(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBindlessImageAtomicOr32(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBindlessImageAtomicXor32(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBindlessImageAtomicExchange32(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBoundImageAtomicIAdd32(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBoundImageAtomicSMin32(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBoundImageAtomicUMin32(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBoundImageAtomicSMax32(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBoundImageAtomicUMax32(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBoundImageAtomicInc32(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBoundImageAtomicDec32(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBoundImageAtomicAnd32(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBoundImageAtomicOr32(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBoundImageAtomicXor32(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
void EmitBoundImageAtomicExchange32(EmitContext&) {
|
||||
throw LogicError("Unreachable instruction");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
625
src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
Normal file
625
src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
Normal file
@@ -0,0 +1,625 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/backend/glasm/reg_alloc.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
enum class Attribute : u64;
|
||||
enum class Patch : u64;
|
||||
class Inst;
|
||||
class Value;
|
||||
} // namespace Shader::IR
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
class EmitContext;
|
||||
|
||||
// Microinstruction emitters
|
||||
void EmitPhi(EmitContext& ctx, IR::Inst& inst);
|
||||
void EmitVoid(EmitContext& ctx);
|
||||
void EmitIdentity(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
|
||||
void EmitConditionRef(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
|
||||
void EmitReference(EmitContext&, const IR::Value& value);
|
||||
void EmitPhiMove(EmitContext& ctx, const IR::Value& phi, const IR::Value& value);
|
||||
void EmitJoin(EmitContext& ctx);
|
||||
void EmitDemoteToHelperInvocation(EmitContext& ctx);
|
||||
void EmitBarrier(EmitContext& ctx);
|
||||
void EmitWorkgroupMemoryBarrier(EmitContext& ctx);
|
||||
void EmitDeviceMemoryBarrier(EmitContext& ctx);
|
||||
void EmitPrologue(EmitContext& ctx);
|
||||
void EmitEpilogue(EmitContext& ctx);
|
||||
void EmitEmitVertex(EmitContext& ctx, ScalarS32 stream);
|
||||
void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream);
|
||||
void EmitGetRegister(EmitContext& ctx);
|
||||
void EmitSetRegister(EmitContext& ctx);
|
||||
void EmitGetPred(EmitContext& ctx);
|
||||
void EmitSetPred(EmitContext& ctx);
|
||||
void EmitSetGotoVariable(EmitContext& ctx);
|
||||
void EmitGetGotoVariable(EmitContext& ctx);
|
||||
void EmitSetIndirectBranchVariable(EmitContext& ctx);
|
||||
void EmitGetIndirectBranchVariable(EmitContext& ctx);
|
||||
void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
|
||||
void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
|
||||
void EmitGetCbufU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
|
||||
void EmitGetCbufS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
|
||||
void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
|
||||
void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
|
||||
void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
|
||||
void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32 vertex);
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, ScalarF32 value, ScalarU32 vertex);
|
||||
void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, ScalarS32 offset, ScalarU32 vertex);
|
||||
void EmitSetAttributeIndexed(EmitContext& ctx, ScalarU32 offset, ScalarF32 value, ScalarU32 vertex);
|
||||
void EmitGetPatch(EmitContext& ctx, IR::Inst& inst, IR::Patch patch);
|
||||
void EmitSetPatch(EmitContext& ctx, IR::Patch patch, ScalarF32 value);
|
||||
void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, ScalarF32 value);
|
||||
void EmitSetSampleMask(EmitContext& ctx, ScalarS32 value);
|
||||
void EmitSetFragDepth(EmitContext& ctx, ScalarF32 value);
|
||||
void EmitGetZFlag(EmitContext& ctx);
|
||||
void EmitGetSFlag(EmitContext& ctx);
|
||||
void EmitGetCFlag(EmitContext& ctx);
|
||||
void EmitGetOFlag(EmitContext& ctx);
|
||||
void EmitSetZFlag(EmitContext& ctx);
|
||||
void EmitSetSFlag(EmitContext& ctx);
|
||||
void EmitSetCFlag(EmitContext& ctx);
|
||||
void EmitSetOFlag(EmitContext& ctx);
|
||||
void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst);
|
||||
void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst);
|
||||
void EmitInvocationId(EmitContext& ctx, IR::Inst& inst);
|
||||
void EmitSampleId(EmitContext& ctx, IR::Inst& inst);
|
||||
void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst);
|
||||
void EmitYDirection(EmitContext& ctx, IR::Inst& inst);
|
||||
void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset);
|
||||
void EmitWriteLocal(EmitContext& ctx, ScalarU32 word_offset, ScalarU32 value);
|
||||
void EmitUndefU1(EmitContext& ctx, IR::Inst& inst);
|
||||
void EmitUndefU8(EmitContext& ctx, IR::Inst& inst);
|
||||
void EmitUndefU16(EmitContext& ctx, IR::Inst& inst);
|
||||
void EmitUndefU32(EmitContext& ctx, IR::Inst& inst);
|
||||
void EmitUndefU64(EmitContext& ctx, IR::Inst& inst);
|
||||
void EmitLoadGlobalU8(EmitContext& ctx, IR::Inst& inst, Register address);
|
||||
void EmitLoadGlobalS8(EmitContext& ctx, IR::Inst& inst, Register address);
|
||||
void EmitLoadGlobalU16(EmitContext& ctx, IR::Inst& inst, Register address);
|
||||
void EmitLoadGlobalS16(EmitContext& ctx, IR::Inst& inst, Register address);
|
||||
void EmitLoadGlobal32(EmitContext& ctx, IR::Inst& inst, Register address);
|
||||
void EmitLoadGlobal64(EmitContext& ctx, IR::Inst& inst, Register address);
|
||||
void EmitLoadGlobal128(EmitContext& ctx, IR::Inst& inst, Register address);
|
||||
void EmitWriteGlobalU8(EmitContext& ctx, Register address, Register value);
|
||||
void EmitWriteGlobalS8(EmitContext& ctx, Register address, Register value);
|
||||
void EmitWriteGlobalU16(EmitContext& ctx, Register address, Register value);
|
||||
void EmitWriteGlobalS16(EmitContext& ctx, Register address, Register value);
|
||||
void EmitWriteGlobal32(EmitContext& ctx, Register address, ScalarU32 value);
|
||||
void EmitWriteGlobal64(EmitContext& ctx, Register address, Register value);
|
||||
void EmitWriteGlobal128(EmitContext& ctx, Register address, Register value);
|
||||
void EmitLoadStorageU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset);
|
||||
void EmitLoadStorageS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset);
|
||||
void EmitLoadStorageU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset);
|
||||
void EmitLoadStorageS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset);
|
||||
void EmitLoadStorage32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset);
|
||||
void EmitLoadStorage64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset);
|
||||
void EmitLoadStorage128(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset);
|
||||
void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
|
||||
ScalarU32 value);
|
||||
void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
|
||||
ScalarS32 value);
|
||||
void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
|
||||
ScalarU32 value);
|
||||
void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
|
||||
ScalarS32 value);
|
||||
void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
|
||||
ScalarU32 value);
|
||||
void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
|
||||
Register value);
|
||||
void EmitWriteStorage128(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
|
||||
Register value);
|
||||
void EmitLoadSharedU8(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset);
|
||||
void EmitLoadSharedS8(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset);
|
||||
void EmitLoadSharedU16(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset);
|
||||
void EmitLoadSharedS16(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset);
|
||||
void EmitLoadSharedU32(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset);
|
||||
void EmitLoadSharedU64(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset);
|
||||
void EmitLoadSharedU128(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset);
|
||||
void EmitWriteSharedU8(EmitContext& ctx, ScalarU32 offset, ScalarU32 value);
|
||||
void EmitWriteSharedU16(EmitContext& ctx, ScalarU32 offset, ScalarU32 value);
|
||||
void EmitWriteSharedU32(EmitContext& ctx, ScalarU32 offset, ScalarU32 value);
|
||||
void EmitWriteSharedU64(EmitContext& ctx, ScalarU32 offset, Register value);
|
||||
void EmitWriteSharedU128(EmitContext& ctx, ScalarU32 offset, Register value);
|
||||
void EmitCompositeConstructU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
|
||||
const IR::Value& e2);
|
||||
void EmitCompositeConstructU32x3(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
|
||||
const IR::Value& e2, const IR::Value& e3);
|
||||
void EmitCompositeConstructU32x4(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
|
||||
const IR::Value& e2, const IR::Value& e3, const IR::Value& e4);
|
||||
void EmitCompositeExtractU32x2(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index);
|
||||
void EmitCompositeExtractU32x3(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index);
|
||||
void EmitCompositeExtractU32x4(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index);
|
||||
void EmitCompositeInsertU32x2(EmitContext& ctx, Register composite, ScalarU32 object, u32 index);
|
||||
void EmitCompositeInsertU32x3(EmitContext& ctx, Register composite, ScalarU32 object, u32 index);
|
||||
void EmitCompositeInsertU32x4(EmitContext& ctx, Register composite, ScalarU32 object, u32 index);
|
||||
void EmitCompositeConstructF16x2(EmitContext& ctx, Register e1, Register e2);
|
||||
void EmitCompositeConstructF16x3(EmitContext& ctx, Register e1, Register e2, Register e3);
|
||||
void EmitCompositeConstructF16x4(EmitContext& ctx, Register e1, Register e2, Register e3,
|
||||
Register e4);
|
||||
void EmitCompositeExtractF16x2(EmitContext& ctx, Register composite, u32 index);
|
||||
void EmitCompositeExtractF16x3(EmitContext& ctx, Register composite, u32 index);
|
||||
void EmitCompositeExtractF16x4(EmitContext& ctx, Register composite, u32 index);
|
||||
void EmitCompositeInsertF16x2(EmitContext& ctx, Register composite, Register object, u32 index);
|
||||
void EmitCompositeInsertF16x3(EmitContext& ctx, Register composite, Register object, u32 index);
|
||||
void EmitCompositeInsertF16x4(EmitContext& ctx, Register composite, Register object, u32 index);
|
||||
void EmitCompositeConstructF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
|
||||
const IR::Value& e2);
|
||||
void EmitCompositeConstructF32x3(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
|
||||
const IR::Value& e2, const IR::Value& e3);
|
||||
void EmitCompositeConstructF32x4(EmitContext& ctx, IR::Inst& inst, const IR::Value& e1,
|
||||
const IR::Value& e2, const IR::Value& e3, const IR::Value& e4);
|
||||
void EmitCompositeExtractF32x2(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index);
|
||||
void EmitCompositeExtractF32x3(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index);
|
||||
void EmitCompositeExtractF32x4(EmitContext& ctx, IR::Inst& inst, Register composite, u32 index);
|
||||
void EmitCompositeInsertF32x2(EmitContext& ctx, IR::Inst& inst, Register composite,
|
||||
ScalarF32 object, u32 index);
|
||||
void EmitCompositeInsertF32x3(EmitContext& ctx, IR::Inst& inst, Register composite,
|
||||
ScalarF32 object, u32 index);
|
||||
void EmitCompositeInsertF32x4(EmitContext& ctx, IR::Inst& inst, Register composite,
|
||||
ScalarF32 object, u32 index);
|
||||
void EmitCompositeConstructF64x2(EmitContext& ctx);
|
||||
void EmitCompositeConstructF64x3(EmitContext& ctx);
|
||||
void EmitCompositeConstructF64x4(EmitContext& ctx);
|
||||
void EmitCompositeExtractF64x2(EmitContext& ctx);
|
||||
void EmitCompositeExtractF64x3(EmitContext& ctx);
|
||||
void EmitCompositeExtractF64x4(EmitContext& ctx);
|
||||
void EmitCompositeInsertF64x2(EmitContext& ctx, Register composite, Register object, u32 index);
|
||||
void EmitCompositeInsertF64x3(EmitContext& ctx, Register composite, Register object, u32 index);
|
||||
void EmitCompositeInsertF64x4(EmitContext& ctx, Register composite, Register object, u32 index);
|
||||
void EmitSelectU1(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, ScalarS32 true_value,
|
||||
ScalarS32 false_value);
|
||||
void EmitSelectU8(EmitContext& ctx, ScalarS32 cond, ScalarS32 true_value, ScalarS32 false_value);
|
||||
void EmitSelectU16(EmitContext& ctx, ScalarS32 cond, ScalarS32 true_value, ScalarS32 false_value);
|
||||
void EmitSelectU32(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, ScalarS32 true_value,
|
||||
ScalarS32 false_value);
|
||||
void EmitSelectU64(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, Register true_value,
|
||||
Register false_value);
|
||||
void EmitSelectF16(EmitContext& ctx, ScalarS32 cond, Register true_value, Register false_value);
|
||||
void EmitSelectF32(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, ScalarS32 true_value,
|
||||
ScalarS32 false_value);
|
||||
void EmitSelectF64(EmitContext& ctx, ScalarS32 cond, Register true_value, Register false_value);
|
||||
void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
|
||||
void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
|
||||
void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
|
||||
void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
|
||||
void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
|
||||
void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
|
||||
void EmitPackUint2x32(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitUnpackUint2x32(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitPackFloat2x16(EmitContext& ctx, Register value);
|
||||
void EmitUnpackFloat2x16(EmitContext& ctx, Register value);
|
||||
void EmitPackHalf2x16(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitUnpackHalf2x16(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitPackDouble2x32(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitUnpackDouble2x32(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitGetZeroFromOp(EmitContext& ctx);
|
||||
void EmitGetSignFromOp(EmitContext& ctx);
|
||||
void EmitGetCarryFromOp(EmitContext& ctx);
|
||||
void EmitGetOverflowFromOp(EmitContext& ctx);
|
||||
void EmitGetSparseFromOp(EmitContext& ctx);
|
||||
void EmitGetInBoundsFromOp(EmitContext& ctx);
|
||||
void EmitFPAbs16(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitFPAbs32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
|
||||
void EmitFPAbs64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
|
||||
void EmitFPAdd16(EmitContext& ctx, IR::Inst& inst, Register a, Register b);
|
||||
void EmitFPAdd32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b);
|
||||
void EmitFPAdd64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b);
|
||||
void EmitFPFma16(EmitContext& ctx, IR::Inst& inst, Register a, Register b, Register c);
|
||||
void EmitFPFma32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b, ScalarF32 c);
|
||||
void EmitFPFma64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b, ScalarF64 c);
|
||||
void EmitFPMax32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b);
|
||||
void EmitFPMax64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b);
|
||||
void EmitFPMin32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b);
|
||||
void EmitFPMin64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b);
|
||||
void EmitFPMul16(EmitContext& ctx, IR::Inst& inst, Register a, Register b);
|
||||
void EmitFPMul32(EmitContext& ctx, IR::Inst& inst, ScalarF32 a, ScalarF32 b);
|
||||
void EmitFPMul64(EmitContext& ctx, IR::Inst& inst, ScalarF64 a, ScalarF64 b);
|
||||
void EmitFPNeg16(EmitContext& ctx, Register value);
|
||||
void EmitFPNeg32(EmitContext& ctx, IR::Inst& inst, ScalarRegister value);
|
||||
void EmitFPNeg64(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitFPSin(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
|
||||
void EmitFPCos(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
|
||||
void EmitFPExp2(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
|
||||
void EmitFPLog2(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
|
||||
void EmitFPRecip32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
|
||||
void EmitFPRecip64(EmitContext& ctx, Register value);
|
||||
void EmitFPRecipSqrt32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
|
||||
void EmitFPRecipSqrt64(EmitContext& ctx, Register value);
|
||||
void EmitFPSqrt(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
|
||||
void EmitFPSaturate16(EmitContext& ctx, Register value);
|
||||
void EmitFPSaturate32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
|
||||
void EmitFPSaturate64(EmitContext& ctx, Register value);
|
||||
void EmitFPClamp16(EmitContext& ctx, Register value, Register min_value, Register max_value);
|
||||
void EmitFPClamp32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value, ScalarF32 min_value,
|
||||
ScalarF32 max_value);
|
||||
void EmitFPClamp64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value, ScalarF64 min_value,
|
||||
ScalarF64 max_value);
|
||||
void EmitFPRoundEven16(EmitContext& ctx, Register value);
|
||||
void EmitFPRoundEven32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
|
||||
void EmitFPRoundEven64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
|
||||
void EmitFPFloor16(EmitContext& ctx, Register value);
|
||||
void EmitFPFloor32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
|
||||
void EmitFPFloor64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
|
||||
void EmitFPCeil16(EmitContext& ctx, Register value);
|
||||
void EmitFPCeil32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
|
||||
void EmitFPCeil64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
|
||||
void EmitFPTrunc16(EmitContext& ctx, Register value);
|
||||
void EmitFPTrunc32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
|
||||
void EmitFPTrunc64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
|
||||
void EmitFPOrdEqual16(EmitContext& ctx, Register lhs, Register rhs);
|
||||
void EmitFPOrdEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
|
||||
void EmitFPOrdEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
|
||||
void EmitFPUnordEqual16(EmitContext& ctx, Register lhs, Register rhs);
|
||||
void EmitFPUnordEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
|
||||
void EmitFPUnordEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
|
||||
void EmitFPOrdNotEqual16(EmitContext& ctx, Register lhs, Register rhs);
|
||||
void EmitFPOrdNotEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
|
||||
void EmitFPOrdNotEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
|
||||
void EmitFPUnordNotEqual16(EmitContext& ctx, Register lhs, Register rhs);
|
||||
void EmitFPUnordNotEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
|
||||
void EmitFPUnordNotEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
|
||||
void EmitFPOrdLessThan16(EmitContext& ctx, Register lhs, Register rhs);
|
||||
void EmitFPOrdLessThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
|
||||
void EmitFPOrdLessThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
|
||||
void EmitFPUnordLessThan16(EmitContext& ctx, Register lhs, Register rhs);
|
||||
void EmitFPUnordLessThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
|
||||
void EmitFPUnordLessThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
|
||||
void EmitFPOrdGreaterThan16(EmitContext& ctx, Register lhs, Register rhs);
|
||||
void EmitFPOrdGreaterThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
|
||||
void EmitFPOrdGreaterThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
|
||||
void EmitFPUnordGreaterThan16(EmitContext& ctx, Register lhs, Register rhs);
|
||||
void EmitFPUnordGreaterThan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
|
||||
void EmitFPUnordGreaterThan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
|
||||
void EmitFPOrdLessThanEqual16(EmitContext& ctx, Register lhs, Register rhs);
|
||||
void EmitFPOrdLessThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
|
||||
void EmitFPOrdLessThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
|
||||
void EmitFPUnordLessThanEqual16(EmitContext& ctx, Register lhs, Register rhs);
|
||||
void EmitFPUnordLessThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
|
||||
void EmitFPUnordLessThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
|
||||
void EmitFPOrdGreaterThanEqual16(EmitContext& ctx, Register lhs, Register rhs);
|
||||
void EmitFPOrdGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
|
||||
void EmitFPOrdGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
|
||||
void EmitFPUnordGreaterThanEqual16(EmitContext& ctx, Register lhs, Register rhs);
|
||||
void EmitFPUnordGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, ScalarF32 lhs, ScalarF32 rhs);
|
||||
void EmitFPUnordGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, ScalarF64 lhs, ScalarF64 rhs);
|
||||
void EmitFPIsNan16(EmitContext& ctx, Register value);
|
||||
void EmitFPIsNan32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
|
||||
void EmitFPIsNan64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
|
||||
void EmitIAdd32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
|
||||
void EmitIAdd64(EmitContext& ctx, IR::Inst& inst, Register a, Register b);
|
||||
void EmitISub32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
|
||||
void EmitISub64(EmitContext& ctx, IR::Inst& inst, Register a, Register b);
|
||||
void EmitIMul32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
|
||||
void EmitINeg32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
|
||||
void EmitINeg64(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitIAbs32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
|
||||
void EmitShiftLeftLogical32(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, ScalarU32 shift);
|
||||
void EmitShiftLeftLogical64(EmitContext& ctx, IR::Inst& inst, ScalarRegister base, ScalarU32 shift);
|
||||
void EmitShiftRightLogical32(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, ScalarU32 shift);
|
||||
void EmitShiftRightLogical64(EmitContext& ctx, IR::Inst& inst, ScalarRegister base,
|
||||
ScalarU32 shift);
|
||||
void EmitShiftRightArithmetic32(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, ScalarS32 shift);
|
||||
void EmitShiftRightArithmetic64(EmitContext& ctx, IR::Inst& inst, ScalarRegister base,
|
||||
ScalarS32 shift);
|
||||
void EmitBitwiseAnd32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
|
||||
void EmitBitwiseOr32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
|
||||
void EmitBitwiseXor32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
|
||||
void EmitBitFieldInsert(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, ScalarS32 insert,
|
||||
ScalarS32 offset, ScalarS32 count);
|
||||
void EmitBitFieldSExtract(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, ScalarS32 offset,
|
||||
ScalarS32 count);
|
||||
void EmitBitFieldUExtract(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, ScalarU32 offset,
|
||||
ScalarU32 count);
|
||||
void EmitBitReverse32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
|
||||
void EmitBitCount32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
|
||||
void EmitBitwiseNot32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
|
||||
void EmitFindSMsb32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
|
||||
void EmitFindUMsb32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value);
|
||||
void EmitSMin32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
|
||||
void EmitUMin32(EmitContext& ctx, IR::Inst& inst, ScalarU32 a, ScalarU32 b);
|
||||
void EmitSMax32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
|
||||
void EmitUMax32(EmitContext& ctx, IR::Inst& inst, ScalarU32 a, ScalarU32 b);
|
||||
void EmitSClamp32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value, ScalarS32 min, ScalarS32 max);
|
||||
void EmitUClamp32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 min, ScalarU32 max);
|
||||
void EmitSLessThan(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs);
|
||||
void EmitULessThan(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs);
|
||||
void EmitIEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs);
|
||||
void EmitSLessThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs);
|
||||
void EmitULessThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs);
|
||||
void EmitSGreaterThan(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs);
|
||||
void EmitUGreaterThan(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs);
|
||||
void EmitINotEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs);
|
||||
void EmitSGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs);
|
||||
void EmitUGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs);
|
||||
void EmitSharedAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
|
||||
ScalarU32 value);
|
||||
void EmitSharedAtomicSMin32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
|
||||
ScalarS32 value);
|
||||
void EmitSharedAtomicUMin32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
|
||||
ScalarU32 value);
|
||||
void EmitSharedAtomicSMax32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
|
||||
ScalarS32 value);
|
||||
void EmitSharedAtomicUMax32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
|
||||
ScalarU32 value);
|
||||
void EmitSharedAtomicInc32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
|
||||
ScalarU32 value);
|
||||
void EmitSharedAtomicDec32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
|
||||
ScalarU32 value);
|
||||
void EmitSharedAtomicAnd32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
|
||||
ScalarU32 value);
|
||||
void EmitSharedAtomicOr32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
|
||||
ScalarU32 value);
|
||||
void EmitSharedAtomicXor32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
|
||||
ScalarU32 value);
|
||||
void EmitSharedAtomicExchange32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
|
||||
ScalarU32 value);
|
||||
void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
|
||||
Register value);
|
||||
void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, ScalarU32 value);
|
||||
void EmitStorageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, ScalarS32 value);
|
||||
void EmitStorageAtomicUMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, ScalarU32 value);
|
||||
void EmitStorageAtomicSMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, ScalarS32 value);
|
||||
void EmitStorageAtomicUMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, ScalarU32 value);
|
||||
void EmitStorageAtomicInc32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, ScalarU32 value);
|
||||
void EmitStorageAtomicDec32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, ScalarU32 value);
|
||||
void EmitStorageAtomicAnd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, ScalarU32 value);
|
||||
void EmitStorageAtomicOr32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, ScalarU32 value);
|
||||
void EmitStorageAtomicXor32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, ScalarU32 value);
|
||||
void EmitStorageAtomicExchange32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, ScalarU32 value);
|
||||
void EmitStorageAtomicIAdd64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value);
|
||||
void EmitStorageAtomicSMin64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value);
|
||||
void EmitStorageAtomicUMin64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value);
|
||||
void EmitStorageAtomicSMax64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value);
|
||||
void EmitStorageAtomicUMax64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value);
|
||||
void EmitStorageAtomicAnd64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value);
|
||||
void EmitStorageAtomicOr64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value);
|
||||
void EmitStorageAtomicXor64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value);
|
||||
void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value);
|
||||
void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, ScalarF32 value);
|
||||
void EmitStorageAtomicAddF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value);
|
||||
void EmitStorageAtomicAddF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value);
|
||||
void EmitStorageAtomicMinF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value);
|
||||
void EmitStorageAtomicMinF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value);
|
||||
void EmitStorageAtomicMaxF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value);
|
||||
void EmitStorageAtomicMaxF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value);
|
||||
void EmitGlobalAtomicIAdd32(EmitContext& ctx);
|
||||
void EmitGlobalAtomicSMin32(EmitContext& ctx);
|
||||
void EmitGlobalAtomicUMin32(EmitContext& ctx);
|
||||
void EmitGlobalAtomicSMax32(EmitContext& ctx);
|
||||
void EmitGlobalAtomicUMax32(EmitContext& ctx);
|
||||
void EmitGlobalAtomicInc32(EmitContext& ctx);
|
||||
void EmitGlobalAtomicDec32(EmitContext& ctx);
|
||||
void EmitGlobalAtomicAnd32(EmitContext& ctx);
|
||||
void EmitGlobalAtomicOr32(EmitContext& ctx);
|
||||
void EmitGlobalAtomicXor32(EmitContext& ctx);
|
||||
void EmitGlobalAtomicExchange32(EmitContext& ctx);
|
||||
void EmitGlobalAtomicIAdd64(EmitContext& ctx);
|
||||
void EmitGlobalAtomicSMin64(EmitContext& ctx);
|
||||
void EmitGlobalAtomicUMin64(EmitContext& ctx);
|
||||
void EmitGlobalAtomicSMax64(EmitContext& ctx);
|
||||
void EmitGlobalAtomicUMax64(EmitContext& ctx);
|
||||
void EmitGlobalAtomicInc64(EmitContext& ctx);
|
||||
void EmitGlobalAtomicDec64(EmitContext& ctx);
|
||||
void EmitGlobalAtomicAnd64(EmitContext& ctx);
|
||||
void EmitGlobalAtomicOr64(EmitContext& ctx);
|
||||
void EmitGlobalAtomicXor64(EmitContext& ctx);
|
||||
void EmitGlobalAtomicExchange64(EmitContext& ctx);
|
||||
void EmitGlobalAtomicAddF32(EmitContext& ctx);
|
||||
void EmitGlobalAtomicAddF16x2(EmitContext& ctx);
|
||||
void EmitGlobalAtomicAddF32x2(EmitContext& ctx);
|
||||
void EmitGlobalAtomicMinF16x2(EmitContext& ctx);
|
||||
void EmitGlobalAtomicMinF32x2(EmitContext& ctx);
|
||||
void EmitGlobalAtomicMaxF16x2(EmitContext& ctx);
|
||||
void EmitGlobalAtomicMaxF32x2(EmitContext& ctx);
|
||||
void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
|
||||
void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
|
||||
void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b);
|
||||
void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
|
||||
void EmitConvertS16F16(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertS16F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
|
||||
void EmitConvertS16F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
|
||||
void EmitConvertS32F16(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertS32F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
|
||||
void EmitConvertS32F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
|
||||
void EmitConvertS64F16(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertS64F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
|
||||
void EmitConvertS64F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
|
||||
void EmitConvertU16F16(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertU16F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
|
||||
void EmitConvertU16F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
|
||||
void EmitConvertU32F16(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertU32F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
|
||||
void EmitConvertU32F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
|
||||
void EmitConvertU64F16(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertU64F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
|
||||
void EmitConvertU64F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
|
||||
void EmitConvertU64U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value);
|
||||
void EmitConvertU32U64(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertF16F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
|
||||
void EmitConvertF32F16(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertF32F64(EmitContext& ctx, IR::Inst& inst, ScalarF64 value);
|
||||
void EmitConvertF64F32(EmitContext& ctx, IR::Inst& inst, ScalarF32 value);
|
||||
void EmitConvertF16S8(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertF16S16(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertF16S32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
|
||||
void EmitConvertF16S64(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertF16U8(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertF16U16(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertF16U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value);
|
||||
void EmitConvertF16U64(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertF32S8(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertF32S16(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertF32S32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
|
||||
void EmitConvertF32S64(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertF32U8(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertF32U16(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertF32U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value);
|
||||
void EmitConvertF32U64(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertF64S8(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertF64S16(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertF64S32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value);
|
||||
void EmitConvertF64S64(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertF64U8(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertF64U16(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitConvertF64U32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value);
|
||||
void EmitConvertF64U64(EmitContext& ctx, IR::Inst& inst, Register value);
|
||||
void EmitBindlessImageSampleImplicitLod(EmitContext&);
|
||||
void EmitBindlessImageSampleExplicitLod(EmitContext&);
|
||||
void EmitBindlessImageSampleDrefImplicitLod(EmitContext&);
|
||||
void EmitBindlessImageSampleDrefExplicitLod(EmitContext&);
|
||||
void EmitBindlessImageGather(EmitContext&);
|
||||
void EmitBindlessImageGatherDref(EmitContext&);
|
||||
void EmitBindlessImageFetch(EmitContext&);
|
||||
void EmitBindlessImageQueryDimensions(EmitContext&);
|
||||
void EmitBindlessImageQueryLod(EmitContext&);
|
||||
void EmitBindlessImageGradient(EmitContext&);
|
||||
void EmitBindlessImageRead(EmitContext&);
|
||||
void EmitBindlessImageWrite(EmitContext&);
|
||||
void EmitBoundImageSampleImplicitLod(EmitContext&);
|
||||
void EmitBoundImageSampleExplicitLod(EmitContext&);
|
||||
void EmitBoundImageSampleDrefImplicitLod(EmitContext&);
|
||||
void EmitBoundImageSampleDrefExplicitLod(EmitContext&);
|
||||
void EmitBoundImageGather(EmitContext&);
|
||||
void EmitBoundImageGatherDref(EmitContext&);
|
||||
void EmitBoundImageFetch(EmitContext&);
|
||||
void EmitBoundImageQueryDimensions(EmitContext&);
|
||||
void EmitBoundImageQueryLod(EmitContext&);
|
||||
void EmitBoundImageGradient(EmitContext&);
|
||||
void EmitBoundImageRead(EmitContext&);
|
||||
void EmitBoundImageWrite(EmitContext&);
|
||||
void EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
|
||||
const IR::Value& coord, Register bias_lc, const IR::Value& offset);
|
||||
void EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
|
||||
const IR::Value& coord, ScalarF32 lod, const IR::Value& offset);
|
||||
void EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
|
||||
const IR::Value& coord, const IR::Value& dref,
|
||||
const IR::Value& bias_lc, const IR::Value& offset);
|
||||
void EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
|
||||
const IR::Value& coord, const IR::Value& dref,
|
||||
const IR::Value& lod, const IR::Value& offset);
|
||||
void EmitImageGather(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
|
||||
const IR::Value& coord, const IR::Value& offset, const IR::Value& offset2);
|
||||
void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
|
||||
const IR::Value& coord, const IR::Value& offset, const IR::Value& offset2,
|
||||
const IR::Value& dref);
|
||||
void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
|
||||
const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms);
|
||||
void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
|
||||
ScalarS32 lod);
|
||||
void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord);
|
||||
void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
|
||||
const IR::Value& coord, const IR::Value& derivatives,
|
||||
const IR::Value& offset, const IR::Value& lod_clamp);
|
||||
void EmitImageRead(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord);
|
||||
void EmitImageWrite(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
|
||||
Register color);
|
||||
void EmitBindlessImageAtomicIAdd32(EmitContext&);
|
||||
void EmitBindlessImageAtomicSMin32(EmitContext&);
|
||||
void EmitBindlessImageAtomicUMin32(EmitContext&);
|
||||
void EmitBindlessImageAtomicSMax32(EmitContext&);
|
||||
void EmitBindlessImageAtomicUMax32(EmitContext&);
|
||||
void EmitBindlessImageAtomicInc32(EmitContext&);
|
||||
void EmitBindlessImageAtomicDec32(EmitContext&);
|
||||
void EmitBindlessImageAtomicAnd32(EmitContext&);
|
||||
void EmitBindlessImageAtomicOr32(EmitContext&);
|
||||
void EmitBindlessImageAtomicXor32(EmitContext&);
|
||||
void EmitBindlessImageAtomicExchange32(EmitContext&);
|
||||
void EmitBoundImageAtomicIAdd32(EmitContext&);
|
||||
void EmitBoundImageAtomicSMin32(EmitContext&);
|
||||
void EmitBoundImageAtomicUMin32(EmitContext&);
|
||||
void EmitBoundImageAtomicSMax32(EmitContext&);
|
||||
void EmitBoundImageAtomicUMax32(EmitContext&);
|
||||
void EmitBoundImageAtomicInc32(EmitContext&);
|
||||
void EmitBoundImageAtomicDec32(EmitContext&);
|
||||
void EmitBoundImageAtomicAnd32(EmitContext&);
|
||||
void EmitBoundImageAtomicOr32(EmitContext&);
|
||||
void EmitBoundImageAtomicXor32(EmitContext&);
|
||||
void EmitBoundImageAtomicExchange32(EmitContext&);
|
||||
void EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
|
||||
ScalarU32 value);
|
||||
void EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
|
||||
ScalarS32 value);
|
||||
void EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
|
||||
ScalarU32 value);
|
||||
void EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
|
||||
ScalarS32 value);
|
||||
void EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
|
||||
ScalarU32 value);
|
||||
void EmitImageAtomicInc32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
|
||||
ScalarU32 value);
|
||||
void EmitImageAtomicDec32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
|
||||
ScalarU32 value);
|
||||
void EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
|
||||
ScalarU32 value);
|
||||
void EmitImageAtomicOr32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
|
||||
ScalarU32 value);
|
||||
void EmitImageAtomicXor32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
|
||||
ScalarU32 value);
|
||||
void EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
|
||||
Register coord, ScalarU32 value);
|
||||
void EmitLaneId(EmitContext& ctx, IR::Inst& inst);
|
||||
void EmitVoteAll(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred);
|
||||
void EmitVoteAny(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred);
|
||||
void EmitVoteEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred);
|
||||
void EmitSubgroupBallot(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred);
|
||||
void EmitSubgroupEqMask(EmitContext& ctx, IR::Inst& inst);
|
||||
void EmitSubgroupLtMask(EmitContext& ctx, IR::Inst& inst);
|
||||
void EmitSubgroupLeMask(EmitContext& ctx, IR::Inst& inst);
|
||||
void EmitSubgroupGtMask(EmitContext& ctx, IR::Inst& inst);
|
||||
void EmitSubgroupGeMask(EmitContext& ctx, IR::Inst& inst);
|
||||
void EmitShuffleIndex(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
|
||||
const IR::Value& clamp, const IR::Value& segmentation_mask);
|
||||
void EmitShuffleUp(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
|
||||
const IR::Value& clamp, const IR::Value& segmentation_mask);
|
||||
void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
|
||||
const IR::Value& clamp, const IR::Value& segmentation_mask);
|
||||
void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
|
||||
const IR::Value& clamp, const IR::Value& segmentation_mask);
|
||||
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a, ScalarF32 op_b,
|
||||
ScalarU32 swizzle);
|
||||
void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a);
|
||||
void EmitDPdyFine(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a);
|
||||
void EmitDPdxCoarse(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a);
|
||||
void EmitDPdyCoarse(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a);
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
294
src/shader_recompiler/backend/glasm/emit_glasm_integer.cpp
Normal file
294
src/shader_recompiler/backend/glasm/emit_glasm_integer.cpp
Normal file
@@ -0,0 +1,294 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_context.h"
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
namespace {
|
||||
void BitwiseLogicalOp(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b,
|
||||
std::string_view lop) {
|
||||
const auto zero = inst.GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp);
|
||||
const auto sign = inst.GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp);
|
||||
if (zero) {
|
||||
zero->Invalidate();
|
||||
}
|
||||
if (sign) {
|
||||
sign->Invalidate();
|
||||
}
|
||||
if (zero || sign) {
|
||||
ctx.reg_alloc.InvalidateConditionCodes();
|
||||
}
|
||||
const auto ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("{}.S {}.x,{},{};", lop, ret, a, b);
|
||||
if (zero) {
|
||||
ctx.Add("SEQ.S {},{},0;", *zero, ret);
|
||||
}
|
||||
if (sign) {
|
||||
ctx.Add("SLT.S {},{},0;", *sign, ret);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitIAdd32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
const std::array flags{
|
||||
inst.GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp),
|
||||
inst.GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp),
|
||||
inst.GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp),
|
||||
inst.GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp),
|
||||
};
|
||||
for (IR::Inst* const flag_inst : flags) {
|
||||
if (flag_inst) {
|
||||
flag_inst->Invalidate();
|
||||
}
|
||||
}
|
||||
const bool cc{inst.HasAssociatedPseudoOperation()};
|
||||
const std::string_view cc_mod{cc ? ".CC" : ""};
|
||||
if (cc) {
|
||||
ctx.reg_alloc.InvalidateConditionCodes();
|
||||
}
|
||||
const auto ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("ADD.S{} {}.x,{},{};", cc_mod, ret, a, b);
|
||||
if (!cc) {
|
||||
return;
|
||||
}
|
||||
static constexpr std::array<std::string_view, 4> masks{"", "SF", "CF", "OF"};
|
||||
for (size_t flag_index = 0; flag_index < flags.size(); ++flag_index) {
|
||||
if (!flags[flag_index]) {
|
||||
continue;
|
||||
}
|
||||
const auto flag_ret{ctx.reg_alloc.Define(*flags[flag_index])};
|
||||
if (flag_index == 0) {
|
||||
ctx.Add("SEQ.S {}.x,{}.x,0;", flag_ret, ret);
|
||||
} else {
|
||||
// We could use conditional execution here, but it's broken on Nvidia's compiler
|
||||
ctx.Add("IF {}.x;"
|
||||
"MOV.S {}.x,-1;"
|
||||
"ELSE;"
|
||||
"MOV.S {}.x,0;"
|
||||
"ENDIF;",
|
||||
masks[flag_index], flag_ret, flag_ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmitIAdd64(EmitContext& ctx, IR::Inst& inst, Register a, Register b) {
|
||||
ctx.LongAdd("ADD.S64 {}.x,{}.x,{}.x;", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitISub32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
ctx.Add("SUB.S {}.x,{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitISub64(EmitContext& ctx, IR::Inst& inst, Register a, Register b) {
|
||||
ctx.LongAdd("SUB.S64 {}.x,{}.x,{}.x;", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitIMul32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
ctx.Add("MUL.S {}.x,{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitINeg32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
if (value.type != Type::Register && static_cast<s32>(value.imm_u32) < 0) {
|
||||
ctx.Add("MOV.S {},{};", inst, -static_cast<s32>(value.imm_u32));
|
||||
} else {
|
||||
ctx.Add("MOV.S {},-{};", inst, value);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitINeg64(EmitContext& ctx, IR::Inst& inst, Register value) {
|
||||
ctx.LongAdd("MOV.S64 {},-{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitIAbs32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
ctx.Add("ABS.S {},{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitShiftLeftLogical32(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, ScalarU32 shift) {
|
||||
ctx.Add("SHL.U {}.x,{},{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftLeftLogical64(EmitContext& ctx, IR::Inst& inst, ScalarRegister base,
|
||||
ScalarU32 shift) {
|
||||
ctx.LongAdd("SHL.U64 {}.x,{},{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightLogical32(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, ScalarU32 shift) {
|
||||
ctx.Add("SHR.U {}.x,{},{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightLogical64(EmitContext& ctx, IR::Inst& inst, ScalarRegister base,
|
||||
ScalarU32 shift) {
|
||||
ctx.LongAdd("SHR.U64 {}.x,{},{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightArithmetic32(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, ScalarS32 shift) {
|
||||
ctx.Add("SHR.S {}.x,{},{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitShiftRightArithmetic64(EmitContext& ctx, IR::Inst& inst, ScalarRegister base,
|
||||
ScalarS32 shift) {
|
||||
ctx.LongAdd("SHR.S64 {}.x,{},{};", inst, base, shift);
|
||||
}
|
||||
|
||||
void EmitBitwiseAnd32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
BitwiseLogicalOp(ctx, inst, a, b, "AND");
|
||||
}
|
||||
|
||||
void EmitBitwiseOr32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
BitwiseLogicalOp(ctx, inst, a, b, "OR");
|
||||
}
|
||||
|
||||
void EmitBitwiseXor32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
BitwiseLogicalOp(ctx, inst, a, b, "XOR");
|
||||
}
|
||||
|
||||
void EmitBitFieldInsert(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, ScalarS32 insert,
|
||||
ScalarS32 offset, ScalarS32 count) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (count.type != Type::Register && offset.type != Type::Register) {
|
||||
ctx.Add("BFI.S {},{{{},{},0,0}},{},{};", ret, count, offset, insert, base);
|
||||
} else {
|
||||
ctx.Add("MOV.S RC.x,{};"
|
||||
"MOV.S RC.y,{};"
|
||||
"BFI.S {},RC,{},{};",
|
||||
count, offset, ret, insert, base);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitBitFieldSExtract(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, ScalarS32 offset,
|
||||
ScalarS32 count) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (count.type != Type::Register && offset.type != Type::Register) {
|
||||
ctx.Add("BFE.S {},{{{},{},0,0}},{};", ret, count, offset, base);
|
||||
} else {
|
||||
ctx.Add("MOV.S RC.x,{};"
|
||||
"MOV.S RC.y,{};"
|
||||
"BFE.S {},RC,{};",
|
||||
count, offset, ret, base);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitBitFieldUExtract(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, ScalarU32 offset,
|
||||
ScalarU32 count) {
|
||||
const auto zero = inst.GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp);
|
||||
const auto sign = inst.GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp);
|
||||
if (zero) {
|
||||
zero->Invalidate();
|
||||
}
|
||||
if (sign) {
|
||||
sign->Invalidate();
|
||||
}
|
||||
if (zero || sign) {
|
||||
ctx.reg_alloc.InvalidateConditionCodes();
|
||||
}
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (count.type != Type::Register && offset.type != Type::Register) {
|
||||
ctx.Add("BFE.U {},{{{},{},0,0}},{};", ret, count, offset, base);
|
||||
} else {
|
||||
ctx.Add("MOV.U RC.x,{};"
|
||||
"MOV.U RC.y,{};"
|
||||
"BFE.U {},RC,{};",
|
||||
count, offset, ret, base);
|
||||
}
|
||||
if (zero) {
|
||||
ctx.Add("SEQ.S {},{},0;", *zero, ret);
|
||||
}
|
||||
if (sign) {
|
||||
ctx.Add("SLT.S {},{},0;", *sign, ret);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitBitReverse32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
ctx.Add("BFR {},{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitCount32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
ctx.Add("BTC {},{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitBitwiseNot32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
ctx.Add("NOT.S {},{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFindSMsb32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
ctx.Add("BTFM.S {},{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitFindUMsb32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value) {
|
||||
ctx.Add("BTFM.U {},{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitSMin32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
ctx.Add("MIN.S {},{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitUMin32(EmitContext& ctx, IR::Inst& inst, ScalarU32 a, ScalarU32 b) {
|
||||
ctx.Add("MIN.U {},{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitSMax32(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
ctx.Add("MAX.S {},{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitUMax32(EmitContext& ctx, IR::Inst& inst, ScalarU32 a, ScalarU32 b) {
|
||||
ctx.Add("MAX.U {},{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitSClamp32(EmitContext& ctx, IR::Inst& inst, ScalarS32 value, ScalarS32 min, ScalarS32 max) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("MIN.S RC.x,{},{};"
|
||||
"MAX.S {}.x,RC.x,{};",
|
||||
max, value, ret, min);
|
||||
}
|
||||
|
||||
void EmitUClamp32(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 min, ScalarU32 max) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("MIN.U RC.x,{},{};"
|
||||
"MAX.U {}.x,RC.x,{};",
|
||||
max, value, ret, min);
|
||||
}
|
||||
|
||||
void EmitSLessThan(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
|
||||
ctx.Add("SLT.S {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitULessThan(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs) {
|
||||
ctx.Add("SLT.U {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitIEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
|
||||
ctx.Add("SEQ.S {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitSLessThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
|
||||
ctx.Add("SLE.S {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitULessThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs) {
|
||||
ctx.Add("SLE.U {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitSGreaterThan(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
|
||||
ctx.Add("SGT.S {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitUGreaterThan(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs) {
|
||||
ctx.Add("SGT.U {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitINotEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
|
||||
ctx.Add("SNE.U {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitSGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 lhs, ScalarS32 rhs) {
|
||||
ctx.Add("SGE.S {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
void EmitUGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, ScalarU32 lhs, ScalarU32 rhs) {
|
||||
ctx.Add("SGE.U {}.x,{},{};", inst, lhs, rhs);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
568
src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp
Normal file
568
src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp
Normal file
@@ -0,0 +1,568 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_context.h"
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
namespace {
|
||||
void StorageOp(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
|
||||
std::string_view then_expr, std::string_view else_expr = {}) {
|
||||
// Operate on bindless SSBO, call the expression with bounds checking
|
||||
// address = c[binding].xy
|
||||
// length = c[binding].z
|
||||
const u32 sb_binding{binding.U32()};
|
||||
ctx.Add("PK64.U DC,c[{}];" // pointer = address
|
||||
"CVT.U64.U32 DC.z,{};" // offset = uint64_t(offset)
|
||||
"ADD.U64 DC.x,DC.x,DC.z;" // pointer += offset
|
||||
"SLT.U.CC RC.x,{},c[{}].z;", // cc = offset < length
|
||||
sb_binding, offset, offset, sb_binding);
|
||||
if (else_expr.empty()) {
|
||||
ctx.Add("IF NE.x;{}ENDIF;", then_expr);
|
||||
} else {
|
||||
ctx.Add("IF NE.x;{}ELSE;{}ENDIF;", then_expr, else_expr);
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalStorageOp(EmitContext& ctx, Register address, bool pointer_based, std::string_view expr,
|
||||
std::string_view else_expr = {}) {
|
||||
const size_t num_buffers{ctx.info.storage_buffers_descriptors.size()};
|
||||
for (size_t index = 0; index < num_buffers; ++index) {
|
||||
if (!ctx.info.nvn_buffer_used[index]) {
|
||||
continue;
|
||||
}
|
||||
const auto& ssbo{ctx.info.storage_buffers_descriptors[index]};
|
||||
ctx.Add("LDC.U64 DC.x,c{}[{}];" // ssbo_addr
|
||||
"LDC.U32 RC.x,c{}[{}];" // ssbo_size_u32
|
||||
"CVT.U64.U32 DC.y,RC.x;" // ssbo_size = ssbo_size_u32
|
||||
"ADD.U64 DC.y,DC.y,DC.x;" // ssbo_end = ssbo_addr + ssbo_size
|
||||
"SGE.U64 RC.x,{}.x,DC.x;" // a = input_addr >= ssbo_addr ? -1 : 0
|
||||
"SLT.U64 RC.y,{}.x,DC.y;" // b = input_addr < ssbo_end ? -1 : 0
|
||||
"AND.U.CC RC.x,RC.x,RC.y;" // cond = a && b
|
||||
"IF NE.x;" // if cond
|
||||
"SUB.U64 DC.x,{}.x,DC.x;", // offset = input_addr - ssbo_addr
|
||||
ssbo.cbuf_index, ssbo.cbuf_offset, ssbo.cbuf_index, ssbo.cbuf_offset + 8, address,
|
||||
address, address);
|
||||
if (pointer_based) {
|
||||
ctx.Add("PK64.U DC.y,c[{}];" // host_ssbo = cbuf
|
||||
"ADD.U64 DC.x,DC.x,DC.y;" // host_addr = host_ssbo + offset
|
||||
"{}"
|
||||
"ELSE;",
|
||||
index, expr);
|
||||
} else {
|
||||
ctx.Add("CVT.U32.U64 RC.x,DC.x;"
|
||||
"{},ssbo{}[RC.x];"
|
||||
"ELSE;",
|
||||
expr, index);
|
||||
}
|
||||
}
|
||||
if (!else_expr.empty()) {
|
||||
ctx.Add("{}", else_expr);
|
||||
}
|
||||
const size_t num_used_buffers{ctx.info.nvn_buffer_used.count()};
|
||||
for (size_t index = 0; index < num_used_buffers; ++index) {
|
||||
ctx.Add("ENDIF;");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ValueType>
|
||||
void Write(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset, ValueType value,
|
||||
std::string_view size) {
|
||||
if (ctx.runtime_info.glasm_use_storage_buffers) {
|
||||
ctx.Add("STB.{} {},ssbo{}[{}];", size, value, binding.U32(), offset);
|
||||
} else {
|
||||
StorageOp(ctx, binding, offset, fmt::format("STORE.{} {},DC.x;", size, value));
|
||||
}
|
||||
}
|
||||
|
||||
void Load(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset,
|
||||
std::string_view size) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (ctx.runtime_info.glasm_use_storage_buffers) {
|
||||
ctx.Add("LDB.{} {},ssbo{}[{}];", size, ret, binding.U32(), offset);
|
||||
} else {
|
||||
StorageOp(ctx, binding, offset, fmt::format("LOAD.{} {},DC.x;", size, ret),
|
||||
fmt::format("MOV.U {},{{0,0,0,0}};", ret));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ValueType>
|
||||
void GlobalWrite(EmitContext& ctx, Register address, ValueType value, std::string_view size) {
|
||||
if (ctx.runtime_info.glasm_use_storage_buffers) {
|
||||
GlobalStorageOp(ctx, address, false, fmt::format("STB.{} {}", size, value));
|
||||
} else {
|
||||
GlobalStorageOp(ctx, address, true, fmt::format("STORE.{} {},DC.x;", size, value));
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalLoad(EmitContext& ctx, IR::Inst& inst, Register address, std::string_view size) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (ctx.runtime_info.glasm_use_storage_buffers) {
|
||||
GlobalStorageOp(ctx, address, false, fmt::format("LDB.{} {}", size, ret));
|
||||
} else {
|
||||
GlobalStorageOp(ctx, address, true, fmt::format("LOAD.{} {},DC.x;", size, ret),
|
||||
fmt::format("MOV.S {},0;", ret));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ValueType>
|
||||
void Atom(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset,
|
||||
ValueType value, std::string_view operation, std::string_view size) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
if (ctx.runtime_info.glasm_use_storage_buffers) {
|
||||
ctx.Add("ATOMB.{}.{} {},{},ssbo{}[{}];", operation, size, ret, value, binding.U32(),
|
||||
offset);
|
||||
} else {
|
||||
StorageOp(ctx, binding, offset,
|
||||
fmt::format("ATOM.{}.{} {},{},DC.x;", operation, size, ret, value));
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitLoadGlobalU8(EmitContext& ctx, IR::Inst& inst, Register address) {
|
||||
GlobalLoad(ctx, inst, address, "U8");
|
||||
}
|
||||
|
||||
void EmitLoadGlobalS8(EmitContext& ctx, IR::Inst& inst, Register address) {
|
||||
GlobalLoad(ctx, inst, address, "S8");
|
||||
}
|
||||
|
||||
void EmitLoadGlobalU16(EmitContext& ctx, IR::Inst& inst, Register address) {
|
||||
GlobalLoad(ctx, inst, address, "U16");
|
||||
}
|
||||
|
||||
void EmitLoadGlobalS16(EmitContext& ctx, IR::Inst& inst, Register address) {
|
||||
GlobalLoad(ctx, inst, address, "S16");
|
||||
}
|
||||
|
||||
void EmitLoadGlobal32(EmitContext& ctx, IR::Inst& inst, Register address) {
|
||||
GlobalLoad(ctx, inst, address, "U32");
|
||||
}
|
||||
|
||||
void EmitLoadGlobal64(EmitContext& ctx, IR::Inst& inst, Register address) {
|
||||
GlobalLoad(ctx, inst, address, "U32X2");
|
||||
}
|
||||
|
||||
void EmitLoadGlobal128(EmitContext& ctx, IR::Inst& inst, Register address) {
|
||||
GlobalLoad(ctx, inst, address, "U32X4");
|
||||
}
|
||||
|
||||
void EmitWriteGlobalU8(EmitContext& ctx, Register address, Register value) {
|
||||
GlobalWrite(ctx, address, value, "U8");
|
||||
}
|
||||
|
||||
void EmitWriteGlobalS8(EmitContext& ctx, Register address, Register value) {
|
||||
GlobalWrite(ctx, address, value, "S8");
|
||||
}
|
||||
|
||||
void EmitWriteGlobalU16(EmitContext& ctx, Register address, Register value) {
|
||||
GlobalWrite(ctx, address, value, "U16");
|
||||
}
|
||||
|
||||
void EmitWriteGlobalS16(EmitContext& ctx, Register address, Register value) {
|
||||
GlobalWrite(ctx, address, value, "S16");
|
||||
}
|
||||
|
||||
void EmitWriteGlobal32(EmitContext& ctx, Register address, ScalarU32 value) {
|
||||
GlobalWrite(ctx, address, value, "U32");
|
||||
}
|
||||
|
||||
void EmitWriteGlobal64(EmitContext& ctx, Register address, Register value) {
|
||||
GlobalWrite(ctx, address, value, "U32X2");
|
||||
}
|
||||
|
||||
void EmitWriteGlobal128(EmitContext& ctx, Register address, Register value) {
|
||||
GlobalWrite(ctx, address, value, "U32X4");
|
||||
}
|
||||
|
||||
void EmitLoadStorageU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset) {
|
||||
Load(ctx, inst, binding, offset, "U8");
|
||||
}
|
||||
|
||||
void EmitLoadStorageS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset) {
|
||||
Load(ctx, inst, binding, offset, "S8");
|
||||
}
|
||||
|
||||
void EmitLoadStorageU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset) {
|
||||
Load(ctx, inst, binding, offset, "U16");
|
||||
}
|
||||
|
||||
void EmitLoadStorageS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset) {
|
||||
Load(ctx, inst, binding, offset, "S16");
|
||||
}
|
||||
|
||||
void EmitLoadStorage32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset) {
|
||||
Load(ctx, inst, binding, offset, "U32");
|
||||
}
|
||||
|
||||
void EmitLoadStorage64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset) {
|
||||
Load(ctx, inst, binding, offset, "U32X2");
|
||||
}
|
||||
|
||||
void EmitLoadStorage128(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset) {
|
||||
Load(ctx, inst, binding, offset, "U32X4");
|
||||
}
|
||||
|
||||
void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
|
||||
ScalarU32 value) {
|
||||
Write(ctx, binding, offset, value, "U8");
|
||||
}
|
||||
|
||||
void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
|
||||
ScalarS32 value) {
|
||||
Write(ctx, binding, offset, value, "S8");
|
||||
}
|
||||
|
||||
void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
|
||||
ScalarU32 value) {
|
||||
Write(ctx, binding, offset, value, "U16");
|
||||
}
|
||||
|
||||
void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
|
||||
ScalarS32 value) {
|
||||
Write(ctx, binding, offset, value, "S16");
|
||||
}
|
||||
|
||||
void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
|
||||
ScalarU32 value) {
|
||||
Write(ctx, binding, offset, value, "U32");
|
||||
}
|
||||
|
||||
void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
|
||||
Register value) {
|
||||
Write(ctx, binding, offset, value, "U32X2");
|
||||
}
|
||||
|
||||
void EmitWriteStorage128(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
|
||||
Register value) {
|
||||
Write(ctx, binding, offset, value, "U32X4");
|
||||
}
|
||||
|
||||
void EmitSharedAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
|
||||
ScalarU32 value) {
|
||||
ctx.Add("ATOMS.ADD.U32 {},{},shared_mem[{}];", inst, value, pointer_offset);
|
||||
}
|
||||
|
||||
void EmitSharedAtomicSMin32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
|
||||
ScalarS32 value) {
|
||||
ctx.Add("ATOMS.MIN.S32 {},{},shared_mem[{}];", inst, value, pointer_offset);
|
||||
}
|
||||
|
||||
void EmitSharedAtomicUMin32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
|
||||
ScalarU32 value) {
|
||||
ctx.Add("ATOMS.MIN.U32 {},{},shared_mem[{}];", inst, value, pointer_offset);
|
||||
}
|
||||
|
||||
void EmitSharedAtomicSMax32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
|
||||
ScalarS32 value) {
|
||||
ctx.Add("ATOMS.MAX.S32 {},{},shared_mem[{}];", inst, value, pointer_offset);
|
||||
}
|
||||
|
||||
void EmitSharedAtomicUMax32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
|
||||
ScalarU32 value) {
|
||||
ctx.Add("ATOMS.MAX.U32 {},{},shared_mem[{}];", inst, value, pointer_offset);
|
||||
}
|
||||
|
||||
void EmitSharedAtomicInc32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
|
||||
ScalarU32 value) {
|
||||
ctx.Add("ATOMS.IWRAP.U32 {},{},shared_mem[{}];", inst, value, pointer_offset);
|
||||
}
|
||||
|
||||
void EmitSharedAtomicDec32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
|
||||
ScalarU32 value) {
|
||||
ctx.Add("ATOMS.DWRAP.U32 {},{},shared_mem[{}];", inst, value, pointer_offset);
|
||||
}
|
||||
|
||||
void EmitSharedAtomicAnd32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
|
||||
ScalarU32 value) {
|
||||
ctx.Add("ATOMS.AND.U32 {},{},shared_mem[{}];", inst, value, pointer_offset);
|
||||
}
|
||||
|
||||
void EmitSharedAtomicOr32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
|
||||
ScalarU32 value) {
|
||||
ctx.Add("ATOMS.OR.U32 {},{},shared_mem[{}];", inst, value, pointer_offset);
|
||||
}
|
||||
|
||||
void EmitSharedAtomicXor32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
|
||||
ScalarU32 value) {
|
||||
ctx.Add("ATOMS.XOR.U32 {},{},shared_mem[{}];", inst, value, pointer_offset);
|
||||
}
|
||||
|
||||
void EmitSharedAtomicExchange32(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
|
||||
ScalarU32 value) {
|
||||
ctx.Add("ATOMS.EXCH.U32 {},{},shared_mem[{}];", inst, value, pointer_offset);
|
||||
}
|
||||
|
||||
void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
|
||||
Register value) {
|
||||
ctx.LongAdd("ATOMS.EXCH.U64 {}.x,{},shared_mem[{}];", inst, value, pointer_offset);
|
||||
}
|
||||
|
||||
void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, ScalarU32 value) {
|
||||
Atom(ctx, inst, binding, offset, value, "ADD", "U32");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, ScalarS32 value) {
|
||||
Atom(ctx, inst, binding, offset, value, "MIN", "S32");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicUMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, ScalarU32 value) {
|
||||
Atom(ctx, inst, binding, offset, value, "MIN", "U32");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicSMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, ScalarS32 value) {
|
||||
Atom(ctx, inst, binding, offset, value, "MAX", "S32");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicUMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, ScalarU32 value) {
|
||||
Atom(ctx, inst, binding, offset, value, "MAX", "U32");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicInc32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, ScalarU32 value) {
|
||||
Atom(ctx, inst, binding, offset, value, "IWRAP", "U32");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicDec32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, ScalarU32 value) {
|
||||
Atom(ctx, inst, binding, offset, value, "DWRAP", "U32");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicAnd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, ScalarU32 value) {
|
||||
Atom(ctx, inst, binding, offset, value, "AND", "U32");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicOr32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, ScalarU32 value) {
|
||||
Atom(ctx, inst, binding, offset, value, "OR", "U32");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicXor32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, ScalarU32 value) {
|
||||
Atom(ctx, inst, binding, offset, value, "XOR", "U32");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicExchange32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, ScalarU32 value) {
|
||||
Atom(ctx, inst, binding, offset, value, "EXCH", "U32");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicIAdd64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value) {
|
||||
Atom(ctx, inst, binding, offset, value, "ADD", "U64");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicSMin64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value) {
|
||||
Atom(ctx, inst, binding, offset, value, "MIN", "S64");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicUMin64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value) {
|
||||
Atom(ctx, inst, binding, offset, value, "MIN", "U64");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicSMax64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value) {
|
||||
Atom(ctx, inst, binding, offset, value, "MAX", "S64");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicUMax64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value) {
|
||||
Atom(ctx, inst, binding, offset, value, "MAX", "U64");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicAnd64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value) {
|
||||
Atom(ctx, inst, binding, offset, value, "AND", "U64");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicOr64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value) {
|
||||
Atom(ctx, inst, binding, offset, value, "OR", "U64");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicXor64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value) {
|
||||
Atom(ctx, inst, binding, offset, value, "XOR", "U64");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value) {
|
||||
Atom(ctx, inst, binding, offset, value, "EXCH", "U64");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, ScalarF32 value) {
|
||||
Atom(ctx, inst, binding, offset, value, "ADD", "F32");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicAddF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value) {
|
||||
Atom(ctx, inst, binding, offset, value, "ADD", "F16x2");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicAddF32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] const IR::Value& binding,
|
||||
[[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicMinF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value) {
|
||||
Atom(ctx, inst, binding, offset, value, "MIN", "F16x2");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicMinF32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] const IR::Value& binding,
|
||||
[[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicMaxF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
ScalarU32 offset, Register value) {
|
||||
Atom(ctx, inst, binding, offset, value, "MAX", "F16x2");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicMaxF32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
|
||||
[[maybe_unused]] const IR::Value& binding,
|
||||
[[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicIAdd32(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicSMin32(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicUMin32(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicSMax32(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicUMax32(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicInc32(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicDec32(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicAnd32(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicOr32(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicXor32(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicExchange32(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicIAdd64(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicSMin64(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicUMin64(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicSMax64(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicUMax64(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicInc64(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicDec64(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicAnd64(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicOr64(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicXor64(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicExchange64(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicAddF32(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicAddF16x2(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicAddF32x2(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicMinF16x2(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicMinF32x2(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicMaxF16x2(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicMaxF32x2(EmitContext&) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
@@ -0,0 +1,273 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_context.h"
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4100)
|
||||
#endif
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
#define NotImplemented() throw NotImplementedException("GLASM instruction {}", __LINE__)
|
||||
|
||||
static void DefinePhi(EmitContext& ctx, IR::Inst& phi) {
|
||||
switch (phi.Arg(0).Type()) {
|
||||
case IR::Type::U1:
|
||||
case IR::Type::U32:
|
||||
case IR::Type::F32:
|
||||
ctx.reg_alloc.Define(phi);
|
||||
break;
|
||||
case IR::Type::U64:
|
||||
case IR::Type::F64:
|
||||
ctx.reg_alloc.LongDefine(phi);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Phi node type {}", phi.Type());
|
||||
}
|
||||
}
|
||||
|
||||
void EmitPhi(EmitContext& ctx, IR::Inst& phi) {
|
||||
const size_t num_args{phi.NumArgs()};
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
ctx.reg_alloc.Consume(phi.Arg(i));
|
||||
}
|
||||
if (!phi.Definition<Id>().is_valid) {
|
||||
// The phi node wasn't forward defined
|
||||
DefinePhi(ctx, phi);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitVoid(EmitContext&) {}
|
||||
|
||||
void EmitReference(EmitContext& ctx, const IR::Value& value) {
|
||||
ctx.reg_alloc.Consume(value);
|
||||
}
|
||||
|
||||
void EmitPhiMove(EmitContext& ctx, const IR::Value& phi_value, const IR::Value& value) {
|
||||
IR::Inst& phi{RegAlloc::AliasInst(*phi_value.Inst())};
|
||||
if (!phi.Definition<Id>().is_valid) {
|
||||
// The phi node wasn't forward defined
|
||||
DefinePhi(ctx, phi);
|
||||
}
|
||||
const Register phi_reg{ctx.reg_alloc.Consume(IR::Value{&phi})};
|
||||
const Value eval_value{ctx.reg_alloc.Consume(value)};
|
||||
|
||||
if (phi_reg == eval_value) {
|
||||
return;
|
||||
}
|
||||
switch (phi.Flags<IR::Type>()) {
|
||||
case IR::Type::U1:
|
||||
case IR::Type::U32:
|
||||
case IR::Type::F32:
|
||||
ctx.Add("MOV.S {}.x,{};", phi_reg, ScalarS32{eval_value});
|
||||
break;
|
||||
case IR::Type::U64:
|
||||
case IR::Type::F64:
|
||||
ctx.Add("MOV.U64 {}.x,{};", phi_reg, ScalarRegister{eval_value});
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Phi node type {}", phi.Type());
|
||||
}
|
||||
}
|
||||
|
||||
void EmitJoin(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitDemoteToHelperInvocation(EmitContext& ctx) {
|
||||
ctx.Add("KIL TR.x;");
|
||||
}
|
||||
|
||||
void EmitBarrier(EmitContext& ctx) {
|
||||
ctx.Add("BAR;");
|
||||
}
|
||||
|
||||
void EmitWorkgroupMemoryBarrier(EmitContext& ctx) {
|
||||
ctx.Add("MEMBAR.CTA;");
|
||||
}
|
||||
|
||||
void EmitDeviceMemoryBarrier(EmitContext& ctx) {
|
||||
ctx.Add("MEMBAR;");
|
||||
}
|
||||
|
||||
void EmitPrologue(EmitContext& ctx) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void EmitEpilogue(EmitContext& ctx) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void EmitEmitVertex(EmitContext& ctx, ScalarS32 stream) {
|
||||
if (stream.type == Type::U32 && stream.imm_u32 == 0) {
|
||||
ctx.Add("EMIT;");
|
||||
} else {
|
||||
ctx.Add("EMITS {};", stream);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
|
||||
if (!stream.IsImmediate()) {
|
||||
LOG_WARNING(Shader_GLASM, "Stream is not immediate");
|
||||
}
|
||||
ctx.reg_alloc.Consume(stream);
|
||||
ctx.Add("ENDPRIM;");
|
||||
}
|
||||
|
||||
void EmitGetRegister(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetRegister(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetPred(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetPred(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetGotoVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetGotoVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetIndirectBranchVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetIndirectBranchVariable(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetZFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetSFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetCFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetOFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetZFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetSFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetCFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitSetOFlag(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {},invocation.groupid;", inst);
|
||||
}
|
||||
|
||||
void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {},invocation.localid;", inst);
|
||||
}
|
||||
|
||||
void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {}.x,primitive_invocation.x;", inst);
|
||||
}
|
||||
|
||||
void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {}.x,fragment.sampleid.x;", inst);
|
||||
}
|
||||
|
||||
void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {}.x,fragment.helperthread.x;", inst);
|
||||
}
|
||||
|
||||
void EmitYDirection(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.uses_y_direction = true;
|
||||
ctx.Add("MOV.F {}.x,y_direction[0].w;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU1(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {}.x,0;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU8(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {}.x,0;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU16(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {}.x,0;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU32(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {}.x,0;", inst);
|
||||
}
|
||||
|
||||
void EmitUndefU64(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.LongAdd("MOV.S64 {}.x,0;", inst);
|
||||
}
|
||||
|
||||
void EmitGetZeroFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetSignFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetCarryFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetOverflowFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetSparseFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitGetInBoundsFromOp(EmitContext& ctx) {
|
||||
NotImplemented();
|
||||
}
|
||||
|
||||
void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
ctx.Add("OR.S {},{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
ctx.Add("AND.S {},{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
|
||||
ctx.Add("XOR.S {},{},{};", inst, a, b);
|
||||
}
|
||||
|
||||
void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
|
||||
ctx.Add("SEQ.S {},{},0;", inst, value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
67
src/shader_recompiler/backend/glasm/emit_glasm_select.cpp
Normal file
67
src/shader_recompiler/backend/glasm/emit_glasm_select.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_context.h"
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
void EmitSelectU1(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, ScalarS32 true_value,
|
||||
ScalarS32 false_value) {
|
||||
ctx.Add("CMP.S {},{},{},{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectU8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] ScalarS32 cond,
|
||||
[[maybe_unused]] ScalarS32 true_value, [[maybe_unused]] ScalarS32 false_value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitSelectU16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] ScalarS32 cond,
|
||||
[[maybe_unused]] ScalarS32 true_value, [[maybe_unused]] ScalarS32 false_value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitSelectU32(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, ScalarS32 true_value,
|
||||
ScalarS32 false_value) {
|
||||
ctx.Add("CMP.S {},{},{},{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectU64(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, Register true_value,
|
||||
Register false_value) {
|
||||
ctx.reg_alloc.InvalidateConditionCodes();
|
||||
const Register ret{ctx.reg_alloc.LongDefine(inst)};
|
||||
if (ret == true_value) {
|
||||
ctx.Add("MOV.S.CC RC.x,{};"
|
||||
"MOV.U64 {}.x(EQ.x),{};",
|
||||
cond, ret, false_value);
|
||||
} else if (ret == false_value) {
|
||||
ctx.Add("MOV.S.CC RC.x,{};"
|
||||
"MOV.U64 {}.x(NE.x),{};",
|
||||
cond, ret, true_value);
|
||||
} else {
|
||||
ctx.Add("MOV.S.CC RC.x,{};"
|
||||
"MOV.U64 {}.x,{};"
|
||||
"MOV.U64 {}.x(NE.x),{};",
|
||||
cond, ret, false_value, ret, true_value);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSelectF16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] ScalarS32 cond,
|
||||
[[maybe_unused]] Register true_value, [[maybe_unused]] Register false_value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
void EmitSelectF32(EmitContext& ctx, IR::Inst& inst, ScalarS32 cond, ScalarS32 true_value,
|
||||
ScalarS32 false_value) {
|
||||
ctx.Add("CMP.S {},{},{},{};", inst, cond, true_value, false_value);
|
||||
}
|
||||
|
||||
void EmitSelectF64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] ScalarS32 cond,
|
||||
[[maybe_unused]] Register true_value, [[maybe_unused]] Register false_value) {
|
||||
throw NotImplementedException("GLASM instruction");
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
@@ -0,0 +1,58 @@
|
||||
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_context.h"
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
void EmitLoadSharedU8(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
|
||||
ctx.Add("LDS.U8 {},shared_mem[{}];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedS8(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
|
||||
ctx.Add("LDS.S8 {},shared_mem[{}];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU16(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
|
||||
ctx.Add("LDS.U16 {},shared_mem[{}];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedS16(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
|
||||
ctx.Add("LDS.S16 {},shared_mem[{}];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU32(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
|
||||
ctx.Add("LDS.U32 {},shared_mem[{}];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU64(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
|
||||
ctx.Add("LDS.U32X2 {},shared_mem[{}];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitLoadSharedU128(EmitContext& ctx, IR::Inst& inst, ScalarU32 offset) {
|
||||
ctx.Add("LDS.U32X4 {},shared_mem[{}];", inst, offset);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU8(EmitContext& ctx, ScalarU32 offset, ScalarU32 value) {
|
||||
ctx.Add("STS.U8 {},shared_mem[{}];", value, offset);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU16(EmitContext& ctx, ScalarU32 offset, ScalarU32 value) {
|
||||
ctx.Add("STS.U16 {},shared_mem[{}];", value, offset);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU32(EmitContext& ctx, ScalarU32 offset, ScalarU32 value) {
|
||||
ctx.Add("STS.U32 {},shared_mem[{}];", value, offset);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU64(EmitContext& ctx, ScalarU32 offset, Register value) {
|
||||
ctx.Add("STS.U32X2 {},shared_mem[{}];", value, offset);
|
||||
}
|
||||
|
||||
void EmitWriteSharedU128(EmitContext& ctx, ScalarU32 offset, Register value) {
|
||||
ctx.Add("STS.U32X4 {},shared_mem[{}];", value, offset);
|
||||
}
|
||||
} // namespace Shader::Backend::GLASM
|
||||
150
src/shader_recompiler/backend/glasm/emit_glasm_warp.cpp
Normal file
150
src/shader_recompiler/backend/glasm/emit_glasm_warp.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_context.h"
|
||||
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
void EmitLaneId(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.S {}.x,{}.threadid;", inst, ctx.stage_name);
|
||||
}
|
||||
|
||||
void EmitVoteAll(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred) {
|
||||
ctx.Add("TGALL.S {}.x,{};", inst, pred);
|
||||
}
|
||||
|
||||
void EmitVoteAny(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred) {
|
||||
ctx.Add("TGANY.S {}.x,{};", inst, pred);
|
||||
}
|
||||
|
||||
void EmitVoteEqual(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred) {
|
||||
ctx.Add("TGEQ.S {}.x,{};", inst, pred);
|
||||
}
|
||||
|
||||
void EmitSubgroupBallot(EmitContext& ctx, IR::Inst& inst, ScalarS32 pred) {
|
||||
ctx.Add("TGBALLOT {}.x,{};", inst, pred);
|
||||
}
|
||||
|
||||
void EmitSubgroupEqMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.U {},{}.threadeqmask;", inst, ctx.stage_name);
|
||||
}
|
||||
|
||||
void EmitSubgroupLtMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.U {},{}.threadltmask;", inst, ctx.stage_name);
|
||||
}
|
||||
|
||||
void EmitSubgroupLeMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.U {},{}.threadlemask;", inst, ctx.stage_name);
|
||||
}
|
||||
|
||||
void EmitSubgroupGtMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.U {},{}.threadgtmask;", inst, ctx.stage_name);
|
||||
}
|
||||
|
||||
void EmitSubgroupGeMask(EmitContext& ctx, IR::Inst& inst) {
|
||||
ctx.Add("MOV.U {},{}.threadgemask;", inst, ctx.stage_name);
|
||||
}
|
||||
|
||||
static void Shuffle(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
|
||||
const IR::Value& clamp, const IR::Value& segmentation_mask,
|
||||
std::string_view op) {
|
||||
IR::Inst* const in_bounds{inst.GetAssociatedPseudoOperation(IR::Opcode::GetInBoundsFromOp)};
|
||||
if (in_bounds) {
|
||||
in_bounds->Invalidate();
|
||||
}
|
||||
std::string mask;
|
||||
if (clamp.IsImmediate() && segmentation_mask.IsImmediate()) {
|
||||
mask = fmt::to_string(clamp.U32() | (segmentation_mask.U32() << 8));
|
||||
} else {
|
||||
mask = "RC";
|
||||
ctx.Add("BFI.U RC.x,{{5,8,0,0}},{},{};",
|
||||
ScalarU32{ctx.reg_alloc.Consume(segmentation_mask)},
|
||||
ScalarU32{ctx.reg_alloc.Consume(clamp)});
|
||||
}
|
||||
const Register value_ret{ctx.reg_alloc.Define(inst)};
|
||||
if (in_bounds) {
|
||||
const Register bounds_ret{ctx.reg_alloc.Define(*in_bounds)};
|
||||
ctx.Add("SHF{}.U {},{},{},{};"
|
||||
"MOV.U {}.x,{}.y;",
|
||||
op, bounds_ret, value, index, mask, value_ret, bounds_ret);
|
||||
} else {
|
||||
ctx.Add("SHF{}.U {},{},{},{};"
|
||||
"MOV.U {}.x,{}.y;",
|
||||
op, value_ret, value, index, mask, value_ret, value_ret);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitShuffleIndex(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
|
||||
const IR::Value& clamp, const IR::Value& segmentation_mask) {
|
||||
Shuffle(ctx, inst, value, index, clamp, segmentation_mask, "IDX");
|
||||
}
|
||||
|
||||
void EmitShuffleUp(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
|
||||
const IR::Value& clamp, const IR::Value& segmentation_mask) {
|
||||
Shuffle(ctx, inst, value, index, clamp, segmentation_mask, "UP");
|
||||
}
|
||||
|
||||
void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
|
||||
const IR::Value& clamp, const IR::Value& segmentation_mask) {
|
||||
Shuffle(ctx, inst, value, index, clamp, segmentation_mask, "DOWN");
|
||||
}
|
||||
|
||||
void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
|
||||
const IR::Value& clamp, const IR::Value& segmentation_mask) {
|
||||
Shuffle(ctx, inst, value, index, clamp, segmentation_mask, "XOR");
|
||||
}
|
||||
|
||||
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a, ScalarF32 op_b,
|
||||
ScalarU32 swizzle) {
|
||||
const auto ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("AND.U RC.z,{}.threadid,3;"
|
||||
"SHL.U RC.z,RC.z,1;"
|
||||
"SHR.U RC.z,{},RC.z;"
|
||||
"AND.U RC.z,RC.z,3;"
|
||||
"MUL.F RC.x,{},FSWZA[RC.z];"
|
||||
"MUL.F RC.y,{},FSWZB[RC.z];"
|
||||
"ADD.F {}.x,RC.x,RC.y;",
|
||||
ctx.stage_name, swizzle, op_a, op_b, ret);
|
||||
}
|
||||
|
||||
void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, ScalarF32 p) {
|
||||
if (ctx.profile.support_derivative_control) {
|
||||
ctx.Add("DDX.FINE {}.x,{};", inst, p);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLASM, "Fine derivatives not supported by device");
|
||||
ctx.Add("DDX {}.x,{};", inst, p);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitDPdyFine(EmitContext& ctx, IR::Inst& inst, ScalarF32 p) {
|
||||
if (ctx.profile.support_derivative_control) {
|
||||
ctx.Add("DDY.FINE {}.x,{};", inst, p);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLASM, "Fine derivatives not supported by device");
|
||||
ctx.Add("DDY {}.x,{};", inst, p);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitDPdxCoarse(EmitContext& ctx, IR::Inst& inst, ScalarF32 p) {
|
||||
if (ctx.profile.support_derivative_control) {
|
||||
ctx.Add("DDX.COARSE {}.x,{};", inst, p);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLASM, "Coarse derivatives not supported by device");
|
||||
ctx.Add("DDX {}.x,{};", inst, p);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitDPdyCoarse(EmitContext& ctx, IR::Inst& inst, ScalarF32 p) {
|
||||
if (ctx.profile.support_derivative_control) {
|
||||
ctx.Add("DDY.COARSE {}.x,{};", inst, p);
|
||||
} else {
|
||||
LOG_WARNING(Shader_GLASM, "Coarse derivatives not supported by device");
|
||||
ctx.Add("DDY {}.x,{};", inst, p);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
186
src/shader_recompiler/backend/glasm/reg_alloc.cpp
Normal file
186
src/shader_recompiler/backend/glasm/reg_alloc.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/backend/glasm/emit_context.h"
|
||||
#include "shader_recompiler/backend/glasm/reg_alloc.h"
|
||||
#include "shader_recompiler/exception.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
Register RegAlloc::Define(IR::Inst& inst) {
|
||||
return Define(inst, false);
|
||||
}
|
||||
|
||||
Register RegAlloc::LongDefine(IR::Inst& inst) {
|
||||
return Define(inst, true);
|
||||
}
|
||||
|
||||
Value RegAlloc::Peek(const IR::Value& value) {
|
||||
if (value.IsImmediate()) {
|
||||
return MakeImm(value);
|
||||
} else {
|
||||
return PeekInst(*value.Inst());
|
||||
}
|
||||
}
|
||||
|
||||
Value RegAlloc::Consume(const IR::Value& value) {
|
||||
if (value.IsImmediate()) {
|
||||
return MakeImm(value);
|
||||
} else {
|
||||
return ConsumeInst(*value.Inst());
|
||||
}
|
||||
}
|
||||
|
||||
void RegAlloc::Unref(IR::Inst& inst) {
|
||||
IR::Inst& value_inst{AliasInst(inst)};
|
||||
value_inst.DestructiveRemoveUsage();
|
||||
if (!value_inst.HasUses()) {
|
||||
Free(value_inst.Definition<Id>());
|
||||
}
|
||||
}
|
||||
|
||||
Register RegAlloc::AllocReg() {
|
||||
Register ret;
|
||||
ret.type = Type::Register;
|
||||
ret.id = Alloc(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Register RegAlloc::AllocLongReg() {
|
||||
Register ret;
|
||||
ret.type = Type::Register;
|
||||
ret.id = Alloc(true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void RegAlloc::FreeReg(Register reg) {
|
||||
Free(reg.id);
|
||||
}
|
||||
|
||||
Value RegAlloc::MakeImm(const IR::Value& value) {
|
||||
Value ret;
|
||||
switch (value.Type()) {
|
||||
case IR::Type::Void:
|
||||
ret.type = Type::Void;
|
||||
break;
|
||||
case IR::Type::U1:
|
||||
ret.type = Type::U32;
|
||||
ret.imm_u32 = value.U1() ? 0xffffffff : 0;
|
||||
break;
|
||||
case IR::Type::U32:
|
||||
ret.type = Type::U32;
|
||||
ret.imm_u32 = value.U32();
|
||||
break;
|
||||
case IR::Type::F32:
|
||||
ret.type = Type::U32;
|
||||
ret.imm_u32 = Common::BitCast<u32>(value.F32());
|
||||
break;
|
||||
case IR::Type::U64:
|
||||
ret.type = Type::U64;
|
||||
ret.imm_u64 = value.U64();
|
||||
break;
|
||||
case IR::Type::F64:
|
||||
ret.type = Type::U64;
|
||||
ret.imm_u64 = Common::BitCast<u64>(value.F64());
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Immediate type {}", value.Type());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Register RegAlloc::Define(IR::Inst& inst, bool is_long) {
|
||||
if (inst.HasUses()) {
|
||||
inst.SetDefinition<Id>(Alloc(is_long));
|
||||
} else {
|
||||
Id id{};
|
||||
id.is_long.Assign(is_long ? 1 : 0);
|
||||
id.is_null.Assign(1);
|
||||
inst.SetDefinition<Id>(id);
|
||||
}
|
||||
return Register{PeekInst(inst)};
|
||||
}
|
||||
|
||||
Value RegAlloc::PeekInst(IR::Inst& inst) {
|
||||
Value ret;
|
||||
ret.type = Type::Register;
|
||||
ret.id = inst.Definition<Id>();
|
||||
return ret;
|
||||
}
|
||||
|
||||
Value RegAlloc::ConsumeInst(IR::Inst& inst) {
|
||||
Unref(inst);
|
||||
return PeekInst(inst);
|
||||
}
|
||||
|
||||
Id RegAlloc::Alloc(bool is_long) {
|
||||
size_t& num_regs{is_long ? num_used_long_registers : num_used_registers};
|
||||
std::bitset<NUM_REGS>& use{is_long ? long_register_use : register_use};
|
||||
if (num_used_registers + num_used_long_registers < NUM_REGS) {
|
||||
for (size_t reg = 0; reg < NUM_REGS; ++reg) {
|
||||
if (use[reg]) {
|
||||
continue;
|
||||
}
|
||||
num_regs = std::max(num_regs, reg + 1);
|
||||
use[reg] = true;
|
||||
Id ret{};
|
||||
ret.is_valid.Assign(1);
|
||||
ret.is_long.Assign(is_long ? 1 : 0);
|
||||
ret.is_spill.Assign(0);
|
||||
ret.is_condition_code.Assign(0);
|
||||
ret.is_null.Assign(0);
|
||||
ret.index.Assign(static_cast<u32>(reg));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
throw NotImplementedException("Register spilling");
|
||||
}
|
||||
|
||||
void RegAlloc::Free(Id id) {
|
||||
if (id.is_valid == 0) {
|
||||
throw LogicError("Freeing invalid register");
|
||||
}
|
||||
if (id.is_spill != 0) {
|
||||
throw NotImplementedException("Free spill");
|
||||
}
|
||||
if (id.is_long != 0) {
|
||||
long_register_use[id.index] = false;
|
||||
} else {
|
||||
register_use[id.index] = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/ bool RegAlloc::IsAliased(const IR::Inst& inst) {
|
||||
switch (inst.GetOpcode()) {
|
||||
case IR::Opcode::Identity:
|
||||
case IR::Opcode::BitCastU16F16:
|
||||
case IR::Opcode::BitCastU32F32:
|
||||
case IR::Opcode::BitCastU64F64:
|
||||
case IR::Opcode::BitCastF16U16:
|
||||
case IR::Opcode::BitCastF32U32:
|
||||
case IR::Opcode::BitCastF64U64:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/ IR::Inst& RegAlloc::AliasInst(IR::Inst& inst) {
|
||||
IR::Inst* it{&inst};
|
||||
while (IsAliased(*it)) {
|
||||
const IR::Value arg{it->Arg(0)};
|
||||
if (arg.IsImmediate()) {
|
||||
break;
|
||||
}
|
||||
it = arg.InstRecursive();
|
||||
}
|
||||
return *it;
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
303
src/shader_recompiler/backend/glasm/reg_alloc.h
Normal file
303
src/shader_recompiler/backend/glasm/reg_alloc.h
Normal file
@@ -0,0 +1,303 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bitset>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/bit_cast.h"
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/exception.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
class Inst;
|
||||
class Value;
|
||||
} // namespace Shader::IR
|
||||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
class EmitContext;
|
||||
|
||||
enum class Type : u32 {
|
||||
Void,
|
||||
Register,
|
||||
U32,
|
||||
U64,
|
||||
};
|
||||
|
||||
struct Id {
|
||||
union {
|
||||
u32 raw;
|
||||
BitField<0, 1, u32> is_valid;
|
||||
BitField<1, 1, u32> is_long;
|
||||
BitField<2, 1, u32> is_spill;
|
||||
BitField<3, 1, u32> is_condition_code;
|
||||
BitField<4, 1, u32> is_null;
|
||||
BitField<5, 27, u32> index;
|
||||
};
|
||||
|
||||
bool operator==(Id rhs) const noexcept {
|
||||
return raw == rhs.raw;
|
||||
}
|
||||
bool operator!=(Id rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(Id) == sizeof(u32));
|
||||
|
||||
struct Value {
|
||||
Type type;
|
||||
union {
|
||||
Id id;
|
||||
u32 imm_u32;
|
||||
u64 imm_u64;
|
||||
};
|
||||
|
||||
bool operator==(const Value& rhs) const noexcept {
|
||||
if (type != rhs.type) {
|
||||
return false;
|
||||
}
|
||||
switch (type) {
|
||||
case Type::Void:
|
||||
return true;
|
||||
case Type::Register:
|
||||
return id == rhs.id;
|
||||
case Type::U32:
|
||||
return imm_u32 == rhs.imm_u32;
|
||||
case Type::U64:
|
||||
return imm_u64 == rhs.imm_u64;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool operator!=(const Value& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
};
|
||||
struct Register : Value {};
|
||||
struct ScalarRegister : Value {};
|
||||
struct ScalarU32 : Value {};
|
||||
struct ScalarS32 : Value {};
|
||||
struct ScalarF32 : Value {};
|
||||
struct ScalarF64 : Value {};
|
||||
|
||||
class RegAlloc {
|
||||
public:
|
||||
RegAlloc() = default;
|
||||
|
||||
Register Define(IR::Inst& inst);
|
||||
|
||||
Register LongDefine(IR::Inst& inst);
|
||||
|
||||
[[nodiscard]] Value Peek(const IR::Value& value);
|
||||
|
||||
Value Consume(const IR::Value& value);
|
||||
|
||||
void Unref(IR::Inst& inst);
|
||||
|
||||
[[nodiscard]] Register AllocReg();
|
||||
|
||||
[[nodiscard]] Register AllocLongReg();
|
||||
|
||||
void FreeReg(Register reg);
|
||||
|
||||
void InvalidateConditionCodes() {
|
||||
// This does nothing for now
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t NumUsedRegisters() const noexcept {
|
||||
return num_used_registers;
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t NumUsedLongRegisters() const noexcept {
|
||||
return num_used_long_registers;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsEmpty() const noexcept {
|
||||
return register_use.none() && long_register_use.none();
|
||||
}
|
||||
|
||||
/// Returns true if the instruction is expected to be aliased to another
|
||||
static bool IsAliased(const IR::Inst& inst);
|
||||
|
||||
/// Returns the underlying value out of an alias sequence
|
||||
static IR::Inst& AliasInst(IR::Inst& inst);
|
||||
|
||||
private:
|
||||
static constexpr size_t NUM_REGS = 4096;
|
||||
static constexpr size_t NUM_ELEMENTS = 4;
|
||||
|
||||
Value MakeImm(const IR::Value& value);
|
||||
|
||||
Register Define(IR::Inst& inst, bool is_long);
|
||||
|
||||
Value PeekInst(IR::Inst& inst);
|
||||
|
||||
Value ConsumeInst(IR::Inst& inst);
|
||||
|
||||
Id Alloc(bool is_long);
|
||||
|
||||
void Free(Id id);
|
||||
|
||||
size_t num_used_registers{};
|
||||
size_t num_used_long_registers{};
|
||||
std::bitset<NUM_REGS> register_use{};
|
||||
std::bitset<NUM_REGS> long_register_use{};
|
||||
};
|
||||
|
||||
template <bool scalar, typename FormatContext>
|
||||
auto FormatTo(FormatContext& ctx, Id id) {
|
||||
if (id.is_condition_code != 0) {
|
||||
throw NotImplementedException("Condition code emission");
|
||||
}
|
||||
if (id.is_spill != 0) {
|
||||
throw NotImplementedException("Spill emission");
|
||||
}
|
||||
if constexpr (scalar) {
|
||||
if (id.is_null != 0) {
|
||||
return fmt::format_to(ctx.out(), "{}", id.is_long != 0 ? "DC.x" : "RC.x");
|
||||
}
|
||||
if (id.is_long != 0) {
|
||||
return fmt::format_to(ctx.out(), "D{}.x", id.index.Value());
|
||||
} else {
|
||||
return fmt::format_to(ctx.out(), "R{}.x", id.index.Value());
|
||||
}
|
||||
} else {
|
||||
if (id.is_null != 0) {
|
||||
return fmt::format_to(ctx.out(), "{}", id.is_long != 0 ? "DC" : "RC");
|
||||
}
|
||||
if (id.is_long != 0) {
|
||||
return fmt::format_to(ctx.out(), "D{}", id.index.Value());
|
||||
} else {
|
||||
return fmt::format_to(ctx.out(), "R{}", id.index.Value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::Backend::GLASM::Id> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(Shader::Backend::GLASM::Id id, FormatContext& ctx) {
|
||||
return Shader::Backend::GLASM::FormatTo<true>(ctx, id);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::Backend::GLASM::Register> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::Backend::GLASM::Register& value, FormatContext& ctx) {
|
||||
if (value.type != Shader::Backend::GLASM::Type::Register) {
|
||||
throw Shader::InvalidArgument("Register value type is not register");
|
||||
}
|
||||
return Shader::Backend::GLASM::FormatTo<false>(ctx, value.id);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::Backend::GLASM::ScalarRegister> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::Backend::GLASM::ScalarRegister& value, FormatContext& ctx) {
|
||||
if (value.type != Shader::Backend::GLASM::Type::Register) {
|
||||
throw Shader::InvalidArgument("Register value type is not register");
|
||||
}
|
||||
return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::Backend::GLASM::ScalarU32> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::Backend::GLASM::ScalarU32& value, FormatContext& ctx) {
|
||||
switch (value.type) {
|
||||
case Shader::Backend::GLASM::Type::Void:
|
||||
break;
|
||||
case Shader::Backend::GLASM::Type::Register:
|
||||
return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id);
|
||||
case Shader::Backend::GLASM::Type::U32:
|
||||
return fmt::format_to(ctx.out(), "{}", value.imm_u32);
|
||||
case Shader::Backend::GLASM::Type::U64:
|
||||
break;
|
||||
}
|
||||
throw Shader::InvalidArgument("Invalid value type {}", value.type);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::Backend::GLASM::ScalarS32> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::Backend::GLASM::ScalarS32& value, FormatContext& ctx) {
|
||||
switch (value.type) {
|
||||
case Shader::Backend::GLASM::Type::Void:
|
||||
break;
|
||||
case Shader::Backend::GLASM::Type::Register:
|
||||
return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id);
|
||||
case Shader::Backend::GLASM::Type::U32:
|
||||
return fmt::format_to(ctx.out(), "{}", static_cast<s32>(value.imm_u32));
|
||||
case Shader::Backend::GLASM::Type::U64:
|
||||
break;
|
||||
}
|
||||
throw Shader::InvalidArgument("Invalid value type {}", value.type);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::Backend::GLASM::ScalarF32> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::Backend::GLASM::ScalarF32& value, FormatContext& ctx) {
|
||||
switch (value.type) {
|
||||
case Shader::Backend::GLASM::Type::Void:
|
||||
break;
|
||||
case Shader::Backend::GLASM::Type::Register:
|
||||
return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id);
|
||||
case Shader::Backend::GLASM::Type::U32:
|
||||
return fmt::format_to(ctx.out(), "{}", Common::BitCast<f32>(value.imm_u32));
|
||||
case Shader::Backend::GLASM::Type::U64:
|
||||
break;
|
||||
}
|
||||
throw Shader::InvalidArgument("Invalid value type {}", value.type);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<Shader::Backend::GLASM::ScalarF64> {
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::Backend::GLASM::ScalarF64& value, FormatContext& ctx) {
|
||||
switch (value.type) {
|
||||
case Shader::Backend::GLASM::Type::Void:
|
||||
break;
|
||||
case Shader::Backend::GLASM::Type::Register:
|
||||
return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id);
|
||||
case Shader::Backend::GLASM::Type::U32:
|
||||
break;
|
||||
case Shader::Backend::GLASM::Type::U64:
|
||||
return fmt::format_to(ctx.out(), "{}", Common::BitCast<f64>(value.imm_u64));
|
||||
}
|
||||
throw Shader::InvalidArgument("Invalid value type {}", value.type);
|
||||
}
|
||||
};
|
||||
715
src/shader_recompiler/backend/glsl/emit_context.cpp
Normal file
715
src/shader_recompiler/backend/glsl/emit_context.cpp
Normal file
@@ -0,0 +1,715 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/backend/bindings.h"
|
||||
#include "shader_recompiler/backend/glsl/emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
u32 CbufIndex(size_t offset) {
|
||||
return (offset / 4) % 4;
|
||||
}
|
||||
|
||||
char Swizzle(size_t offset) {
|
||||
return "xyzw"[CbufIndex(offset)];
|
||||
}
|
||||
|
||||
std::string_view InterpDecorator(Interpolation interp) {
|
||||
switch (interp) {
|
||||
case Interpolation::Smooth:
|
||||
return "";
|
||||
case Interpolation::Flat:
|
||||
return "flat ";
|
||||
case Interpolation::NoPerspective:
|
||||
return "noperspective ";
|
||||
}
|
||||
throw InvalidArgument("Invalid interpolation {}", interp);
|
||||
}
|
||||
|
||||
std::string_view InputArrayDecorator(Stage stage) {
|
||||
switch (stage) {
|
||||
case Stage::Geometry:
|
||||
case Stage::TessellationControl:
|
||||
case Stage::TessellationEval:
|
||||
return "[]";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
bool StoresPerVertexAttributes(Stage stage) {
|
||||
switch (stage) {
|
||||
case Stage::VertexA:
|
||||
case Stage::VertexB:
|
||||
case Stage::Geometry:
|
||||
case Stage::TessellationEval:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string OutputDecorator(Stage stage, u32 size) {
|
||||
switch (stage) {
|
||||
case Stage::TessellationControl:
|
||||
return fmt::format("[{}]", size);
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
std::string_view SamplerType(TextureType type, bool is_depth) {
|
||||
if (is_depth) {
|
||||
switch (type) {
|
||||
case TextureType::Color1D:
|
||||
return "sampler1DShadow";
|
||||
case TextureType::ColorArray1D:
|
||||
return "sampler1DArrayShadow";
|
||||
case TextureType::Color2D:
|
||||
return "sampler2DShadow";
|
||||
case TextureType::ColorArray2D:
|
||||
return "sampler2DArrayShadow";
|
||||
case TextureType::ColorCube:
|
||||
return "samplerCubeShadow";
|
||||
case TextureType::ColorArrayCube:
|
||||
return "samplerCubeArrayShadow";
|
||||
default:
|
||||
throw NotImplementedException("Texture type: {}", type);
|
||||
}
|
||||
}
|
||||
switch (type) {
|
||||
case TextureType::Color1D:
|
||||
return "sampler1D";
|
||||
case TextureType::ColorArray1D:
|
||||
return "sampler1DArray";
|
||||
case TextureType::Color2D:
|
||||
return "sampler2D";
|
||||
case TextureType::ColorArray2D:
|
||||
return "sampler2DArray";
|
||||
case TextureType::Color3D:
|
||||
return "sampler3D";
|
||||
case TextureType::ColorCube:
|
||||
return "samplerCube";
|
||||
case TextureType::ColorArrayCube:
|
||||
return "samplerCubeArray";
|
||||
case TextureType::Buffer:
|
||||
return "samplerBuffer";
|
||||
default:
|
||||
throw NotImplementedException("Texture type: {}", type);
|
||||
}
|
||||
}
|
||||
|
||||
std::string_view ImageType(TextureType type) {
|
||||
switch (type) {
|
||||
case TextureType::Color1D:
|
||||
return "uimage1D";
|
||||
case TextureType::ColorArray1D:
|
||||
return "uimage1DArray";
|
||||
case TextureType::Color2D:
|
||||
return "uimage2D";
|
||||
case TextureType::ColorArray2D:
|
||||
return "uimage2DArray";
|
||||
case TextureType::Color3D:
|
||||
return "uimage3D";
|
||||
case TextureType::ColorCube:
|
||||
return "uimageCube";
|
||||
case TextureType::ColorArrayCube:
|
||||
return "uimageCubeArray";
|
||||
case TextureType::Buffer:
|
||||
return "uimageBuffer";
|
||||
default:
|
||||
throw NotImplementedException("Image type: {}", type);
|
||||
}
|
||||
}
|
||||
|
||||
std::string_view ImageFormatString(ImageFormat format) {
|
||||
switch (format) {
|
||||
case ImageFormat::Typeless:
|
||||
return "";
|
||||
case ImageFormat::R8_UINT:
|
||||
return ",r8ui";
|
||||
case ImageFormat::R8_SINT:
|
||||
return ",r8i";
|
||||
case ImageFormat::R16_UINT:
|
||||
return ",r16ui";
|
||||
case ImageFormat::R16_SINT:
|
||||
return ",r16i";
|
||||
case ImageFormat::R32_UINT:
|
||||
return ",r32ui";
|
||||
case ImageFormat::R32G32_UINT:
|
||||
return ",rg32ui";
|
||||
case ImageFormat::R32G32B32A32_UINT:
|
||||
return ",rgba32ui";
|
||||
default:
|
||||
throw NotImplementedException("Image format: {}", format);
|
||||
}
|
||||
}
|
||||
|
||||
std::string_view ImageAccessQualifier(bool is_written, bool is_read) {
|
||||
if (is_written && !is_read) {
|
||||
return "writeonly ";
|
||||
}
|
||||
if (is_read && !is_written) {
|
||||
return "readonly ";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string_view GetTessMode(TessPrimitive primitive) {
|
||||
switch (primitive) {
|
||||
case TessPrimitive::Triangles:
|
||||
return "triangles";
|
||||
case TessPrimitive::Quads:
|
||||
return "quads";
|
||||
case TessPrimitive::Isolines:
|
||||
return "isolines";
|
||||
}
|
||||
throw InvalidArgument("Invalid tessellation primitive {}", primitive);
|
||||
}
|
||||
|
||||
std::string_view GetTessSpacing(TessSpacing spacing) {
|
||||
switch (spacing) {
|
||||
case TessSpacing::Equal:
|
||||
return "equal_spacing";
|
||||
case TessSpacing::FractionalOdd:
|
||||
return "fractional_odd_spacing";
|
||||
case TessSpacing::FractionalEven:
|
||||
return "fractional_even_spacing";
|
||||
}
|
||||
throw InvalidArgument("Invalid tessellation spacing {}", spacing);
|
||||
}
|
||||
|
||||
std::string_view InputPrimitive(InputTopology topology) {
|
||||
switch (topology) {
|
||||
case InputTopology::Points:
|
||||
return "points";
|
||||
case InputTopology::Lines:
|
||||
return "lines";
|
||||
case InputTopology::LinesAdjacency:
|
||||
return "lines_adjacency";
|
||||
case InputTopology::Triangles:
|
||||
return "triangles";
|
||||
case InputTopology::TrianglesAdjacency:
|
||||
return "triangles_adjacency";
|
||||
}
|
||||
throw InvalidArgument("Invalid input topology {}", topology);
|
||||
}
|
||||
|
||||
std::string_view OutputPrimitive(OutputTopology topology) {
|
||||
switch (topology) {
|
||||
case OutputTopology::PointList:
|
||||
return "points";
|
||||
case OutputTopology::LineStrip:
|
||||
return "line_strip";
|
||||
case OutputTopology::TriangleStrip:
|
||||
return "triangle_strip";
|
||||
}
|
||||
throw InvalidArgument("Invalid output topology {}", topology);
|
||||
}
|
||||
|
||||
void SetupLegacyOutPerVertex(EmitContext& ctx, std::string& header) {
|
||||
if (!ctx.info.stores.Legacy()) {
|
||||
return;
|
||||
}
|
||||
if (ctx.info.stores.FixedFunctionTexture()) {
|
||||
header += "vec4 gl_TexCoord[8];";
|
||||
}
|
||||
if (ctx.info.stores.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
|
||||
header += "vec4 gl_FrontColor;";
|
||||
}
|
||||
if (ctx.info.stores.AnyComponent(IR::Attribute::ColorFrontSpecularR)) {
|
||||
header += "vec4 gl_FrontSecondaryColor;";
|
||||
}
|
||||
if (ctx.info.stores.AnyComponent(IR::Attribute::ColorBackDiffuseR)) {
|
||||
header += "vec4 gl_BackColor;";
|
||||
}
|
||||
if (ctx.info.stores.AnyComponent(IR::Attribute::ColorBackSpecularR)) {
|
||||
header += "vec4 gl_BackSecondaryColor;";
|
||||
}
|
||||
}
|
||||
|
||||
void SetupOutPerVertex(EmitContext& ctx, std::string& header) {
|
||||
if (!StoresPerVertexAttributes(ctx.stage)) {
|
||||
return;
|
||||
}
|
||||
if (ctx.uses_geometry_passthrough) {
|
||||
return;
|
||||
}
|
||||
header += "out gl_PerVertex{vec4 gl_Position;";
|
||||
if (ctx.info.stores[IR::Attribute::PointSize]) {
|
||||
header += "float gl_PointSize;";
|
||||
}
|
||||
if (ctx.info.stores.ClipDistances()) {
|
||||
header += "float gl_ClipDistance[];";
|
||||
}
|
||||
if (ctx.info.stores[IR::Attribute::ViewportIndex] &&
|
||||
ctx.profile.support_viewport_index_layer_non_geometry && ctx.stage != Stage::Geometry) {
|
||||
header += "int gl_ViewportIndex;";
|
||||
}
|
||||
SetupLegacyOutPerVertex(ctx, header);
|
||||
header += "};";
|
||||
if (ctx.info.stores[IR::Attribute::ViewportIndex] && ctx.stage == Stage::Geometry) {
|
||||
header += "out int gl_ViewportIndex;";
|
||||
}
|
||||
}
|
||||
|
||||
void SetupInPerVertex(EmitContext& ctx, std::string& header) {
|
||||
// Currently only required for TessellationControl to adhere to
|
||||
// ARB_separate_shader_objects requirements
|
||||
if (ctx.stage != Stage::TessellationControl) {
|
||||
return;
|
||||
}
|
||||
const bool loads_position{ctx.info.loads.AnyComponent(IR::Attribute::PositionX)};
|
||||
const bool loads_point_size{ctx.info.loads[IR::Attribute::PointSize]};
|
||||
const bool loads_clip_distance{ctx.info.loads.ClipDistances()};
|
||||
const bool loads_per_vertex{loads_position || loads_point_size || loads_clip_distance};
|
||||
if (!loads_per_vertex) {
|
||||
return;
|
||||
}
|
||||
header += "in gl_PerVertex{";
|
||||
if (loads_position) {
|
||||
header += "vec4 gl_Position;";
|
||||
}
|
||||
if (loads_point_size) {
|
||||
header += "float gl_PointSize;";
|
||||
}
|
||||
if (loads_clip_distance) {
|
||||
header += "float gl_ClipDistance[];";
|
||||
}
|
||||
header += "}gl_in[gl_MaxPatchVertices];";
|
||||
}
|
||||
|
||||
void SetupLegacyInPerFragment(EmitContext& ctx, std::string& header) {
|
||||
if (!ctx.info.loads.Legacy()) {
|
||||
return;
|
||||
}
|
||||
header += "in gl_PerFragment{";
|
||||
if (ctx.info.loads.FixedFunctionTexture()) {
|
||||
header += "vec4 gl_TexCoord[8];";
|
||||
}
|
||||
if (ctx.info.loads.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
|
||||
header += "vec4 gl_Color;";
|
||||
}
|
||||
header += "};";
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
|
||||
const RuntimeInfo& runtime_info_)
|
||||
: info{program.info}, profile{profile_}, runtime_info{runtime_info_}, stage{program.stage},
|
||||
uses_geometry_passthrough{program.is_geometry_passthrough &&
|
||||
profile.support_geometry_shader_passthrough} {
|
||||
if (profile.need_fastmath_off) {
|
||||
header += "#pragma optionNV(fastmath off)\n";
|
||||
}
|
||||
SetupExtensions();
|
||||
switch (program.stage) {
|
||||
case Stage::VertexA:
|
||||
case Stage::VertexB:
|
||||
stage_name = "vs";
|
||||
break;
|
||||
case Stage::TessellationControl:
|
||||
stage_name = "tcs";
|
||||
header += fmt::format("layout(vertices={})out;", program.invocations);
|
||||
break;
|
||||
case Stage::TessellationEval:
|
||||
stage_name = "tes";
|
||||
header += fmt::format("layout({},{},{})in;", GetTessMode(runtime_info.tess_primitive),
|
||||
GetTessSpacing(runtime_info.tess_spacing),
|
||||
runtime_info.tess_clockwise ? "cw" : "ccw");
|
||||
break;
|
||||
case Stage::Geometry:
|
||||
stage_name = "gs";
|
||||
header += fmt::format("layout({})in;", InputPrimitive(runtime_info.input_topology));
|
||||
if (uses_geometry_passthrough) {
|
||||
header += "layout(passthrough)in gl_PerVertex{vec4 gl_Position;};";
|
||||
break;
|
||||
} else if (program.is_geometry_passthrough &&
|
||||
!profile.support_geometry_shader_passthrough) {
|
||||
LOG_WARNING(Shader_GLSL, "Passthrough geometry program used but not supported");
|
||||
}
|
||||
header += fmt::format(
|
||||
"layout({},max_vertices={})out;in gl_PerVertex{{vec4 gl_Position;}}gl_in[];",
|
||||
OutputPrimitive(program.output_topology), program.output_vertices);
|
||||
break;
|
||||
case Stage::Fragment:
|
||||
stage_name = "fs";
|
||||
position_name = "gl_FragCoord";
|
||||
if (runtime_info.force_early_z) {
|
||||
header += "layout(early_fragment_tests)in;";
|
||||
}
|
||||
if (info.uses_sample_id) {
|
||||
header += "in int gl_SampleID;";
|
||||
}
|
||||
if (info.stores_sample_mask) {
|
||||
header += "out int gl_SampleMask[];";
|
||||
}
|
||||
break;
|
||||
case Stage::Compute:
|
||||
stage_name = "cs";
|
||||
const u32 local_x{std::max(program.workgroup_size[0], 1u)};
|
||||
const u32 local_y{std::max(program.workgroup_size[1], 1u)};
|
||||
const u32 local_z{std::max(program.workgroup_size[2], 1u)};
|
||||
header += fmt::format("layout(local_size_x={},local_size_y={},local_size_z={}) in;",
|
||||
local_x, local_y, local_z);
|
||||
break;
|
||||
}
|
||||
SetupOutPerVertex(*this, header);
|
||||
SetupInPerVertex(*this, header);
|
||||
SetupLegacyInPerFragment(*this, header);
|
||||
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (!info.loads.Generic(index) || !runtime_info.previous_stage_stores.Generic(index)) {
|
||||
continue;
|
||||
}
|
||||
const auto qualifier{uses_geometry_passthrough ? "passthrough"
|
||||
: fmt::format("location={}", index)};
|
||||
header += fmt::format("layout({}){}in vec4 in_attr{}{};", qualifier,
|
||||
InterpDecorator(info.interpolation[index]), index,
|
||||
InputArrayDecorator(stage));
|
||||
}
|
||||
for (size_t index = 0; index < info.uses_patches.size(); ++index) {
|
||||
if (!info.uses_patches[index]) {
|
||||
continue;
|
||||
}
|
||||
const auto qualifier{stage == Stage::TessellationControl ? "out" : "in"};
|
||||
header += fmt::format("layout(location={})patch {} vec4 patch{};", index, qualifier, index);
|
||||
}
|
||||
if (stage == Stage::Fragment) {
|
||||
for (size_t index = 0; index < info.stores_frag_color.size(); ++index) {
|
||||
if (!info.stores_frag_color[index] && !profile.need_declared_frag_colors) {
|
||||
continue;
|
||||
}
|
||||
header += fmt::format("layout(location={})out vec4 frag_color{};", index, index);
|
||||
}
|
||||
}
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (info.stores.Generic(index)) {
|
||||
DefineGenericOutput(index, program.invocations);
|
||||
}
|
||||
}
|
||||
DefineConstantBuffers(bindings);
|
||||
DefineStorageBuffers(bindings);
|
||||
SetupImages(bindings);
|
||||
SetupTextures(bindings);
|
||||
DefineHelperFunctions();
|
||||
DefineConstants();
|
||||
}
|
||||
|
||||
void EmitContext::SetupExtensions() {
|
||||
header += "#extension GL_ARB_separate_shader_objects : enable\n";
|
||||
if (info.uses_shadow_lod && profile.support_gl_texture_shadow_lod) {
|
||||
header += "#extension GL_EXT_texture_shadow_lod : enable\n";
|
||||
}
|
||||
if (info.uses_int64 && profile.support_int64) {
|
||||
header += "#extension GL_ARB_gpu_shader_int64 : enable\n";
|
||||
}
|
||||
if (info.uses_int64_bit_atomics) {
|
||||
header += "#extension GL_NV_shader_atomic_int64 : enable\n";
|
||||
}
|
||||
if (info.uses_atomic_f32_add) {
|
||||
header += "#extension GL_NV_shader_atomic_float : enable\n";
|
||||
}
|
||||
if (info.uses_atomic_f16x2_add || info.uses_atomic_f16x2_min || info.uses_atomic_f16x2_max) {
|
||||
header += "#extension GL_NV_shader_atomic_fp16_vector : enable\n";
|
||||
}
|
||||
if (info.uses_fp16) {
|
||||
if (profile.support_gl_nv_gpu_shader_5) {
|
||||
header += "#extension GL_NV_gpu_shader5 : enable\n";
|
||||
}
|
||||
if (profile.support_gl_amd_gpu_shader_half_float) {
|
||||
header += "#extension GL_AMD_gpu_shader_half_float : enable\n";
|
||||
}
|
||||
}
|
||||
if (info.uses_subgroup_invocation_id || info.uses_subgroup_mask || info.uses_subgroup_vote ||
|
||||
info.uses_subgroup_shuffles || info.uses_fswzadd) {
|
||||
header += "#extension GL_ARB_shader_ballot : enable\n"
|
||||
"#extension GL_ARB_shader_group_vote : enable\n";
|
||||
if (!info.uses_int64 && profile.support_int64) {
|
||||
header += "#extension GL_ARB_gpu_shader_int64 : enable\n";
|
||||
}
|
||||
if (profile.support_gl_warp_intrinsics) {
|
||||
header += "#extension GL_NV_shader_thread_shuffle : enable\n";
|
||||
}
|
||||
}
|
||||
if ((info.stores[IR::Attribute::ViewportIndex] || info.stores[IR::Attribute::Layer]) &&
|
||||
profile.support_viewport_index_layer_non_geometry && stage != Stage::Geometry) {
|
||||
header += "#extension GL_ARB_shader_viewport_layer_array : enable\n";
|
||||
}
|
||||
if (info.uses_sparse_residency && profile.support_gl_sparse_textures) {
|
||||
header += "#extension GL_ARB_sparse_texture2 : enable\n";
|
||||
}
|
||||
if (info.stores[IR::Attribute::ViewportMask] && profile.support_viewport_mask) {
|
||||
header += "#extension GL_NV_viewport_array2 : enable\n";
|
||||
}
|
||||
if (info.uses_typeless_image_reads) {
|
||||
header += "#extension GL_EXT_shader_image_load_formatted : enable\n";
|
||||
}
|
||||
if (info.uses_derivatives && profile.support_gl_derivative_control) {
|
||||
header += "#extension GL_ARB_derivative_control : enable\n";
|
||||
}
|
||||
if (uses_geometry_passthrough) {
|
||||
header += "#extension GL_NV_geometry_shader_passthrough : enable\n";
|
||||
}
|
||||
}
|
||||
|
||||
void EmitContext::DefineConstantBuffers(Bindings& bindings) {
|
||||
if (info.constant_buffer_descriptors.empty()) {
|
||||
return;
|
||||
}
|
||||
for (const auto& desc : info.constant_buffer_descriptors) {
|
||||
header += fmt::format(
|
||||
"layout(std140,binding={}) uniform {}_cbuf_{}{{vec4 {}_cbuf{}[{}];}};",
|
||||
bindings.uniform_buffer, stage_name, desc.index, stage_name, desc.index, 4 * 1024);
|
||||
bindings.uniform_buffer += desc.count;
|
||||
}
|
||||
}
|
||||
|
||||
void EmitContext::DefineStorageBuffers(Bindings& bindings) {
|
||||
if (info.storage_buffers_descriptors.empty()) {
|
||||
return;
|
||||
}
|
||||
u32 index{};
|
||||
for (const auto& desc : info.storage_buffers_descriptors) {
|
||||
header += fmt::format("layout(std430,binding={}) buffer {}_ssbo_{}{{uint {}_ssbo{}[];}};",
|
||||
bindings.storage_buffer, stage_name, bindings.storage_buffer,
|
||||
stage_name, index);
|
||||
bindings.storage_buffer += desc.count;
|
||||
index += desc.count;
|
||||
}
|
||||
}
|
||||
|
||||
void EmitContext::DefineGenericOutput(size_t index, u32 invocations) {
|
||||
static constexpr std::string_view swizzle{"xyzw"};
|
||||
const size_t base_index{static_cast<size_t>(IR::Attribute::Generic0X) + index * 4};
|
||||
u32 element{0};
|
||||
while (element < 4) {
|
||||
std::string definition{fmt::format("layout(location={}", index)};
|
||||
const u32 remainder{4 - element};
|
||||
const TransformFeedbackVarying* xfb_varying{};
|
||||
if (!runtime_info.xfb_varyings.empty()) {
|
||||
xfb_varying = &runtime_info.xfb_varyings[base_index + element];
|
||||
xfb_varying = xfb_varying && xfb_varying->components > 0 ? xfb_varying : nullptr;
|
||||
}
|
||||
const u32 num_components{xfb_varying ? xfb_varying->components : remainder};
|
||||
if (element > 0) {
|
||||
definition += fmt::format(",component={}", element);
|
||||
}
|
||||
if (xfb_varying) {
|
||||
definition +=
|
||||
fmt::format(",xfb_buffer={},xfb_stride={},xfb_offset={}", xfb_varying->buffer,
|
||||
xfb_varying->stride, xfb_varying->offset);
|
||||
}
|
||||
std::string name{fmt::format("out_attr{}", index)};
|
||||
if (num_components < 4 || element > 0) {
|
||||
name += fmt::format("_{}", swizzle.substr(element, num_components));
|
||||
}
|
||||
const auto type{num_components == 1 ? "float" : fmt::format("vec{}", num_components)};
|
||||
definition += fmt::format(")out {} {}{};", type, name, OutputDecorator(stage, invocations));
|
||||
header += definition;
|
||||
|
||||
const GenericElementInfo element_info{
|
||||
.name = name,
|
||||
.first_element = element,
|
||||
.num_components = num_components,
|
||||
};
|
||||
std::fill_n(output_generics[index].begin() + element, num_components, element_info);
|
||||
element += num_components;
|
||||
}
|
||||
}
|
||||
|
||||
void EmitContext::DefineHelperFunctions() {
|
||||
header += "\n#define ftoi floatBitsToInt\n#define ftou floatBitsToUint\n"
|
||||
"#define itof intBitsToFloat\n#define utof uintBitsToFloat\n";
|
||||
if (info.uses_global_increment || info.uses_shared_increment) {
|
||||
header += "uint CasIncrement(uint op_a,uint op_b){return op_a>=op_b?0u:(op_a+1u);}";
|
||||
}
|
||||
if (info.uses_global_decrement || info.uses_shared_decrement) {
|
||||
header += "uint CasDecrement(uint op_a,uint op_b){"
|
||||
"return op_a==0||op_a>op_b?op_b:(op_a-1u);}";
|
||||
}
|
||||
if (info.uses_atomic_f32_add) {
|
||||
header += "uint CasFloatAdd(uint op_a,float op_b){"
|
||||
"return ftou(utof(op_a)+op_b);}";
|
||||
}
|
||||
if (info.uses_atomic_f32x2_add) {
|
||||
header += "uint CasFloatAdd32x2(uint op_a,vec2 op_b){"
|
||||
"return packHalf2x16(unpackHalf2x16(op_a)+op_b);}";
|
||||
}
|
||||
if (info.uses_atomic_f32x2_min) {
|
||||
header += "uint CasFloatMin32x2(uint op_a,vec2 op_b){return "
|
||||
"packHalf2x16(min(unpackHalf2x16(op_a),op_b));}";
|
||||
}
|
||||
if (info.uses_atomic_f32x2_max) {
|
||||
header += "uint CasFloatMax32x2(uint op_a,vec2 op_b){return "
|
||||
"packHalf2x16(max(unpackHalf2x16(op_a),op_b));}";
|
||||
}
|
||||
if (info.uses_atomic_f16x2_add) {
|
||||
header += "uint CasFloatAdd16x2(uint op_a,f16vec2 op_b){return "
|
||||
"packFloat2x16(unpackFloat2x16(op_a)+op_b);}";
|
||||
}
|
||||
if (info.uses_atomic_f16x2_min) {
|
||||
header += "uint CasFloatMin16x2(uint op_a,f16vec2 op_b){return "
|
||||
"packFloat2x16(min(unpackFloat2x16(op_a),op_b));}";
|
||||
}
|
||||
if (info.uses_atomic_f16x2_max) {
|
||||
header += "uint CasFloatMax16x2(uint op_a,f16vec2 op_b){return "
|
||||
"packFloat2x16(max(unpackFloat2x16(op_a),op_b));}";
|
||||
}
|
||||
if (info.uses_atomic_s32_min) {
|
||||
header += "uint CasMinS32(uint op_a,uint op_b){return uint(min(int(op_a),int(op_b)));}";
|
||||
}
|
||||
if (info.uses_atomic_s32_max) {
|
||||
header += "uint CasMaxS32(uint op_a,uint op_b){return uint(max(int(op_a),int(op_b)));}";
|
||||
}
|
||||
if (info.uses_global_memory && profile.support_int64) {
|
||||
header += DefineGlobalMemoryFunctions();
|
||||
}
|
||||
if (info.loads_indexed_attributes) {
|
||||
const bool is_array{stage == Stage::Geometry};
|
||||
const auto vertex_arg{is_array ? ",uint vertex" : ""};
|
||||
std::string func{
|
||||
fmt::format("float IndexedAttrLoad(int offset{}){{int base_index=offset>>2;uint "
|
||||
"masked_index=uint(base_index)&3u;switch(base_index>>2){{",
|
||||
vertex_arg)};
|
||||
if (info.loads.AnyComponent(IR::Attribute::PositionX)) {
|
||||
const auto position_idx{is_array ? "gl_in[vertex]." : ""};
|
||||
func += fmt::format("case {}:return {}{}[masked_index];",
|
||||
static_cast<u32>(IR::Attribute::PositionX) >> 2, position_idx,
|
||||
position_name);
|
||||
}
|
||||
const u32 base_attribute_value = static_cast<u32>(IR::Attribute::Generic0X) >> 2;
|
||||
for (u32 index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (!info.loads.Generic(index)) {
|
||||
continue;
|
||||
}
|
||||
const auto vertex_idx{is_array ? "[vertex]" : ""};
|
||||
func += fmt::format("case {}:return in_attr{}{}[masked_index];",
|
||||
base_attribute_value + index, index, vertex_idx);
|
||||
}
|
||||
func += "default: return 0.0;}}";
|
||||
header += func;
|
||||
}
|
||||
if (info.stores_indexed_attributes) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
std::string EmitContext::DefineGlobalMemoryFunctions() {
|
||||
const auto define_body{[&](std::string& func, size_t index, std::string_view return_statement) {
|
||||
const auto& ssbo{info.storage_buffers_descriptors[index]};
|
||||
const u32 size_cbuf_offset{ssbo.cbuf_offset + 8};
|
||||
const auto ssbo_addr{fmt::format("ssbo_addr{}", index)};
|
||||
const auto cbuf{fmt::format("{}_cbuf{}", stage_name, ssbo.cbuf_index)};
|
||||
std::array<std::string, 2> addr_xy;
|
||||
std::array<std::string, 2> size_xy;
|
||||
for (size_t i = 0; i < addr_xy.size(); ++i) {
|
||||
const auto addr_loc{ssbo.cbuf_offset + 4 * i};
|
||||
const auto size_loc{size_cbuf_offset + 4 * i};
|
||||
addr_xy[i] = fmt::format("ftou({}[{}].{})", cbuf, addr_loc / 16, Swizzle(addr_loc));
|
||||
size_xy[i] = fmt::format("ftou({}[{}].{})", cbuf, size_loc / 16, Swizzle(size_loc));
|
||||
}
|
||||
const auto addr_pack{fmt::format("packUint2x32(uvec2({},{}))", addr_xy[0], addr_xy[1])};
|
||||
const auto addr_statment{fmt::format("uint64_t {}={};", ssbo_addr, addr_pack)};
|
||||
func += addr_statment;
|
||||
|
||||
const auto size_vec{fmt::format("uvec2({},{})", size_xy[0], size_xy[1])};
|
||||
const auto comp_lhs{fmt::format("(addr>={})", ssbo_addr)};
|
||||
const auto comp_rhs{fmt::format("(addr<({}+uint64_t({})))", ssbo_addr, size_vec)};
|
||||
const auto comparison{fmt::format("if({}&&{}){{", comp_lhs, comp_rhs)};
|
||||
func += comparison;
|
||||
|
||||
const auto ssbo_name{fmt::format("{}_ssbo{}", stage_name, index)};
|
||||
func += fmt::format(fmt::runtime(return_statement), ssbo_name, ssbo_addr);
|
||||
}};
|
||||
std::string write_func{"void WriteGlobal32(uint64_t addr,uint data){"};
|
||||
std::string write_func_64{"void WriteGlobal64(uint64_t addr,uvec2 data){"};
|
||||
std::string write_func_128{"void WriteGlobal128(uint64_t addr,uvec4 data){"};
|
||||
std::string load_func{"uint LoadGlobal32(uint64_t addr){"};
|
||||
std::string load_func_64{"uvec2 LoadGlobal64(uint64_t addr){"};
|
||||
std::string load_func_128{"uvec4 LoadGlobal128(uint64_t addr){"};
|
||||
const size_t num_buffers{info.storage_buffers_descriptors.size()};
|
||||
for (size_t index = 0; index < num_buffers; ++index) {
|
||||
if (!info.nvn_buffer_used[index]) {
|
||||
continue;
|
||||
}
|
||||
define_body(write_func, index, "{0}[uint(addr-{1})>>2]=data;return;}}");
|
||||
define_body(write_func_64, index,
|
||||
"{0}[uint(addr-{1})>>2]=data.x;{0}[uint(addr-{1}+4)>>2]=data.y;return;}}");
|
||||
define_body(write_func_128, index,
|
||||
"{0}[uint(addr-{1})>>2]=data.x;{0}[uint(addr-{1}+4)>>2]=data.y;{0}[uint("
|
||||
"addr-{1}+8)>>2]=data.z;{0}[uint(addr-{1}+12)>>2]=data.w;return;}}");
|
||||
define_body(load_func, index, "return {0}[uint(addr-{1})>>2];}}");
|
||||
define_body(load_func_64, index,
|
||||
"return uvec2({0}[uint(addr-{1})>>2],{0}[uint(addr-{1}+4)>>2]);}}");
|
||||
define_body(load_func_128, index,
|
||||
"return uvec4({0}[uint(addr-{1})>>2],{0}[uint(addr-{1}+4)>>2],{0}["
|
||||
"uint(addr-{1}+8)>>2],{0}[uint(addr-{1}+12)>>2]);}}");
|
||||
}
|
||||
write_func += '}';
|
||||
write_func_64 += '}';
|
||||
write_func_128 += '}';
|
||||
load_func += "return 0u;}";
|
||||
load_func_64 += "return uvec2(0);}";
|
||||
load_func_128 += "return uvec4(0);}";
|
||||
return write_func + write_func_64 + write_func_128 + load_func + load_func_64 + load_func_128;
|
||||
}
|
||||
|
||||
void EmitContext::SetupImages(Bindings& bindings) {
|
||||
image_buffers.reserve(info.image_buffer_descriptors.size());
|
||||
for (const auto& desc : info.image_buffer_descriptors) {
|
||||
image_buffers.push_back({bindings.image, desc.count});
|
||||
const auto format{ImageFormatString(desc.format)};
|
||||
const auto qualifier{ImageAccessQualifier(desc.is_written, desc.is_read)};
|
||||
const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""};
|
||||
header += fmt::format("layout(binding={}{}) uniform {}uimageBuffer img{}{};",
|
||||
bindings.image, format, qualifier, bindings.image, array_decorator);
|
||||
bindings.image += desc.count;
|
||||
}
|
||||
images.reserve(info.image_descriptors.size());
|
||||
for (const auto& desc : info.image_descriptors) {
|
||||
images.push_back({bindings.image, desc.count});
|
||||
const auto format{ImageFormatString(desc.format)};
|
||||
const auto image_type{ImageType(desc.type)};
|
||||
const auto qualifier{ImageAccessQualifier(desc.is_written, desc.is_read)};
|
||||
const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""};
|
||||
header += fmt::format("layout(binding={}{})uniform {}{} img{}{};", bindings.image, format,
|
||||
qualifier, image_type, bindings.image, array_decorator);
|
||||
bindings.image += desc.count;
|
||||
}
|
||||
}
|
||||
|
||||
void EmitContext::SetupTextures(Bindings& bindings) {
|
||||
texture_buffers.reserve(info.texture_buffer_descriptors.size());
|
||||
for (const auto& desc : info.texture_buffer_descriptors) {
|
||||
texture_buffers.push_back({bindings.texture, desc.count});
|
||||
const auto sampler_type{SamplerType(TextureType::Buffer, false)};
|
||||
const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""};
|
||||
header += fmt::format("layout(binding={}) uniform {} tex{}{};", bindings.texture,
|
||||
sampler_type, bindings.texture, array_decorator);
|
||||
bindings.texture += desc.count;
|
||||
}
|
||||
textures.reserve(info.texture_descriptors.size());
|
||||
for (const auto& desc : info.texture_descriptors) {
|
||||
textures.push_back({bindings.texture, desc.count});
|
||||
const auto sampler_type{SamplerType(desc.type, desc.is_depth)};
|
||||
const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""};
|
||||
header += fmt::format("layout(binding={}) uniform {} tex{}{};", bindings.texture,
|
||||
sampler_type, bindings.texture, array_decorator);
|
||||
bindings.texture += desc.count;
|
||||
}
|
||||
}
|
||||
|
||||
void EmitContext::DefineConstants() {
|
||||
if (info.uses_fswzadd) {
|
||||
header += "const float FSWZ_A[]=float[4](-1.f,1.f,-1.f,0.f);"
|
||||
"const float FSWZ_B[]=float[4](-1.f,-1.f,1.f,-1.f);";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
174
src/shader_recompiler/backend/glsl/emit_context.h
Normal file
174
src/shader_recompiler/backend/glsl/emit_context.h
Normal file
@@ -0,0 +1,174 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/var_alloc.h"
|
||||
#include "shader_recompiler/stage.h"
|
||||
|
||||
namespace Shader {
|
||||
struct Info;
|
||||
struct Profile;
|
||||
struct RuntimeInfo;
|
||||
} // namespace Shader
|
||||
|
||||
namespace Shader::Backend {
|
||||
struct Bindings;
|
||||
}
|
||||
|
||||
namespace Shader::IR {
|
||||
class Inst;
|
||||
struct Program;
|
||||
} // namespace Shader::IR
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
struct GenericElementInfo {
|
||||
std::string name;
|
||||
u32 first_element{};
|
||||
u32 num_components{};
|
||||
};
|
||||
|
||||
struct TextureImageDefinition {
|
||||
u32 binding;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
class EmitContext {
|
||||
public:
|
||||
explicit EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
|
||||
const RuntimeInfo& runtime_info_);
|
||||
|
||||
template <GlslVarType type, typename... Args>
|
||||
void Add(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
const auto var_def{var_alloc.AddDefine(inst, type)};
|
||||
if (var_def.empty()) {
|
||||
// skip assigment.
|
||||
code += fmt::format(fmt::runtime(format_str + 3), std::forward<Args>(args)...);
|
||||
} else {
|
||||
code += fmt::format(fmt::runtime(format_str), var_def, std::forward<Args>(args)...);
|
||||
}
|
||||
// TODO: Remove this
|
||||
code += '\n';
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU1(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U1>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF16x2(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F16x2>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU32(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U32>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF32(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F32>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU64(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U64>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF64(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F64>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU32x2(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U32x2>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF32x2(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F32x2>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU32x3(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U32x3>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF32x3(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F32x3>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddU32x4(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::U32x4>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddF32x4(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::F32x4>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddPrecF32(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::PrecF32>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void AddPrecF64(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
Add<GlslVarType::PrecF64>(format_str, inst, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void Add(const char* format_str, Args&&... args) {
|
||||
code += fmt::format(fmt::runtime(format_str), std::forward<Args>(args)...);
|
||||
// TODO: Remove this
|
||||
code += '\n';
|
||||
}
|
||||
|
||||
std::string header;
|
||||
std::string code;
|
||||
VarAlloc var_alloc;
|
||||
const Info& info;
|
||||
const Profile& profile;
|
||||
const RuntimeInfo& runtime_info;
|
||||
|
||||
Stage stage{};
|
||||
std::string_view stage_name = "invalid";
|
||||
std::string_view position_name = "gl_Position";
|
||||
|
||||
std::vector<TextureImageDefinition> texture_buffers;
|
||||
std::vector<TextureImageDefinition> image_buffers;
|
||||
std::vector<TextureImageDefinition> textures;
|
||||
std::vector<TextureImageDefinition> images;
|
||||
std::array<std::array<GenericElementInfo, 4>, 32> output_generics{};
|
||||
|
||||
u32 num_safety_loop_vars{};
|
||||
|
||||
bool uses_y_direction{};
|
||||
bool uses_cc_carry{};
|
||||
bool uses_geometry_passthrough{};
|
||||
|
||||
private:
|
||||
void SetupExtensions();
|
||||
void DefineConstantBuffers(Bindings& bindings);
|
||||
void DefineStorageBuffers(Bindings& bindings);
|
||||
void DefineGenericOutput(size_t index, u32 invocations);
|
||||
void DefineHelperFunctions();
|
||||
void DefineConstants();
|
||||
std::string DefineGlobalMemoryFunctions();
|
||||
void SetupImages(Bindings& bindings);
|
||||
void SetupTextures(Bindings& bindings);
|
||||
};
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
252
src/shader_recompiler/backend/glsl/emit_glsl.cpp
Normal file
252
src/shader_recompiler/backend/glsl/emit_glsl.cpp
Normal file
@@ -0,0 +1,252 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/div_ceil.h"
|
||||
#include "common/settings.h"
|
||||
#include "shader_recompiler/backend/glsl/emit_context.h"
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl.h"
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/frontend/ir/ir_emitter.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
template <class Func>
|
||||
struct FuncTraits {};
|
||||
|
||||
template <class ReturnType_, class... Args>
|
||||
struct FuncTraits<ReturnType_ (*)(Args...)> {
|
||||
using ReturnType = ReturnType_;
|
||||
|
||||
static constexpr size_t NUM_ARGS = sizeof...(Args);
|
||||
|
||||
template <size_t I>
|
||||
using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
|
||||
};
|
||||
|
||||
template <auto func, typename... Args>
|
||||
void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) {
|
||||
inst->SetDefinition<Id>(func(ctx, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename ArgType>
|
||||
auto Arg(EmitContext& ctx, const IR::Value& arg) {
|
||||
if constexpr (std::is_same_v<ArgType, std::string_view>) {
|
||||
return ctx.var_alloc.Consume(arg);
|
||||
} else if constexpr (std::is_same_v<ArgType, const IR::Value&>) {
|
||||
return arg;
|
||||
} else if constexpr (std::is_same_v<ArgType, u32>) {
|
||||
return arg.U32();
|
||||
} else if constexpr (std::is_same_v<ArgType, IR::Attribute>) {
|
||||
return arg.Attribute();
|
||||
} else if constexpr (std::is_same_v<ArgType, IR::Patch>) {
|
||||
return arg.Patch();
|
||||
} else if constexpr (std::is_same_v<ArgType, IR::Reg>) {
|
||||
return arg.Reg();
|
||||
}
|
||||
}
|
||||
|
||||
template <auto func, bool is_first_arg_inst, size_t... I>
|
||||
void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
|
||||
using Traits = FuncTraits<decltype(func)>;
|
||||
if constexpr (std::is_same_v<typename Traits::ReturnType, Id>) {
|
||||
if constexpr (is_first_arg_inst) {
|
||||
SetDefinition<func>(
|
||||
ctx, inst, *inst,
|
||||
Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
|
||||
} else {
|
||||
SetDefinition<func>(
|
||||
ctx, inst, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
|
||||
}
|
||||
} else {
|
||||
if constexpr (is_first_arg_inst) {
|
||||
func(ctx, *inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
|
||||
} else {
|
||||
func(ctx, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <auto func>
|
||||
void Invoke(EmitContext& ctx, IR::Inst* inst) {
|
||||
using Traits = FuncTraits<decltype(func)>;
|
||||
static_assert(Traits::NUM_ARGS >= 1, "Insufficient arguments");
|
||||
if constexpr (Traits::NUM_ARGS == 1) {
|
||||
Invoke<func, false>(ctx, inst, std::make_index_sequence<0>{});
|
||||
} else {
|
||||
using FirstArgType = typename Traits::template ArgType<1>;
|
||||
static constexpr bool is_first_arg_inst = std::is_same_v<FirstArgType, IR::Inst&>;
|
||||
using Indices = std::make_index_sequence<Traits::NUM_ARGS - (is_first_arg_inst ? 2 : 1)>;
|
||||
Invoke<func, is_first_arg_inst>(ctx, inst, Indices{});
|
||||
}
|
||||
}
|
||||
|
||||
void EmitInst(EmitContext& ctx, IR::Inst* inst) {
|
||||
switch (inst->GetOpcode()) {
|
||||
#define OPCODE(name, result_type, ...) \
|
||||
case IR::Opcode::name: \
|
||||
return Invoke<&Emit##name>(ctx, inst);
|
||||
#include "shader_recompiler/frontend/ir/opcodes.inc"
|
||||
#undef OPCODE
|
||||
}
|
||||
throw LogicError("Invalid opcode {}", inst->GetOpcode());
|
||||
}
|
||||
|
||||
bool IsReference(IR::Inst& inst) {
|
||||
return inst.GetOpcode() == IR::Opcode::Reference;
|
||||
}
|
||||
|
||||
void PrecolorInst(IR::Inst& phi) {
|
||||
// Insert phi moves before references to avoid overwritting other phis
|
||||
const size_t num_args{phi.NumArgs()};
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
IR::Block& phi_block{*phi.PhiBlock(i)};
|
||||
auto it{std::find_if_not(phi_block.rbegin(), phi_block.rend(), IsReference).base()};
|
||||
IR::IREmitter ir{phi_block, it};
|
||||
const IR::Value arg{phi.Arg(i)};
|
||||
if (arg.IsImmediate()) {
|
||||
ir.PhiMove(phi, arg);
|
||||
} else {
|
||||
ir.PhiMove(phi, IR::Value{arg.InstRecursive()});
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
IR::IREmitter{*phi.PhiBlock(i)}.Reference(IR::Value{&phi});
|
||||
}
|
||||
}
|
||||
|
||||
void Precolor(const IR::Program& program) {
|
||||
for (IR::Block* const block : program.blocks) {
|
||||
for (IR::Inst& phi : block->Instructions()) {
|
||||
if (!IR::IsPhi(phi)) {
|
||||
break;
|
||||
}
|
||||
PrecolorInst(phi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmitCode(EmitContext& ctx, const IR::Program& program) {
|
||||
for (const IR::AbstractSyntaxNode& node : program.syntax_list) {
|
||||
switch (node.type) {
|
||||
case IR::AbstractSyntaxNode::Type::Block:
|
||||
for (IR::Inst& inst : node.data.block->Instructions()) {
|
||||
EmitInst(ctx, &inst);
|
||||
}
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::If:
|
||||
ctx.Add("if({}){{", ctx.var_alloc.Consume(node.data.if_node.cond));
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::EndIf:
|
||||
ctx.Add("}}");
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Break:
|
||||
if (node.data.break_node.cond.IsImmediate()) {
|
||||
if (node.data.break_node.cond.U1()) {
|
||||
ctx.Add("break;");
|
||||
}
|
||||
} else {
|
||||
ctx.Add("if({}){{break;}}", ctx.var_alloc.Consume(node.data.break_node.cond));
|
||||
}
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Return:
|
||||
case IR::AbstractSyntaxNode::Type::Unreachable:
|
||||
ctx.Add("return;");
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Loop:
|
||||
ctx.Add("for(;;){{");
|
||||
break;
|
||||
case IR::AbstractSyntaxNode::Type::Repeat:
|
||||
if (Settings::values.disable_shader_loop_safety_checks) {
|
||||
ctx.Add("if(!{}){{break;}}}}", ctx.var_alloc.Consume(node.data.repeat.cond));
|
||||
} else {
|
||||
ctx.Add("if(--loop{}<0 || !{}){{break;}}}}", ctx.num_safety_loop_vars++,
|
||||
ctx.var_alloc.Consume(node.data.repeat.cond));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("AbstractSyntaxNode Type {}", node.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string GlslVersionSpecifier(const EmitContext& ctx) {
|
||||
if (ctx.uses_y_direction || ctx.info.stores.Legacy() || ctx.info.loads.Legacy()) {
|
||||
return " compatibility";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool IsPreciseType(GlslVarType type) {
|
||||
switch (type) {
|
||||
case GlslVarType::PrecF32:
|
||||
case GlslVarType::PrecF64:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void DefineVariables(const EmitContext& ctx, std::string& header) {
|
||||
for (u32 i = 0; i < static_cast<u32>(GlslVarType::Void); ++i) {
|
||||
const auto type{static_cast<GlslVarType>(i)};
|
||||
const auto& tracker{ctx.var_alloc.GetUseTracker(type)};
|
||||
const auto type_name{ctx.var_alloc.GetGlslType(type)};
|
||||
const bool has_precise_bug{ctx.stage == Stage::Fragment && ctx.profile.has_gl_precise_bug};
|
||||
const auto precise{!has_precise_bug && IsPreciseType(type) ? "precise " : ""};
|
||||
// Temps/return types that are never used are stored at index 0
|
||||
if (tracker.uses_temp) {
|
||||
header += fmt::format("{}{} t{}={}(0);", precise, type_name,
|
||||
ctx.var_alloc.Representation(0, type), type_name);
|
||||
}
|
||||
for (u32 index = 0; index < tracker.num_used; ++index) {
|
||||
header += fmt::format("{}{} {}={}(0);", precise, type_name,
|
||||
ctx.var_alloc.Representation(index, type), type_name);
|
||||
}
|
||||
}
|
||||
for (u32 i = 0; i < ctx.num_safety_loop_vars; ++i) {
|
||||
header += fmt::format("int loop{}=0x2000;", i);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
std::string EmitGLSL(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program,
|
||||
Bindings& bindings) {
|
||||
EmitContext ctx{program, bindings, profile, runtime_info};
|
||||
Precolor(program);
|
||||
EmitCode(ctx, program);
|
||||
const std::string version{fmt::format("#version 450{}\n", GlslVersionSpecifier(ctx))};
|
||||
ctx.header.insert(0, version);
|
||||
if (program.shared_memory_size > 0) {
|
||||
const auto requested_size{program.shared_memory_size};
|
||||
const auto max_size{profile.gl_max_compute_smem_size};
|
||||
const bool needs_clamp{requested_size > max_size};
|
||||
if (needs_clamp) {
|
||||
LOG_WARNING(Shader_GLSL, "Requested shared memory size ({}) exceeds device limit ({})",
|
||||
requested_size, max_size);
|
||||
}
|
||||
const auto smem_size{needs_clamp ? max_size : requested_size};
|
||||
ctx.header += fmt::format("shared uint smem[{}];", Common::DivCeil(smem_size, 4U));
|
||||
}
|
||||
ctx.header += "void main(){\n";
|
||||
if (program.local_memory_size > 0) {
|
||||
ctx.header += fmt::format("uint lmem[{}];", Common::DivCeil(program.local_memory_size, 4U));
|
||||
}
|
||||
DefineVariables(ctx, ctx.header);
|
||||
if (ctx.uses_cc_carry) {
|
||||
ctx.header += "uint carry;";
|
||||
}
|
||||
if (program.info.uses_subgroup_shuffles) {
|
||||
ctx.header += "bool shfl_in_bounds;";
|
||||
}
|
||||
ctx.code.insert(0, ctx.header);
|
||||
ctx.code += '}';
|
||||
return ctx.code;
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
24
src/shader_recompiler/backend/glsl/emit_glsl.h
Normal file
24
src/shader_recompiler/backend/glsl/emit_glsl.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "shader_recompiler/backend/bindings.h"
|
||||
#include "shader_recompiler/frontend/ir/program.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
|
||||
[[nodiscard]] std::string EmitGLSL(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||
IR::Program& program, Bindings& bindings);
|
||||
|
||||
[[nodiscard]] inline std::string EmitGLSL(const Profile& profile, IR::Program& program) {
|
||||
Bindings binding;
|
||||
return EmitGLSL(profile, {}, program, binding);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLSL
|
||||
418
src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp
Normal file
418
src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp
Normal file
@@ -0,0 +1,418 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_context.h"
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
constexpr char cas_loop[]{
|
||||
"for (;;){{uint old={};{}=atomicCompSwap({},old,{}({},{}));if({}==old){{break;}}}}"};
|
||||
|
||||
void SharedCasFunction(EmitContext& ctx, IR::Inst& inst, std::string_view offset,
|
||||
std::string_view value, std::string_view function) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
const std::string smem{fmt::format("smem[{}>>2]", offset)};
|
||||
ctx.Add(cas_loop, smem, ret, smem, function, smem, value, ret);
|
||||
}
|
||||
|
||||
void SsboCasFunction(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value, std::string_view function) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
const std::string ssbo{fmt::format("{}_ssbo{}[{}>>2]", ctx.stage_name, binding.U32(),
|
||||
ctx.var_alloc.Consume(offset))};
|
||||
ctx.Add(cas_loop, ssbo, ret, ssbo, function, ssbo, value, ret);
|
||||
}
|
||||
|
||||
void SsboCasFunctionF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value,
|
||||
std::string_view function) {
|
||||
const std::string ssbo{fmt::format("{}_ssbo{}[{}>>2]", ctx.stage_name, binding.U32(),
|
||||
ctx.var_alloc.Consume(offset))};
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
ctx.Add(cas_loop, ssbo, ret, ssbo, function, ssbo, value, ret);
|
||||
ctx.AddF32("{}=utof({});", inst, ret);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitSharedAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
|
||||
std::string_view value) {
|
||||
ctx.AddU32("{}=atomicAdd(smem[{}>>2],{});", inst, pointer_offset, value);
|
||||
}
|
||||
|
||||
void EmitSharedAtomicSMin32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
|
||||
std::string_view value) {
|
||||
const std::string u32_value{fmt::format("uint({})", value)};
|
||||
SharedCasFunction(ctx, inst, pointer_offset, u32_value, "CasMinS32");
|
||||
}
|
||||
|
||||
void EmitSharedAtomicUMin32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
|
||||
std::string_view value) {
|
||||
ctx.AddU32("{}=atomicMin(smem[{}>>2],{});", inst, pointer_offset, value);
|
||||
}
|
||||
|
||||
void EmitSharedAtomicSMax32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
|
||||
std::string_view value) {
|
||||
const std::string u32_value{fmt::format("uint({})", value)};
|
||||
SharedCasFunction(ctx, inst, pointer_offset, u32_value, "CasMaxS32");
|
||||
}
|
||||
|
||||
void EmitSharedAtomicUMax32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
|
||||
std::string_view value) {
|
||||
ctx.AddU32("{}=atomicMax(smem[{}>>2],{});", inst, pointer_offset, value);
|
||||
}
|
||||
|
||||
void EmitSharedAtomicInc32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
|
||||
std::string_view value) {
|
||||
SharedCasFunction(ctx, inst, pointer_offset, value, "CasIncrement");
|
||||
}
|
||||
|
||||
void EmitSharedAtomicDec32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
|
||||
std::string_view value) {
|
||||
SharedCasFunction(ctx, inst, pointer_offset, value, "CasDecrement");
|
||||
}
|
||||
|
||||
void EmitSharedAtomicAnd32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
|
||||
std::string_view value) {
|
||||
ctx.AddU32("{}=atomicAnd(smem[{}>>2],{});", inst, pointer_offset, value);
|
||||
}
|
||||
|
||||
void EmitSharedAtomicOr32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
|
||||
std::string_view value) {
|
||||
ctx.AddU32("{}=atomicOr(smem[{}>>2],{});", inst, pointer_offset, value);
|
||||
}
|
||||
|
||||
void EmitSharedAtomicXor32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
|
||||
std::string_view value) {
|
||||
ctx.AddU32("{}=atomicXor(smem[{}>>2],{});", inst, pointer_offset, value);
|
||||
}
|
||||
|
||||
void EmitSharedAtomicExchange32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
|
||||
std::string_view value) {
|
||||
ctx.AddU32("{}=atomicExchange(smem[{}>>2],{});", inst, pointer_offset, value);
|
||||
}
|
||||
|
||||
void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
|
||||
std::string_view value) {
|
||||
LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
|
||||
ctx.AddU64("{}=packUint2x32(uvec2(smem[{}>>2],smem[({}+4)>>2]));", inst, pointer_offset,
|
||||
pointer_offset);
|
||||
ctx.Add("smem[{}>>2]=unpackUint2x32({}).x;smem[({}+4)>>2]=unpackUint2x32({}).y;",
|
||||
pointer_offset, value, pointer_offset, value);
|
||||
}
|
||||
|
||||
void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
ctx.AddU32("{}=atomicAdd({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
|
||||
ctx.var_alloc.Consume(offset), value);
|
||||
}
|
||||
|
||||
void EmitStorageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
const std::string u32_value{fmt::format("uint({})", value)};
|
||||
SsboCasFunction(ctx, inst, binding, offset, u32_value, "CasMinS32");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicUMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
ctx.AddU32("{}=atomicMin({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
|
||||
ctx.var_alloc.Consume(offset), value);
|
||||
}
|
||||
|
||||
void EmitStorageAtomicSMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
const std::string u32_value{fmt::format("uint({})", value)};
|
||||
SsboCasFunction(ctx, inst, binding, offset, u32_value, "CasMaxS32");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicUMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
ctx.AddU32("{}=atomicMax({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
|
||||
ctx.var_alloc.Consume(offset), value);
|
||||
}
|
||||
|
||||
void EmitStorageAtomicInc32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
SsboCasFunction(ctx, inst, binding, offset, value, "CasIncrement");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicDec32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
SsboCasFunction(ctx, inst, binding, offset, value, "CasDecrement");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicAnd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
ctx.AddU32("{}=atomicAnd({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
|
||||
ctx.var_alloc.Consume(offset), value);
|
||||
}
|
||||
|
||||
void EmitStorageAtomicOr32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
ctx.AddU32("{}=atomicOr({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
|
||||
ctx.var_alloc.Consume(offset), value);
|
||||
}
|
||||
|
||||
void EmitStorageAtomicXor32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
ctx.AddU32("{}=atomicXor({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
|
||||
ctx.var_alloc.Consume(offset), value);
|
||||
}
|
||||
|
||||
void EmitStorageAtomicExchange32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
ctx.AddU32("{}=atomicExchange({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
|
||||
ctx.var_alloc.Consume(offset), value);
|
||||
}
|
||||
|
||||
void EmitStorageAtomicIAdd64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
|
||||
ctx.AddU64("{}=packUint2x32(uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]));", inst,
|
||||
ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
|
||||
binding.U32(), ctx.var_alloc.Consume(offset));
|
||||
ctx.Add("{}_ssbo{}[{}>>2]+=unpackUint2x32({}).x;{}_ssbo{}[({}>>2)+1]+=unpackUint2x32({}).y;",
|
||||
ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value, ctx.stage_name,
|
||||
binding.U32(), ctx.var_alloc.Consume(offset), value);
|
||||
}
|
||||
|
||||
void EmitStorageAtomicSMin64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
|
||||
ctx.AddU64("{}=packInt2x32(ivec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]));", inst,
|
||||
ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
|
||||
binding.U32(), ctx.var_alloc.Consume(offset));
|
||||
ctx.Add("for(int i=0;i<2;++i){{ "
|
||||
"{}_ssbo{}[({}>>2)+i]=uint(min(int({}_ssbo{}[({}>>2)+i]),unpackInt2x32(int64_t({}))[i])"
|
||||
");}}",
|
||||
ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
|
||||
binding.U32(), ctx.var_alloc.Consume(offset), value);
|
||||
}
|
||||
|
||||
void EmitStorageAtomicUMin64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
|
||||
ctx.AddU64("{}=packUint2x32(uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]));", inst,
|
||||
ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
|
||||
binding.U32(), ctx.var_alloc.Consume(offset));
|
||||
ctx.Add("for(int i=0;i<2;++i){{ "
|
||||
"{}_ssbo{}[({}>>2)+i]=min({}_ssbo{}[({}>>2)+i],unpackUint2x32(uint64_t({}))[i]);}}",
|
||||
ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
|
||||
binding.U32(), ctx.var_alloc.Consume(offset), value);
|
||||
}
|
||||
|
||||
void EmitStorageAtomicSMax64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
|
||||
ctx.AddU64("{}=packInt2x32(ivec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]));", inst,
|
||||
ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
|
||||
binding.U32(), ctx.var_alloc.Consume(offset));
|
||||
ctx.Add("for(int i=0;i<2;++i){{ "
|
||||
"{}_ssbo{}[({}>>2)+i]=uint(max(int({}_ssbo{}[({}>>2)+i]),unpackInt2x32(int64_t({}))[i])"
|
||||
");}}",
|
||||
ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
|
||||
binding.U32(), ctx.var_alloc.Consume(offset), value);
|
||||
}
|
||||
|
||||
void EmitStorageAtomicUMax64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
|
||||
ctx.AddU64("{}=packUint2x32(uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]));", inst,
|
||||
ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
|
||||
binding.U32(), ctx.var_alloc.Consume(offset));
|
||||
ctx.Add("for(int "
|
||||
"i=0;i<2;++i){{{}_ssbo{}[({}>>2)+i]=max({}_ssbo{}[({}>>2)+i],unpackUint2x32(uint64_t({}"
|
||||
"))[i]);}}",
|
||||
ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
|
||||
binding.U32(), ctx.var_alloc.Consume(offset), value);
|
||||
}
|
||||
|
||||
void EmitStorageAtomicAnd64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
ctx.AddU64(
|
||||
"{}=packUint2x32(uvec2(atomicAnd({}_ssbo{}[{}>>2],unpackUint2x32({}).x),atomicAnd({}_"
|
||||
"ssbo{}[({}>>2)+1],unpackUint2x32({}).y)));",
|
||||
inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value, ctx.stage_name,
|
||||
binding.U32(), ctx.var_alloc.Consume(offset), value);
|
||||
}
|
||||
|
||||
void EmitStorageAtomicOr64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
ctx.AddU64("{}=packUint2x32(uvec2(atomicOr({}_ssbo{}[{}>>2],unpackUint2x32({}).x),atomicOr({}_"
|
||||
"ssbo{}[({}>>2)+1],unpackUint2x32({}).y)));",
|
||||
inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value,
|
||||
ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value);
|
||||
}
|
||||
|
||||
void EmitStorageAtomicXor64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
ctx.AddU64(
|
||||
"{}=packUint2x32(uvec2(atomicXor({}_ssbo{}[{}>>2],unpackUint2x32({}).x),atomicXor({}_"
|
||||
"ssbo{}[({}>>2)+1],unpackUint2x32({}).y)));",
|
||||
inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value, ctx.stage_name,
|
||||
binding.U32(), ctx.var_alloc.Consume(offset), value);
|
||||
}
|
||||
|
||||
void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
ctx.AddU64("{}=packUint2x32(uvec2(atomicExchange({}_ssbo{}[{}>>2],unpackUint2x32({}).x),"
|
||||
"atomicExchange({}_ssbo{}[({}>>2)+1],unpackUint2x32({}).y)));",
|
||||
inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value,
|
||||
ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value);
|
||||
}
|
||||
|
||||
void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
SsboCasFunctionF32(ctx, inst, binding, offset, value, "CasFloatAdd");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicAddF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
SsboCasFunction(ctx, inst, binding, offset, value, "CasFloatAdd16x2");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicAddF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
SsboCasFunction(ctx, inst, binding, offset, value, "CasFloatAdd32x2");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicMinF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
SsboCasFunction(ctx, inst, binding, offset, value, "CasFloatMin16x2");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicMinF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
SsboCasFunction(ctx, inst, binding, offset, value, "CasFloatMin32x2");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicMaxF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
SsboCasFunction(ctx, inst, binding, offset, value, "CasFloatMax16x2");
|
||||
}
|
||||
|
||||
void EmitStorageAtomicMaxF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset, std::string_view value) {
|
||||
SsboCasFunction(ctx, inst, binding, offset, value, "CasFloatMax32x2");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicIAdd32(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicSMin32(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicUMin32(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicSMax32(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicUMax32(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicInc32(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicDec32(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicAnd32(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicOr32(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicXor32(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicExchange32(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicIAdd64(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicSMin64(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicUMin64(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicSMax64(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicUMax64(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicInc64(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicDec64(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicAnd64(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicOr64(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicXor64(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicExchange64(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicAddF32(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicAddF16x2(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicAddF32x2(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicMinF16x2(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicMinF32x2(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicMaxF16x2(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
|
||||
void EmitGlobalAtomicMaxF32x2(EmitContext&) {
|
||||
throw NotImplementedException("GLSL Instrucion");
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
21
src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp
Normal file
21
src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "shader_recompiler/backend/glsl/emit_context.h"
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
void EmitBarrier(EmitContext& ctx) {
|
||||
ctx.Add("barrier();");
|
||||
}
|
||||
|
||||
void EmitWorkgroupMemoryBarrier(EmitContext& ctx) {
|
||||
ctx.Add("groupMemoryBarrier();");
|
||||
}
|
||||
|
||||
void EmitDeviceMemoryBarrier(EmitContext& ctx) {
|
||||
ctx.Add("memoryBarrier();");
|
||||
}
|
||||
} // namespace Shader::Backend::GLSL
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user