Compare commits
388 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76f178ba6e | ||
|
|
08470d261d | ||
|
|
ffe1e2b5ec | ||
|
|
997a802bd6 | ||
|
|
75795a9a63 | ||
|
|
9bd9980372 | ||
|
|
6286b8dedd | ||
|
|
8ba06aa4e1 | ||
|
|
dd236c6c1d | ||
|
|
10ba8d16be | ||
|
|
7a2f60df26 | ||
|
|
8a6a25e4b6 | ||
|
|
a429644672 | ||
|
|
cedbe925cd | ||
|
|
e84b760016 | ||
|
|
744b207d92 | ||
|
|
950b6dbc80 | ||
|
|
3194f14aca | ||
|
|
8244536f7a | ||
|
|
23c7dda710 | ||
|
|
e6aff11057 | ||
|
|
aa41fcc04e | ||
|
|
ac4154bfde | ||
|
|
f8382c9d9d | ||
|
|
6ca8637d4c | ||
|
|
497f593525 | ||
|
|
7981910746 | ||
|
|
dc4415811c | ||
|
|
4afebf26b6 | ||
|
|
5a5c6d4ed8 | ||
|
|
e731c4b991 | ||
|
|
fc37672f26 | ||
|
|
977418c65b | ||
|
|
f66743cd0c | ||
|
|
d4e93cf38c | ||
|
|
bdcedc8506 | ||
|
|
22f4268c2f | ||
|
|
7051dc1902 | ||
|
|
01af036c1f | ||
|
|
63c2635e6f | ||
|
|
dbfbe352e0 | ||
|
|
e5bb5d13c4 | ||
|
|
e70451d967 | ||
|
|
81fa492825 | ||
|
|
bdddbe2daa | ||
|
|
06dea163fa | ||
|
|
bc681dc555 | ||
|
|
9418b983bd | ||
|
|
76d6178e4a | ||
|
|
38c1e77f01 | ||
|
|
b6b2e31e5e | ||
|
|
fc51ece7bf | ||
|
|
98d85cdc20 | ||
|
|
dab450ec46 | ||
|
|
351816ac38 | ||
|
|
acf328a71f | ||
|
|
9f46066bda | ||
|
|
ba9674862d | ||
|
|
ac7ee21331 | ||
|
|
56ea0f8acb | ||
|
|
716d6aee30 | ||
|
|
664fa4ea06 | ||
|
|
f5658a9fda | ||
|
|
edb9cccb36 | ||
|
|
f54d2d3114 | ||
|
|
d787856621 | ||
|
|
9fdfd58f9f | ||
|
|
cdeadd448b | ||
|
|
1c45c8086e | ||
|
|
2fd3b328ae | ||
|
|
230ac6a4e8 | ||
|
|
eae2ed6b07 | ||
|
|
38036eb1c8 | ||
|
|
e8ded20d24 | ||
|
|
53d673a7d3 | ||
|
|
311d2fc768 | ||
|
|
b16c8e0e8d | ||
|
|
7cc46a6faa | ||
|
|
ddafc99776 | ||
|
|
d64edf21bb | ||
|
|
5afc397d52 | ||
|
|
6442e02c5d | ||
|
|
8e6e55d6f8 | ||
|
|
464bd5fad7 | ||
|
|
86b1f15d9a | ||
|
|
52acb7f9a0 | ||
|
|
d91a880f11 | ||
|
|
71cc772988 | ||
|
|
f91046bf8d | ||
|
|
a7131af7d6 | ||
|
|
8baf98e439 | ||
|
|
c5afe93dcc | ||
|
|
4373fa8042 | ||
|
|
4dfd5c84ea | ||
|
|
380fc8d2e1 | ||
|
|
c51dbf8038 | ||
|
|
41905ee467 | ||
|
|
35145bd529 | ||
|
|
27cbb75e7c | ||
|
|
42cb8f1124 | ||
|
|
9b8fb3c756 | ||
|
|
d71d7d917e | ||
|
|
134f3ff9b4 | ||
|
|
3287b1247d | ||
|
|
240d45830d | ||
|
|
3377b78ea7 | ||
|
|
801fd04f75 | ||
|
|
e183820956 | ||
|
|
70a31eda62 | ||
|
|
5ed377b989 | ||
|
|
e7d97605e8 | ||
|
|
835a3d09c6 | ||
|
|
731a9a322e | ||
|
|
d3dc4e399c | ||
|
|
69c7a01f88 | ||
|
|
62560f1e63 | ||
|
|
afebdda203 | ||
|
|
4bc4851d45 | ||
|
|
47459f6a36 | ||
|
|
2fae1e6205 | ||
|
|
b67360c0f8 | ||
|
|
8d5bdcb17b | ||
|
|
c320702092 | ||
|
|
ae6189d7c2 | ||
|
|
7acebd7eb6 | ||
|
|
8e9f23f393 | ||
|
|
4d711dface | ||
|
|
3dcaa84ba4 | ||
|
|
666d431ad8 | ||
|
|
244fe13219 | ||
|
|
b30b1f741d | ||
|
|
829f424618 | ||
|
|
a166217480 | ||
|
|
753bc2026f | ||
|
|
54681909be | ||
|
|
00607fe1e0 | ||
|
|
325977c0c6 | ||
|
|
70ff82f72d | ||
|
|
96a4abe12d | ||
|
|
93547cac68 | ||
|
|
911c56ccef | ||
|
|
465ba30d08 | ||
|
|
e24197bb3f | ||
|
|
00e9ba0603 | ||
|
|
f159a12820 | ||
|
|
3a10016e38 | ||
|
|
4dcca90ef4 | ||
|
|
e22816a5bb | ||
|
|
daae6a323b | ||
|
|
38fe070d78 | ||
|
|
ca2d228c9d | ||
|
|
c21dc36cda | ||
|
|
95b804ff05 | ||
|
|
448856695a | ||
|
|
825d629565 | ||
|
|
fce33adcf1 | ||
|
|
e891ff9a0c | ||
|
|
8357908099 | ||
|
|
503ebe9b96 | ||
|
|
e4bc3c3342 | ||
|
|
eb5861e0a2 | ||
|
|
b1acb4f73f | ||
|
|
b1061afed9 | ||
|
|
e612242977 | ||
|
|
978172530e | ||
|
|
120f688272 | ||
|
|
e1932351a9 | ||
|
|
66a8a3e887 | ||
|
|
0528be5c92 | ||
|
|
e8efd5a901 | ||
|
|
bd8b9bbcee | ||
|
|
22e825a3bc | ||
|
|
c281173df6 | ||
|
|
1aa75b1081 | ||
|
|
59d0d34dce | ||
|
|
84e9f9f395 | ||
|
|
16cfbb068c | ||
|
|
662feb8c1c | ||
|
|
e4f9ce0379 | ||
|
|
aa6fe3f1aa | ||
|
|
49eff536d0 | ||
|
|
4a8fe67964 | ||
|
|
0361aa1915 | ||
|
|
fa1d625eed | ||
|
|
1e84d22275 | ||
|
|
002d9508a0 | ||
|
|
67e7186d79 | ||
|
|
fc205a1bc5 | ||
|
|
2cdda8c564 | ||
|
|
dba112e510 | ||
|
|
c083ea7d78 | ||
|
|
6fc485a607 | ||
|
|
64facb403e | ||
|
|
08c638f249 | ||
|
|
dfa2e336ba | ||
|
|
6af8ff24c9 | ||
|
|
85a4222a8c | ||
|
|
ca7618684c | ||
|
|
735c003a70 | ||
|
|
ef7f6eb67d | ||
|
|
a6a350ddc3 | ||
|
|
887d5288ef | ||
|
|
ac204754d4 | ||
|
|
6669b359a3 | ||
|
|
042256c6bb | ||
|
|
6ac3eb4d87 | ||
|
|
f9df2c6bcd | ||
|
|
cd0e28c9ec | ||
|
|
a33870996b | ||
|
|
42f1874965 | ||
|
|
1bd95a314f | ||
|
|
b1498d2c54 | ||
|
|
62437943a7 | ||
|
|
2eeea90713 | ||
|
|
3ce66776ec | ||
|
|
35bb9239ca | ||
|
|
98c8948b23 | ||
|
|
15cadc3948 | ||
|
|
a5bfc0d045 | ||
|
|
a42a6e1a2c | ||
|
|
4f8d152b18 | ||
|
|
231601763c | ||
|
|
bf1a1d989f | ||
|
|
13afd0e5b0 | ||
|
|
d8f5c45051 | ||
|
|
b727d99441 | ||
|
|
3c22bd92d8 | ||
|
|
9e46953580 | ||
|
|
46a1888e02 | ||
|
|
37536d7a49 | ||
|
|
40a2c57df5 | ||
|
|
b910a83a47 | ||
|
|
b01dd7d1c8 | ||
|
|
f7ec078592 | ||
|
|
758ad3f75d | ||
|
|
9b08698a0c | ||
|
|
69ad6279e4 | ||
|
|
6530144ccb | ||
|
|
ba6f390448 | ||
|
|
7f52efdf61 | ||
|
|
dacf83ac02 | ||
|
|
9e74e6988b | ||
|
|
eed789d0d1 | ||
|
|
b92dfcd7f2 | ||
|
|
1c4bf9cbfa | ||
|
|
5ccb07933a | ||
|
|
17a7fa751b | ||
|
|
9677db03da | ||
|
|
1bc0da3dea | ||
|
|
7d9a5e9e30 | ||
|
|
07a954e67f | ||
|
|
1eee891f6e | ||
|
|
e8125af8dd | ||
|
|
d3e433a380 | ||
|
|
7c16b3551b | ||
|
|
0914c70b7f | ||
|
|
2392b548be | ||
|
|
f92236976b | ||
|
|
04d1134191 | ||
|
|
d5ab0358b6 | ||
|
|
2a662fea36 | ||
|
|
e1a16a52fa | ||
|
|
0f343d32c4 | ||
|
|
42708c762e | ||
|
|
915d73f3b8 | ||
|
|
a0321b984f | ||
|
|
f646321dd0 | ||
|
|
c8f5f54a44 | ||
|
|
925521da5f | ||
|
|
d2d5554296 | ||
|
|
b95f064b51 | ||
|
|
1698143a1d | ||
|
|
96ac3d518a | ||
|
|
e38ed26b98 | ||
|
|
c7db1ef565 | ||
|
|
1326e326f5 | ||
|
|
5056d23d0d | ||
|
|
6c0c2dfabc | ||
|
|
1c385362f5 | ||
|
|
7ee6065178 | ||
|
|
969357af1a | ||
|
|
ebbfe73557 | ||
|
|
e1efab1f51 | ||
|
|
db2f547434 | ||
|
|
a17214baea | ||
|
|
aef159354c | ||
|
|
0aaa69e4d7 | ||
|
|
f3d4d4eaa8 | ||
|
|
1f57f679a4 | ||
|
|
01a05b48b7 | ||
|
|
795893a9a5 | ||
|
|
c6f78a4a6d | ||
|
|
e25297536f | ||
|
|
14877b8f35 | ||
|
|
b2a38cce4e | ||
|
|
667f026c95 | ||
|
|
2e16c23784 | ||
|
|
dc672ca4b3 | ||
|
|
add2c38b73 | ||
|
|
0c82b00dfd | ||
|
|
571451bdfe | ||
|
|
45ac1c62c6 | ||
|
|
00e3eab9c1 | ||
|
|
466ce715e4 | ||
|
|
3c648e3e2d | ||
|
|
78ab2e0474 | ||
|
|
7cbe6748c3 | ||
|
|
e22ad52cdb | ||
|
|
1e9213632a | ||
|
|
1dda77d392 | ||
|
|
e2dd59e341 | ||
|
|
2b4cdb73b6 | ||
|
|
754aac331f | ||
|
|
3ef5f2017d | ||
|
|
1989e1b9ac | ||
|
|
3d0a2375ca | ||
|
|
a1bf353780 | ||
|
|
b9472eae44 | ||
|
|
d219a96cc8 | ||
|
|
ea956c823e | ||
|
|
5c90d22f3d | ||
|
|
179bafa7cb | ||
|
|
0728dfef84 | ||
|
|
d4da52bbd9 | ||
|
|
f5cf67140b | ||
|
|
19bce3685a | ||
|
|
27d57e0c4a | ||
|
|
7dc488a375 | ||
|
|
4a6a1aeab4 | ||
|
|
ef27b4b7b5 | ||
|
|
6b2719c0bb | ||
|
|
dc7ebc2d01 | ||
|
|
10d8afb302 | ||
|
|
1956a34ee5 | ||
|
|
c0c4da27d9 | ||
|
|
fe8e5d8ae4 | ||
|
|
ecf275887b | ||
|
|
fbbad95845 | ||
|
|
2342c0d50e | ||
|
|
bf0c929d4c | ||
|
|
d65fa7d65c | ||
|
|
d31156931d | ||
|
|
b2bc7682b4 | ||
|
|
c8261a1a57 | ||
|
|
fd4c5463e8 | ||
|
|
88cb05e6e7 | ||
|
|
e61c7e9310 | ||
|
|
47106ab152 | ||
|
|
1b6308727c | ||
|
|
a2c2c5768f | ||
|
|
a8508f2bc0 | ||
|
|
556f3a6e9a | ||
|
|
e545c2322c | ||
|
|
2ef8af93aa | ||
|
|
ad4e5c15fb | ||
|
|
f6f0762e81 | ||
|
|
7f6c686d55 | ||
|
|
ec206f7f95 | ||
|
|
eaf60ca5d8 | ||
|
|
93acfbd3a5 | ||
|
|
9295966d26 | ||
|
|
9fc42fffd9 | ||
|
|
493f0ad904 | ||
|
|
ba84f0988f | ||
|
|
9e42025e5b | ||
|
|
76b55c3624 | ||
|
|
293d4d553a | ||
|
|
6a0220b2e1 | ||
|
|
1caf3f11c8 | ||
|
|
bfda5ff3f6 | ||
|
|
fd62bdf377 | ||
|
|
14c2a4a2ec | ||
|
|
6910ade146 | ||
|
|
91aa58e410 | ||
|
|
6d3a046caa | ||
|
|
54a00ee4cf | ||
|
|
cc0694559f | ||
|
|
bcd348f238 | ||
|
|
c31382ced5 | ||
|
|
73d2d3342d | ||
|
|
aae8c180cb | ||
|
|
ef9920e164 | ||
|
|
fe1238be7a | ||
|
|
2b58652f08 | ||
|
|
3217400dd1 | ||
|
|
336a4f8e99 | ||
|
|
cbea8c74de | ||
|
|
e3cad7d49e |
@@ -6,9 +6,9 @@ function(copy_yuzu_Qt5_deps target_dir)
|
||||
set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/")
|
||||
set(Qt5_IMAGEFORMATS_DIR "${Qt5_DIR}/../../../plugins/imageformats/")
|
||||
set(Qt5_RESOURCES_DIR "${Qt5_DIR}/../../../resources/")
|
||||
set(PLATFORMS ${DLL_DEST}platforms/)
|
||||
set(STYLES ${DLL_DEST}styles/)
|
||||
set(IMAGEFORMATS ${DLL_DEST}imageformats/)
|
||||
set(PLATFORMS ${DLL_DEST}plugins/platforms/)
|
||||
set(STYLES ${DLL_DEST}plugins/styles/)
|
||||
set(IMAGEFORMATS ${DLL_DEST}plugins/imageformats/)
|
||||
windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST}
|
||||
icudt*.dll
|
||||
icuin*.dll
|
||||
@@ -42,11 +42,15 @@ function(copy_yuzu_Qt5_deps target_dir)
|
||||
icudtl.dat
|
||||
)
|
||||
endif ()
|
||||
|
||||
windows_copy_files(yuzu ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*)
|
||||
windows_copy_files(yuzu ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*)
|
||||
windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS}
|
||||
qjpeg$<$<CONFIG:Debug>:d>.*
|
||||
qgif$<$<CONFIG:Debug>:d>.*
|
||||
)
|
||||
# Create an empty qt.conf file. Qt will detect that this file exists, and use the folder that its in as the root folder.
|
||||
# This way it'll look for plugins in the root/plugins/ folder
|
||||
add_custom_command(TARGET yuzu POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E touch ${DLL_DEST}qt.conf
|
||||
)
|
||||
endfunction(copy_yuzu_Qt5_deps)
|
||||
|
||||
@@ -57,8 +57,6 @@ set(HASH_FILES
|
||||
"${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}/renderer_opengl/gl_shader_gen.cpp"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.h"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic_half.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic_half_immediate.cpp"
|
||||
@@ -91,8 +89,6 @@ set(HASH_FILES
|
||||
"${VIDEO_CORE}/shader/ast.h"
|
||||
"${VIDEO_CORE}/shader/compiler_settings.cpp"
|
||||
"${VIDEO_CORE}/shader/compiler_settings.h"
|
||||
"${VIDEO_CORE}/shader/const_buffer_locker.cpp"
|
||||
"${VIDEO_CORE}/shader/const_buffer_locker.h"
|
||||
"${VIDEO_CORE}/shader/control_flow.cpp"
|
||||
"${VIDEO_CORE}/shader/control_flow.h"
|
||||
"${VIDEO_CORE}/shader/decode.cpp"
|
||||
@@ -101,9 +97,13 @@ set(HASH_FILES
|
||||
"${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)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
yuzu emulator
|
||||
=============
|
||||
[](https://travis-ci.org/yuzu-emu/yuzu)
|
||||
[](https://travis-ci.com/yuzu-emu/yuzu)
|
||||
[](https://dev.azure.com/yuzu-emu/yuzu/)
|
||||
[](https://discord.gg/XQV6dn9)
|
||||
|
||||
yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/).
|
||||
|
||||
@@ -21,7 +22,7 @@ For development discussion, please join us on [Discord](https://discord.gg/XQV6d
|
||||
|
||||
Most of the development happens on GitHub. It's also where [our central repository](https://github.com/yuzu-emu/yuzu) is hosted.
|
||||
|
||||
If you want to contribute please take a look at the [Contributor's Guide](CONTRIBUTING.md) and [Developer Information](https://github.com/yuzu-emu/yuzu/wiki/Developer-Information). You should as well contact any of the developers on Discord in order to know about the current state of the emulator.
|
||||
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 should also contact any of the developers on Discord in order to know about the current state of the emulator.
|
||||
|
||||
### Building
|
||||
|
||||
|
||||
6
dist/license.md
vendored
6
dist/license.md
vendored
@@ -2,8 +2,8 @@ The icons in this folder and its subfolders have the following licenses:
|
||||
|
||||
Icon Name | License | Origin/Author
|
||||
--- | --- | ---
|
||||
qt_themes/default/icons/16x16/checked.png | Free for non-commercial use
|
||||
qt_themes/default/icons/16x16/failed.png | Free for non-commercial use
|
||||
qt_themes/default/icons/16x16/checked.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/default/icons/16x16/failed.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/default/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/default/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/default/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
|
||||
@@ -11,8 +11,6 @@ qt_themes/default/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/default/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/default/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team
|
||||
qt_themes/default/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/qdarkstyle/icons/16x16/checked.png | Free for non-commercial use
|
||||
qt_themes/qdarkstyle/icons/16x16/failed.png | Free for non-commercial use
|
||||
qt_themes/qdarkstyle/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/qdarkstyle/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
|
||||
qt_themes/qdarkstyle/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
|
||||
|
||||
BIN
dist/qt_themes/default/icons/16x16/checked.png
vendored
BIN
dist/qt_themes/default/icons/16x16/checked.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 451 B After Width: | Height: | Size: 657 B |
BIN
dist/qt_themes/default/icons/16x16/failed.png
vendored
BIN
dist/qt_themes/default/icons/16x16/failed.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 428 B After Width: | Height: | Size: 524 B |
2
externals/httplib/README.md
vendored
2
externals/httplib/README.md
vendored
@@ -1,4 +1,4 @@
|
||||
From https://github.com/yhirose/cpp-httplib/commit/d9479bc0b12e8a1e8bce2d34da4feeef488581f3
|
||||
From https://github.com/yhirose/cpp-httplib/tree/fce8e6fefdab4ad48bc5b25c98e5ebfda4f3cf53
|
||||
|
||||
MIT License
|
||||
|
||||
|
||||
2435
externals/httplib/httplib.h
vendored
2435
externals/httplib/httplib.h
vendored
File diff suppressed because it is too large
Load Diff
7
externals/microprofile/microprofile.h
vendored
7
externals/microprofile/microprofile.h
vendored
@@ -243,6 +243,7 @@ typedef uint32_t ThreadIdType;
|
||||
#define MICROPROFILE_DEFINE_GPU(var, name, color) MicroProfileToken g_mp_##var = MicroProfileGetToken("GPU", name, color, MicroProfileTokenTypeGpu)
|
||||
#define MICROPROFILE_TOKEN_PASTE0(a, b) a ## b
|
||||
#define MICROPROFILE_TOKEN_PASTE(a, b) MICROPROFILE_TOKEN_PASTE0(a,b)
|
||||
#define MICROPROFILE_TOKEN(var) g_mp_##var
|
||||
#define MICROPROFILE_SCOPE(var) MicroProfileScopeHandler MICROPROFILE_TOKEN_PASTE(foo, __LINE__)(g_mp_##var)
|
||||
#define MICROPROFILE_SCOPE_TOKEN(token) MicroProfileScopeHandler MICROPROFILE_TOKEN_PASTE(foo, __LINE__)(token)
|
||||
#define MICROPROFILE_SCOPEI(group, name, color) static MicroProfileToken MICROPROFILE_TOKEN_PASTE(g_mp,__LINE__) = MicroProfileGetToken(group, name, color, MicroProfileTokenTypeCpu); MicroProfileScopeHandler MICROPROFILE_TOKEN_PASTE(foo,__LINE__)( MICROPROFILE_TOKEN_PASTE(g_mp,__LINE__))
|
||||
@@ -827,7 +828,7 @@ inline MicroProfileLogEntry MicroProfileMakeLogIndex(uint64_t nBegin, MicroProfi
|
||||
MicroProfileLogEntry Entry = (nBegin<<62) | ((0x3fff&nToken)<<48) | (MP_LOG_TICK_MASK&nTick);
|
||||
int t = MicroProfileLogType(Entry);
|
||||
uint64_t nTimerIndex = MicroProfileLogTimerIndex(Entry);
|
||||
MP_ASSERT(t == nBegin);
|
||||
MP_ASSERT((uint64_t)t == nBegin);
|
||||
MP_ASSERT(nTimerIndex == (nToken&0x3fff));
|
||||
return Entry;
|
||||
|
||||
@@ -1555,10 +1556,10 @@ void MicroProfileFlip()
|
||||
|
||||
pFramePut->nFrameStartCpu = MP_TICK();
|
||||
pFramePut->nFrameStartGpu = (uint32_t)MicroProfileGpuInsertTimeStamp();
|
||||
if(pFrameNext->nFrameStartGpu != (uint64_t)-1)
|
||||
if(pFrameNext->nFrameStartGpu != -1)
|
||||
pFrameNext->nFrameStartGpu = MicroProfileGpuGetTimeStamp((uint32_t)pFrameNext->nFrameStartGpu);
|
||||
|
||||
if(pFrameCurrent->nFrameStartGpu == (uint64_t)-1)
|
||||
if(pFrameCurrent->nFrameStartGpu == -1)
|
||||
pFrameCurrent->nFrameStartGpu = pFrameNext->nFrameStartGpu + 1;
|
||||
|
||||
uint64_t nFrameStartCpu = pFrameCurrent->nFrameStartCpu;
|
||||
|
||||
@@ -343,8 +343,8 @@ The icons used in this project have the following licenses:
|
||||
|
||||
Icon Name | License | Origin/Author
|
||||
--- | --- | ---
|
||||
checked.png | Free for non-commercial use
|
||||
failed.png | Free for non-commercial use
|
||||
checked.png | CC BY-ND 3.0 | https://icons8.com
|
||||
failed.png | CC BY-ND 3.0 | https://icons8.com
|
||||
lock.png | CC BY-ND 3.0 | https://icons8.com
|
||||
plus_folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
|
||||
bad_folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
|
||||
|
||||
@@ -5,21 +5,141 @@
|
||||
#define _USE_MATH_DEFINES
|
||||
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
#include "audio_core/algorithm/interpolate.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
/// The Lanczos kernel
|
||||
static double Lanczos(std::size_t a, double x) {
|
||||
if (x == 0.0)
|
||||
return 1.0;
|
||||
const double px = M_PI * x;
|
||||
return a * std::sin(px) * std::sin(px / a) / (px * px);
|
||||
}
|
||||
constexpr std::array<s16, 512> curve_lut0{
|
||||
6600, 19426, 6722, 3, 6479, 19424, 6845, 9, 6359, 19419, 6968, 15, 6239,
|
||||
19412, 7093, 22, 6121, 19403, 7219, 28, 6004, 19391, 7345, 34, 5888, 19377,
|
||||
7472, 41, 5773, 19361, 7600, 48, 5659, 19342, 7728, 55, 5546, 19321, 7857,
|
||||
62, 5434, 19298, 7987, 69, 5323, 19273, 8118, 77, 5213, 19245, 8249, 84,
|
||||
5104, 19215, 8381, 92, 4997, 19183, 8513, 101, 4890, 19148, 8646, 109, 4785,
|
||||
19112, 8780, 118, 4681, 19073, 8914, 127, 4579, 19031, 9048, 137, 4477, 18988,
|
||||
9183, 147, 4377, 18942, 9318, 157, 4277, 18895, 9454, 168, 4179, 18845, 9590,
|
||||
179, 4083, 18793, 9726, 190, 3987, 18738, 9863, 202, 3893, 18682, 10000, 215,
|
||||
3800, 18624, 10137, 228, 3709, 18563, 10274, 241, 3618, 18500, 10411, 255, 3529,
|
||||
18436, 10549, 270, 3441, 18369, 10687, 285, 3355, 18300, 10824, 300, 3269, 18230,
|
||||
10962, 317, 3186, 18157, 11100, 334, 3103, 18082, 11238, 351, 3022, 18006, 11375,
|
||||
369, 2942, 17927, 11513, 388, 2863, 17847, 11650, 408, 2785, 17765, 11788, 428,
|
||||
2709, 17681, 11925, 449, 2635, 17595, 12062, 471, 2561, 17507, 12198, 494, 2489,
|
||||
17418, 12334, 517, 2418, 17327, 12470, 541, 2348, 17234, 12606, 566, 2280, 17140,
|
||||
12741, 592, 2213, 17044, 12876, 619, 2147, 16946, 13010, 647, 2083, 16846, 13144,
|
||||
675, 2020, 16745, 13277, 704, 1958, 16643, 13409, 735, 1897, 16539, 13541, 766,
|
||||
1838, 16434, 13673, 798, 1780, 16327, 13803, 832, 1723, 16218, 13933, 866, 1667,
|
||||
16109, 14062, 901, 1613, 15998, 14191, 937, 1560, 15885, 14318, 975, 1508, 15772,
|
||||
14445, 1013, 1457, 15657, 14571, 1052, 1407, 15540, 14695, 1093, 1359, 15423, 14819,
|
||||
1134, 1312, 15304, 14942, 1177, 1266, 15185, 15064, 1221, 1221, 15064, 15185, 1266,
|
||||
1177, 14942, 15304, 1312, 1134, 14819, 15423, 1359, 1093, 14695, 15540, 1407, 1052,
|
||||
14571, 15657, 1457, 1013, 14445, 15772, 1508, 975, 14318, 15885, 1560, 937, 14191,
|
||||
15998, 1613, 901, 14062, 16109, 1667, 866, 13933, 16218, 1723, 832, 13803, 16327,
|
||||
1780, 798, 13673, 16434, 1838, 766, 13541, 16539, 1897, 735, 13409, 16643, 1958,
|
||||
704, 13277, 16745, 2020, 675, 13144, 16846, 2083, 647, 13010, 16946, 2147, 619,
|
||||
12876, 17044, 2213, 592, 12741, 17140, 2280, 566, 12606, 17234, 2348, 541, 12470,
|
||||
17327, 2418, 517, 12334, 17418, 2489, 494, 12198, 17507, 2561, 471, 12062, 17595,
|
||||
2635, 449, 11925, 17681, 2709, 428, 11788, 17765, 2785, 408, 11650, 17847, 2863,
|
||||
388, 11513, 17927, 2942, 369, 11375, 18006, 3022, 351, 11238, 18082, 3103, 334,
|
||||
11100, 18157, 3186, 317, 10962, 18230, 3269, 300, 10824, 18300, 3355, 285, 10687,
|
||||
18369, 3441, 270, 10549, 18436, 3529, 255, 10411, 18500, 3618, 241, 10274, 18563,
|
||||
3709, 228, 10137, 18624, 3800, 215, 10000, 18682, 3893, 202, 9863, 18738, 3987,
|
||||
190, 9726, 18793, 4083, 179, 9590, 18845, 4179, 168, 9454, 18895, 4277, 157,
|
||||
9318, 18942, 4377, 147, 9183, 18988, 4477, 137, 9048, 19031, 4579, 127, 8914,
|
||||
19073, 4681, 118, 8780, 19112, 4785, 109, 8646, 19148, 4890, 101, 8513, 19183,
|
||||
4997, 92, 8381, 19215, 5104, 84, 8249, 19245, 5213, 77, 8118, 19273, 5323,
|
||||
69, 7987, 19298, 5434, 62, 7857, 19321, 5546, 55, 7728, 19342, 5659, 48,
|
||||
7600, 19361, 5773, 41, 7472, 19377, 5888, 34, 7345, 19391, 6004, 28, 7219,
|
||||
19403, 6121, 22, 7093, 19412, 6239, 15, 6968, 19419, 6359, 9, 6845, 19424,
|
||||
6479, 3, 6722, 19426, 6600};
|
||||
|
||||
constexpr std::array<s16, 512> curve_lut1{
|
||||
-68, 32639, 69, -5, -200, 32630, 212, -15, -328, 32613, 359, -26, -450,
|
||||
32586, 512, -36, -568, 32551, 669, -47, -680, 32507, 832, -58, -788, 32454,
|
||||
1000, -69, -891, 32393, 1174, -80, -990, 32323, 1352, -92, -1084, 32244, 1536,
|
||||
-103, -1173, 32157, 1724, -115, -1258, 32061, 1919, -128, -1338, 31956, 2118, -140,
|
||||
-1414, 31844, 2322, -153, -1486, 31723, 2532, -167, -1554, 31593, 2747, -180, -1617,
|
||||
31456, 2967, -194, -1676, 31310, 3192, -209, -1732, 31157, 3422, -224, -1783, 30995,
|
||||
3657, -240, -1830, 30826, 3897, -256, -1874, 30649, 4143, -272, -1914, 30464, 4393,
|
||||
-289, -1951, 30272, 4648, -307, -1984, 30072, 4908, -325, -2014, 29866, 5172, -343,
|
||||
-2040, 29652, 5442, -362, -2063, 29431, 5716, -382, -2083, 29203, 5994, -403, -2100,
|
||||
28968, 6277, -424, -2114, 28727, 6565, -445, -2125, 28480, 6857, -468, -2133, 28226,
|
||||
7153, -490, -2139, 27966, 7453, -514, -2142, 27700, 7758, -538, -2142, 27428, 8066,
|
||||
-563, -2141, 27151, 8378, -588, -2136, 26867, 8694, -614, -2130, 26579, 9013, -641,
|
||||
-2121, 26285, 9336, -668, -2111, 25987, 9663, -696, -2098, 25683, 9993, -724, -2084,
|
||||
25375, 10326, -753, -2067, 25063, 10662, -783, -2049, 24746, 11000, -813, -2030, 24425,
|
||||
11342, -844, -2009, 24100, 11686, -875, -1986, 23771, 12033, -907, -1962, 23438, 12382,
|
||||
-939, -1937, 23103, 12733, -972, -1911, 22764, 13086, -1005, -1883, 22422, 13441, -1039,
|
||||
-1855, 22077, 13798, -1072, -1825, 21729, 14156, -1107, -1795, 21380, 14516, -1141, -1764,
|
||||
21027, 14877, -1176, -1732, 20673, 15239, -1211, -1700, 20317, 15602, -1246, -1667, 19959,
|
||||
15965, -1282, -1633, 19600, 16329, -1317, -1599, 19239, 16694, -1353, -1564, 18878, 17058,
|
||||
-1388, -1530, 18515, 17423, -1424, -1495, 18151, 17787, -1459, -1459, 17787, 18151, -1495,
|
||||
-1424, 17423, 18515, -1530, -1388, 17058, 18878, -1564, -1353, 16694, 19239, -1599, -1317,
|
||||
16329, 19600, -1633, -1282, 15965, 19959, -1667, -1246, 15602, 20317, -1700, -1211, 15239,
|
||||
20673, -1732, -1176, 14877, 21027, -1764, -1141, 14516, 21380, -1795, -1107, 14156, 21729,
|
||||
-1825, -1072, 13798, 22077, -1855, -1039, 13441, 22422, -1883, -1005, 13086, 22764, -1911,
|
||||
-972, 12733, 23103, -1937, -939, 12382, 23438, -1962, -907, 12033, 23771, -1986, -875,
|
||||
11686, 24100, -2009, -844, 11342, 24425, -2030, -813, 11000, 24746, -2049, -783, 10662,
|
||||
25063, -2067, -753, 10326, 25375, -2084, -724, 9993, 25683, -2098, -696, 9663, 25987,
|
||||
-2111, -668, 9336, 26285, -2121, -641, 9013, 26579, -2130, -614, 8694, 26867, -2136,
|
||||
-588, 8378, 27151, -2141, -563, 8066, 27428, -2142, -538, 7758, 27700, -2142, -514,
|
||||
7453, 27966, -2139, -490, 7153, 28226, -2133, -468, 6857, 28480, -2125, -445, 6565,
|
||||
28727, -2114, -424, 6277, 28968, -2100, -403, 5994, 29203, -2083, -382, 5716, 29431,
|
||||
-2063, -362, 5442, 29652, -2040, -343, 5172, 29866, -2014, -325, 4908, 30072, -1984,
|
||||
-307, 4648, 30272, -1951, -289, 4393, 30464, -1914, -272, 4143, 30649, -1874, -256,
|
||||
3897, 30826, -1830, -240, 3657, 30995, -1783, -224, 3422, 31157, -1732, -209, 3192,
|
||||
31310, -1676, -194, 2967, 31456, -1617, -180, 2747, 31593, -1554, -167, 2532, 31723,
|
||||
-1486, -153, 2322, 31844, -1414, -140, 2118, 31956, -1338, -128, 1919, 32061, -1258,
|
||||
-115, 1724, 32157, -1173, -103, 1536, 32244, -1084, -92, 1352, 32323, -990, -80,
|
||||
1174, 32393, -891, -69, 1000, 32454, -788, -58, 832, 32507, -680, -47, 669,
|
||||
32551, -568, -36, 512, 32586, -450, -26, 359, 32613, -328, -15, 212, 32630,
|
||||
-200, -5, 69, 32639, -68};
|
||||
|
||||
constexpr std::array<s16, 512> curve_lut2{
|
||||
3195, 26287, 3329, -32, 3064, 26281, 3467, -34, 2936, 26270, 3608, -38, 2811,
|
||||
26253, 3751, -42, 2688, 26230, 3897, -46, 2568, 26202, 4046, -50, 2451, 26169,
|
||||
4199, -54, 2338, 26130, 4354, -58, 2227, 26085, 4512, -63, 2120, 26035, 4673,
|
||||
-67, 2015, 25980, 4837, -72, 1912, 25919, 5004, -76, 1813, 25852, 5174, -81,
|
||||
1716, 25780, 5347, -87, 1622, 25704, 5522, -92, 1531, 25621, 5701, -98, 1442,
|
||||
25533, 5882, -103, 1357, 25440, 6066, -109, 1274, 25342, 6253, -115, 1193, 25239,
|
||||
6442, -121, 1115, 25131, 6635, -127, 1040, 25018, 6830, -133, 967, 24899, 7027,
|
||||
-140, 897, 24776, 7227, -146, 829, 24648, 7430, -153, 764, 24516, 7635, -159,
|
||||
701, 24379, 7842, -166, 641, 24237, 8052, -174, 583, 24091, 8264, -181, 526,
|
||||
23940, 8478, -187, 472, 23785, 8695, -194, 420, 23626, 8914, -202, 371, 23462,
|
||||
9135, -209, 324, 23295, 9358, -215, 279, 23123, 9583, -222, 236, 22948, 9809,
|
||||
-230, 194, 22769, 10038, -237, 154, 22586, 10269, -243, 117, 22399, 10501, -250,
|
||||
81, 22208, 10735, -258, 47, 22015, 10970, -265, 15, 21818, 11206, -271, -16,
|
||||
21618, 11444, -277, -44, 21415, 11684, -283, -71, 21208, 11924, -290, -97, 20999,
|
||||
12166, -296, -121, 20786, 12409, -302, -143, 20571, 12653, -306, -163, 20354, 12898,
|
||||
-311, -183, 20134, 13143, -316, -201, 19911, 13389, -321, -218, 19686, 13635, -325,
|
||||
-234, 19459, 13882, -328, -248, 19230, 14130, -332, -261, 18998, 14377, -335, -273,
|
||||
18765, 14625, -337, -284, 18531, 14873, -339, -294, 18295, 15121, -341, -302, 18057,
|
||||
15369, -341, -310, 17817, 15617, -341, -317, 17577, 15864, -340, -323, 17335, 16111,
|
||||
-340, -328, 17092, 16357, -338, -332, 16848, 16603, -336, -336, 16603, 16848, -332,
|
||||
-338, 16357, 17092, -328, -340, 16111, 17335, -323, -340, 15864, 17577, -317, -341,
|
||||
15617, 17817, -310, -341, 15369, 18057, -302, -341, 15121, 18295, -294, -339, 14873,
|
||||
18531, -284, -337, 14625, 18765, -273, -335, 14377, 18998, -261, -332, 14130, 19230,
|
||||
-248, -328, 13882, 19459, -234, -325, 13635, 19686, -218, -321, 13389, 19911, -201,
|
||||
-316, 13143, 20134, -183, -311, 12898, 20354, -163, -306, 12653, 20571, -143, -302,
|
||||
12409, 20786, -121, -296, 12166, 20999, -97, -290, 11924, 21208, -71, -283, 11684,
|
||||
21415, -44, -277, 11444, 21618, -16, -271, 11206, 21818, 15, -265, 10970, 22015,
|
||||
47, -258, 10735, 22208, 81, -250, 10501, 22399, 117, -243, 10269, 22586, 154,
|
||||
-237, 10038, 22769, 194, -230, 9809, 22948, 236, -222, 9583, 23123, 279, -215,
|
||||
9358, 23295, 324, -209, 9135, 23462, 371, -202, 8914, 23626, 420, -194, 8695,
|
||||
23785, 472, -187, 8478, 23940, 526, -181, 8264, 24091, 583, -174, 8052, 24237,
|
||||
641, -166, 7842, 24379, 701, -159, 7635, 24516, 764, -153, 7430, 24648, 829,
|
||||
-146, 7227, 24776, 897, -140, 7027, 24899, 967, -133, 6830, 25018, 1040, -127,
|
||||
6635, 25131, 1115, -121, 6442, 25239, 1193, -115, 6253, 25342, 1274, -109, 6066,
|
||||
25440, 1357, -103, 5882, 25533, 1442, -98, 5701, 25621, 1531, -92, 5522, 25704,
|
||||
1622, -87, 5347, 25780, 1716, -81, 5174, 25852, 1813, -76, 5004, 25919, 1912,
|
||||
-72, 4837, 25980, 2015, -67, 4673, 26035, 2120, -63, 4512, 26085, 2227, -58,
|
||||
4354, 26130, 2338, -54, 4199, 26169, 2451, -50, 4046, 26202, 2568, -46, 3897,
|
||||
26230, 2688, -42, 3751, 26253, 2811, -38, 3608, 26270, 2936, -34, 3467, 26281,
|
||||
3064, -32, 3329, 26287, 3195};
|
||||
|
||||
std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input, double ratio) {
|
||||
if (input.size() < 2)
|
||||
@@ -27,43 +147,51 @@ std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input,
|
||||
|
||||
if (ratio <= 0) {
|
||||
LOG_CRITICAL(Audio, "Nonsensical interpolation ratio {}", ratio);
|
||||
ratio = 1.0;
|
||||
return input;
|
||||
}
|
||||
|
||||
if (ratio != state.current_ratio) {
|
||||
const double cutoff_frequency = std::min(0.5 / ratio, 0.5 * ratio);
|
||||
state.nyquist = CascadingFilter::LowPass(std::clamp(cutoff_frequency, 0.0, 0.4), 3);
|
||||
state.current_ratio = ratio;
|
||||
}
|
||||
state.nyquist.Process(input);
|
||||
const s32 step{static_cast<s32>(ratio * 0x8000)};
|
||||
const std::array<s16, 512>& lut = [step] {
|
||||
if (step > 0xaaaa) {
|
||||
return curve_lut0;
|
||||
}
|
||||
if (step <= 0x8000) {
|
||||
return curve_lut1;
|
||||
}
|
||||
return curve_lut2;
|
||||
}();
|
||||
|
||||
constexpr std::size_t taps = InterpolationState::lanczos_taps;
|
||||
const std::size_t num_frames = input.size() / 2;
|
||||
const std::size_t num_frames{input.size() / 2};
|
||||
|
||||
std::vector<s16> output;
|
||||
output.reserve(static_cast<std::size_t>(input.size() / ratio + 4));
|
||||
output.reserve(static_cast<std::size_t>(input.size() / ratio + InterpolationState::taps));
|
||||
|
||||
double& pos = state.position;
|
||||
auto& h = state.history;
|
||||
for (std::size_t i = 0; i < num_frames; ++i) {
|
||||
std::rotate(h.begin(), h.end() - 1, h.end());
|
||||
h[0][0] = input[i * 2 + 0];
|
||||
h[0][1] = input[i * 2 + 1];
|
||||
for (std::size_t frame{}; frame < num_frames; ++frame) {
|
||||
const std::size_t lut_index{(state.fraction >> 8) * InterpolationState::taps};
|
||||
|
||||
while (pos <= 1.0) {
|
||||
double l = 0.0;
|
||||
double r = 0.0;
|
||||
for (std::size_t j = 0; j < h.size(); j++) {
|
||||
const double lanczos_calc = Lanczos(taps, pos + j - taps + 1);
|
||||
l += lanczos_calc * h[j][0];
|
||||
r += lanczos_calc * h[j][1];
|
||||
}
|
||||
output.emplace_back(static_cast<s16>(std::clamp(l, -32768.0, 32767.0)));
|
||||
output.emplace_back(static_cast<s16>(std::clamp(r, -32768.0, 32767.0)));
|
||||
std::rotate(state.history.begin(), state.history.end() - 1, state.history.end());
|
||||
state.history[0][0] = input[frame * 2 + 0];
|
||||
state.history[0][1] = input[frame * 2 + 1];
|
||||
|
||||
pos += ratio;
|
||||
while (state.position <= 1.0) {
|
||||
const s32 left{state.history[0][0] * lut[lut_index + 0] +
|
||||
state.history[1][0] * lut[lut_index + 1] +
|
||||
state.history[2][0] * lut[lut_index + 2] +
|
||||
state.history[3][0] * lut[lut_index + 3]};
|
||||
const s32 right{state.history[0][1] * lut[lut_index + 0] +
|
||||
state.history[1][1] * lut[lut_index + 1] +
|
||||
state.history[2][1] * lut[lut_index + 2] +
|
||||
state.history[3][1] * lut[lut_index + 3]};
|
||||
const s32 new_offset{state.fraction + step};
|
||||
|
||||
state.fraction = new_offset & 0x7fff;
|
||||
|
||||
output.emplace_back(static_cast<s16>(std::clamp(left >> 15, SHRT_MIN, SHRT_MAX)));
|
||||
output.emplace_back(static_cast<s16>(std::clamp(right >> 15, SHRT_MIN, SHRT_MAX)));
|
||||
|
||||
state.position += ratio;
|
||||
}
|
||||
pos -= 1.0;
|
||||
state.position -= 1.0;
|
||||
}
|
||||
|
||||
return output;
|
||||
|
||||
@@ -6,19 +6,17 @@
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include "audio_core/algorithm/filter.h"
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
struct InterpolationState {
|
||||
static constexpr std::size_t lanczos_taps = 4;
|
||||
static constexpr std::size_t history_size = lanczos_taps * 2 - 1;
|
||||
|
||||
double current_ratio = 0.0;
|
||||
CascadingFilter nyquist;
|
||||
std::array<std::array<s16, 2>, history_size> history = {};
|
||||
double position = 0;
|
||||
static constexpr std::size_t taps{4};
|
||||
static constexpr std::size_t history_size{taps * 2 - 1};
|
||||
std::array<std::array<s16, 2>, history_size> history{};
|
||||
double position{};
|
||||
s32 fraction{};
|
||||
};
|
||||
|
||||
/// Interpolates input signal to produce output signal.
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "audio_core/cubeb_sink.h"
|
||||
#include "audio_core/stream.h"
|
||||
#include "audio_core/time_stretch.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/ring_buffer.h"
|
||||
#include "core/settings.h"
|
||||
@@ -65,12 +66,25 @@ public:
|
||||
void EnqueueSamples(u32 source_num_channels, const std::vector<s16>& samples) override {
|
||||
if (source_num_channels > num_channels) {
|
||||
// Downsample 6 channels to 2
|
||||
ASSERT_MSG(source_num_channels == 6, "Channel count must be 6");
|
||||
|
||||
std::vector<s16> buf;
|
||||
buf.reserve(samples.size() * num_channels / source_num_channels);
|
||||
for (std::size_t i = 0; i < samples.size(); i += source_num_channels) {
|
||||
for (std::size_t ch = 0; ch < num_channels; ch++) {
|
||||
buf.push_back(samples[i + ch]);
|
||||
}
|
||||
// Downmixing implementation taken from the ATSC standard
|
||||
const s16 left{samples[i + 0]};
|
||||
const s16 right{samples[i + 1]};
|
||||
const s16 center{samples[i + 2]};
|
||||
const s16 surround_left{samples[i + 4]};
|
||||
const s16 surround_right{samples[i + 5]};
|
||||
// Not used in the ATSC reference implementation
|
||||
[[maybe_unused]] const s16 low_frequency_effects { samples[i + 3] };
|
||||
|
||||
constexpr s32 clev{707}; // center mixing level coefficient
|
||||
constexpr s32 slev{707}; // surround mixing level coefficient
|
||||
|
||||
buf.push_back(left + (clev * center / 1000) + (slev * surround_left / 1000));
|
||||
buf.push_back(right + (clev * center / 1000) + (slev * surround_right / 1000));
|
||||
}
|
||||
queue.Push(buf);
|
||||
return;
|
||||
|
||||
@@ -38,8 +38,6 @@ add_custom_command(OUTPUT scm_rev.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}/renderer_opengl/gl_shader_gen.cpp"
|
||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.h"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic_half.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/arithmetic_half_immediate.cpp"
|
||||
@@ -72,8 +70,6 @@ add_custom_command(OUTPUT scm_rev.cpp
|
||||
"${VIDEO_CORE}/shader/ast.h"
|
||||
"${VIDEO_CORE}/shader/compiler_settings.cpp"
|
||||
"${VIDEO_CORE}/shader/compiler_settings.h"
|
||||
"${VIDEO_CORE}/shader/const_buffer_locker.cpp"
|
||||
"${VIDEO_CORE}/shader/const_buffer_locker.h"
|
||||
"${VIDEO_CORE}/shader/control_flow.cpp"
|
||||
"${VIDEO_CORE}/shader/control_flow.h"
|
||||
"${VIDEO_CORE}/shader/decode.cpp"
|
||||
@@ -82,9 +78,13 @@ add_custom_command(OUTPUT scm_rev.cpp
|
||||
"${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
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.h"
|
||||
|
||||
@@ -24,17 +24,29 @@ struct Rectangle {
|
||||
: left(left), top(top), right(right), bottom(bottom) {}
|
||||
|
||||
T GetWidth() const {
|
||||
return std::abs(static_cast<std::make_signed_t<T>>(right - left));
|
||||
if constexpr (std::is_floating_point_v<T>) {
|
||||
return std::abs(right - left);
|
||||
} else {
|
||||
return std::abs(static_cast<std::make_signed_t<T>>(right - left));
|
||||
}
|
||||
}
|
||||
|
||||
T GetHeight() const {
|
||||
return std::abs(static_cast<std::make_signed_t<T>>(bottom - top));
|
||||
if constexpr (std::is_floating_point_v<T>) {
|
||||
return std::abs(bottom - top);
|
||||
} else {
|
||||
return std::abs(static_cast<std::make_signed_t<T>>(bottom - top));
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle<T> TranslateX(const T x) const {
|
||||
return Rectangle{left + x, top, right + x, bottom};
|
||||
}
|
||||
|
||||
Rectangle<T> TranslateY(const T y) const {
|
||||
return Rectangle{left, top + y, right, bottom + y};
|
||||
}
|
||||
|
||||
Rectangle<T> Scale(const float s) const {
|
||||
return Rectangle{left, top, static_cast<T>(left + GetWidth() * s),
|
||||
static_cast<T>(top + GetHeight() * s)};
|
||||
|
||||
@@ -16,7 +16,6 @@ void PageTable::Resize(std::size_t address_space_width_in_bits) {
|
||||
|
||||
pointers.resize(num_page_table_entries);
|
||||
attributes.resize(num_page_table_entries);
|
||||
backing_addr.resize(num_page_table_entries);
|
||||
|
||||
// The default is a 39-bit address space, which causes an initial 1GB allocation size. If the
|
||||
// vector size is subsequently decreased (via resize), the vector might not automatically
|
||||
@@ -25,6 +24,17 @@ void PageTable::Resize(std::size_t address_space_width_in_bits) {
|
||||
|
||||
pointers.shrink_to_fit();
|
||||
attributes.shrink_to_fit();
|
||||
}
|
||||
|
||||
BackingPageTable::BackingPageTable(std::size_t page_size_in_bits) : PageTable{page_size_in_bits} {}
|
||||
|
||||
BackingPageTable::~BackingPageTable() = default;
|
||||
|
||||
void BackingPageTable::Resize(std::size_t address_space_width_in_bits) {
|
||||
PageTable::Resize(address_space_width_in_bits);
|
||||
const std::size_t num_page_table_entries = 1ULL
|
||||
<< (address_space_width_in_bits - page_size_in_bits);
|
||||
backing_addr.resize(num_page_table_entries);
|
||||
backing_addr.shrink_to_fit();
|
||||
}
|
||||
|
||||
|
||||
@@ -76,9 +76,20 @@ struct PageTable {
|
||||
*/
|
||||
std::vector<PageType> attributes;
|
||||
|
||||
std::vector<u64> backing_addr;
|
||||
|
||||
const std::size_t page_size_in_bits{};
|
||||
};
|
||||
|
||||
/**
|
||||
* A more advanced Page Table with the ability to save a backing address when using it
|
||||
* depends on another MMU.
|
||||
*/
|
||||
struct BackingPageTable : PageTable {
|
||||
explicit BackingPageTable(std::size_t page_size_in_bits);
|
||||
~BackingPageTable();
|
||||
|
||||
void Resize(std::size_t address_space_width_in_bits);
|
||||
|
||||
std::vector<u64> backing_addr;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -131,8 +131,8 @@ add_library(core STATIC
|
||||
frontend/framebuffer_layout.cpp
|
||||
frontend/framebuffer_layout.h
|
||||
frontend/input.h
|
||||
frontend/scope_acquire_window_context.cpp
|
||||
frontend/scope_acquire_window_context.h
|
||||
frontend/scope_acquire_context.cpp
|
||||
frontend/scope_acquire_context.h
|
||||
gdbstub/gdbstub.cpp
|
||||
gdbstub/gdbstub.h
|
||||
hardware_interrupt_manager.cpp
|
||||
@@ -187,6 +187,8 @@ add_library(core STATIC
|
||||
hle/kernel/synchronization.h
|
||||
hle/kernel/thread.cpp
|
||||
hle/kernel/thread.h
|
||||
hle/kernel/time_manager.cpp
|
||||
hle/kernel/time_manager.h
|
||||
hle/kernel/transfer_memory.cpp
|
||||
hle/kernel/transfer_memory.h
|
||||
hle/kernel/vm_manager.cpp
|
||||
@@ -593,8 +595,12 @@ endif()
|
||||
|
||||
if (ARCHITECTURE_x86_64)
|
||||
target_sources(core PRIVATE
|
||||
arm/dynarmic/arm_dynarmic.cpp
|
||||
arm/dynarmic/arm_dynarmic.h
|
||||
arm/dynarmic/arm_dynarmic_32.cpp
|
||||
arm/dynarmic/arm_dynarmic_32.h
|
||||
arm/dynarmic/arm_dynarmic_64.cpp
|
||||
arm/dynarmic/arm_dynarmic_64.h
|
||||
arm/dynarmic/arm_dynarmic_cp15.cpp
|
||||
arm/dynarmic/arm_dynarmic_cp15.h
|
||||
)
|
||||
target_link_libraries(core PRIVATE dynarmic)
|
||||
endif()
|
||||
|
||||
@@ -25,7 +25,20 @@ public:
|
||||
explicit ARM_Interface(System& system_) : system{system_} {}
|
||||
virtual ~ARM_Interface() = default;
|
||||
|
||||
struct ThreadContext {
|
||||
struct ThreadContext32 {
|
||||
std::array<u32, 16> cpu_registers;
|
||||
u32 cpsr;
|
||||
std::array<u8, 4> padding;
|
||||
std::array<u64, 32> fprs;
|
||||
u32 fpscr;
|
||||
u32 fpexc;
|
||||
u32 tpidr;
|
||||
};
|
||||
// Internally within the kernel, it expects the AArch32 version of the
|
||||
// thread context to be 344 bytes in size.
|
||||
static_assert(sizeof(ThreadContext32) == 0x158);
|
||||
|
||||
struct ThreadContext64 {
|
||||
std::array<u64, 31> cpu_registers;
|
||||
u64 sp;
|
||||
u64 pc;
|
||||
@@ -38,7 +51,7 @@ public:
|
||||
};
|
||||
// Internally within the kernel, it expects the AArch64 version of the
|
||||
// thread context to be 800 bytes in size.
|
||||
static_assert(sizeof(ThreadContext) == 0x320);
|
||||
static_assert(sizeof(ThreadContext64) == 0x320);
|
||||
|
||||
/// Runs the CPU until an event happens
|
||||
virtual void Run() = 0;
|
||||
@@ -130,17 +143,10 @@ public:
|
||||
*/
|
||||
virtual void SetTPIDR_EL0(u64 value) = 0;
|
||||
|
||||
/**
|
||||
* Saves the current CPU context
|
||||
* @param ctx Thread context to save
|
||||
*/
|
||||
virtual void SaveContext(ThreadContext& ctx) = 0;
|
||||
|
||||
/**
|
||||
* Loads a CPU context
|
||||
* @param ctx Thread context to load
|
||||
*/
|
||||
virtual void LoadContext(const ThreadContext& ctx) = 0;
|
||||
virtual void SaveContext(ThreadContext32& ctx) = 0;
|
||||
virtual void SaveContext(ThreadContext64& ctx) = 0;
|
||||
virtual void LoadContext(const ThreadContext32& ctx) = 0;
|
||||
virtual void LoadContext(const ThreadContext64& ctx) = 0;
|
||||
|
||||
/// Clears the exclusive monitor's state.
|
||||
virtual void ClearExclusiveState() = 0;
|
||||
|
||||
208
src/core/arm/dynarmic/arm_dynarmic_32.cpp
Normal file
208
src/core/arm/dynarmic/arm_dynarmic_32.cpp
Normal file
@@ -0,0 +1,208 @@
|
||||
// Copyright 2020 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cinttypes>
|
||||
#include <memory>
|
||||
#include <dynarmic/A32/a32.h>
|
||||
#include <dynarmic/A32/config.h>
|
||||
#include <dynarmic/A32/context.h>
|
||||
#include "common/microprofile.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic_32.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic_64.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic_cp15.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_manager.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
|
||||
public:
|
||||
explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent) : parent(parent) {}
|
||||
|
||||
u8 MemoryRead8(u32 vaddr) override {
|
||||
return parent.system.Memory().Read8(vaddr);
|
||||
}
|
||||
u16 MemoryRead16(u32 vaddr) override {
|
||||
return parent.system.Memory().Read16(vaddr);
|
||||
}
|
||||
u32 MemoryRead32(u32 vaddr) override {
|
||||
return parent.system.Memory().Read32(vaddr);
|
||||
}
|
||||
u64 MemoryRead64(u32 vaddr) override {
|
||||
return parent.system.Memory().Read64(vaddr);
|
||||
}
|
||||
|
||||
void MemoryWrite8(u32 vaddr, u8 value) override {
|
||||
parent.system.Memory().Write8(vaddr, value);
|
||||
}
|
||||
void MemoryWrite16(u32 vaddr, u16 value) override {
|
||||
parent.system.Memory().Write16(vaddr, value);
|
||||
}
|
||||
void MemoryWrite32(u32 vaddr, u32 value) override {
|
||||
parent.system.Memory().Write32(vaddr, value);
|
||||
}
|
||||
void MemoryWrite64(u32 vaddr, u64 value) override {
|
||||
parent.system.Memory().Write64(vaddr, value);
|
||||
}
|
||||
|
||||
void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
|
||||
switch (exception) {
|
||||
case Dynarmic::A32::Exception::UndefinedInstruction:
|
||||
case Dynarmic::A32::Exception::UnpredictableInstruction:
|
||||
break;
|
||||
case Dynarmic::A32::Exception::Breakpoint:
|
||||
break;
|
||||
}
|
||||
LOG_CRITICAL(HW_GPU, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
|
||||
static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void CallSVC(u32 swi) override {
|
||||
Kernel::CallSVC(parent.system, swi);
|
||||
}
|
||||
|
||||
void AddTicks(u64 ticks) override {
|
||||
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
|
||||
// rough approximation of the amount of executed ticks in the system, it may be thrown off
|
||||
// if not all cores are doing a similar amount of work. Instead of doing this, we should
|
||||
// device a way so that timing is consistent across all cores without increasing the ticks 4
|
||||
// times.
|
||||
u64 amortized_ticks = (ticks - num_interpreted_instructions) / Core::NUM_CPU_CORES;
|
||||
// Always execute at least one tick.
|
||||
amortized_ticks = std::max<u64>(amortized_ticks, 1);
|
||||
|
||||
parent.system.CoreTiming().AddTicks(amortized_ticks);
|
||||
num_interpreted_instructions = 0;
|
||||
}
|
||||
u64 GetTicksRemaining() override {
|
||||
return std::max(parent.system.CoreTiming().GetDowncount(), {});
|
||||
}
|
||||
|
||||
ARM_Dynarmic_32& parent;
|
||||
std::size_t num_interpreted_instructions{};
|
||||
u64 tpidrro_el0{};
|
||||
u64 tpidr_el0{};
|
||||
};
|
||||
|
||||
std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable& page_table,
|
||||
std::size_t address_space_bits) const {
|
||||
Dynarmic::A32::UserConfig config;
|
||||
config.callbacks = cb.get();
|
||||
// TODO(bunnei): Implement page table for 32-bit
|
||||
// config.page_table = &page_table.pointers;
|
||||
config.coprocessors[15] = std::make_shared<DynarmicCP15>((u32*)&CP15_regs[0]);
|
||||
config.define_unpredictable_behaviour = true;
|
||||
return std::make_unique<Dynarmic::A32::Jit>(config);
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_32, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64));
|
||||
|
||||
void ARM_Dynarmic_32::Run() {
|
||||
MICROPROFILE_SCOPE(ARM_Jit_Dynarmic_32);
|
||||
jit->Run();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::Step() {
|
||||
cb->InterpreterFallback(jit->Regs()[15], 1);
|
||||
}
|
||||
|
||||
ARM_Dynarmic_32::ARM_Dynarmic_32(System& system, ExclusiveMonitor& exclusive_monitor,
|
||||
std::size_t core_index)
|
||||
: ARM_Interface{system},
|
||||
cb(std::make_unique<DynarmicCallbacks32>(*this)), core_index{core_index},
|
||||
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
|
||||
|
||||
ARM_Dynarmic_32::~ARM_Dynarmic_32() = default;
|
||||
|
||||
void ARM_Dynarmic_32::SetPC(u64 pc) {
|
||||
jit->Regs()[15] = static_cast<u32>(pc);
|
||||
}
|
||||
|
||||
u64 ARM_Dynarmic_32::GetPC() const {
|
||||
return jit->Regs()[15];
|
||||
}
|
||||
|
||||
u64 ARM_Dynarmic_32::GetReg(int index) const {
|
||||
return jit->Regs()[index];
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::SetReg(int index, u64 value) {
|
||||
jit->Regs()[index] = static_cast<u32>(value);
|
||||
}
|
||||
|
||||
u128 ARM_Dynarmic_32::GetVectorReg(int index) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::SetVectorReg(int index, u128 value) {}
|
||||
|
||||
u32 ARM_Dynarmic_32::GetPSTATE() const {
|
||||
return jit->Cpsr();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::SetPSTATE(u32 cpsr) {
|
||||
jit->SetCpsr(cpsr);
|
||||
}
|
||||
|
||||
u64 ARM_Dynarmic_32::GetTlsAddress() const {
|
||||
return CP15_regs[static_cast<std::size_t>(CP15Register::CP15_THREAD_URO)];
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::SetTlsAddress(VAddr address) {
|
||||
CP15_regs[static_cast<std::size_t>(CP15Register::CP15_THREAD_URO)] = static_cast<u32>(address);
|
||||
}
|
||||
|
||||
u64 ARM_Dynarmic_32::GetTPIDR_EL0() const {
|
||||
return cb->tpidr_el0;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) {
|
||||
cb->tpidr_el0 = value;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
|
||||
Dynarmic::A32::Context context;
|
||||
jit->SaveContext(context);
|
||||
ctx.cpu_registers = context.Regs();
|
||||
ctx.cpsr = context.Cpsr();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
|
||||
Dynarmic::A32::Context context;
|
||||
context.Regs() = ctx.cpu_registers;
|
||||
context.SetCpsr(ctx.cpsr);
|
||||
jit->LoadContext(context);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::PrepareReschedule() {
|
||||
jit->HaltExecution();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::ClearInstructionCache() {
|
||||
jit->ClearCache();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::ClearExclusiveState() {}
|
||||
|
||||
void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
|
||||
std::size_t new_address_space_size_in_bits) {
|
||||
auto key = std::make_pair(&page_table, new_address_space_size_in_bits);
|
||||
auto iter = jit_cache.find(key);
|
||||
if (iter != jit_cache.end()) {
|
||||
jit = iter->second;
|
||||
return;
|
||||
}
|
||||
jit = MakeJit(page_table, new_address_space_size_in_bits);
|
||||
jit_cache.emplace(key, jit);
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
77
src/core/arm/dynarmic/arm_dynarmic_32.h
Normal file
77
src/core/arm/dynarmic/arm_dynarmic_32.h
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright 2020 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <dynarmic/A32/a32.h>
|
||||
#include <dynarmic/A64/a64.h>
|
||||
#include <dynarmic/A64/exclusive_monitor.h>
|
||||
#include "common/common_types.h"
|
||||
#include "common/hash.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
|
||||
namespace Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
class DynarmicCallbacks32;
|
||||
class DynarmicExclusiveMonitor;
|
||||
class System;
|
||||
|
||||
class ARM_Dynarmic_32 final : public ARM_Interface {
|
||||
public:
|
||||
ARM_Dynarmic_32(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
|
||||
~ARM_Dynarmic_32() override;
|
||||
|
||||
void SetPC(u64 pc) override;
|
||||
u64 GetPC() const override;
|
||||
u64 GetReg(int index) const override;
|
||||
void SetReg(int index, u64 value) override;
|
||||
u128 GetVectorReg(int index) const override;
|
||||
void SetVectorReg(int index, u128 value) override;
|
||||
u32 GetPSTATE() const override;
|
||||
void SetPSTATE(u32 pstate) override;
|
||||
void Run() override;
|
||||
void Step() override;
|
||||
VAddr GetTlsAddress() const override;
|
||||
void SetTlsAddress(VAddr address) override;
|
||||
void SetTPIDR_EL0(u64 value) override;
|
||||
u64 GetTPIDR_EL0() const override;
|
||||
|
||||
void SaveContext(ThreadContext32& ctx) override;
|
||||
void SaveContext(ThreadContext64& ctx) override {}
|
||||
void LoadContext(const ThreadContext32& ctx) override;
|
||||
void LoadContext(const ThreadContext64& ctx) override {}
|
||||
|
||||
void PrepareReschedule() override;
|
||||
void ClearExclusiveState() override;
|
||||
|
||||
void ClearInstructionCache() override;
|
||||
void PageTableChanged(Common::PageTable& new_page_table,
|
||||
std::size_t new_address_space_size_in_bits) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable& page_table,
|
||||
std::size_t address_space_bits) const;
|
||||
|
||||
using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
|
||||
using JitCacheType =
|
||||
std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A32::Jit>, Common::PairHash>;
|
||||
|
||||
friend class DynarmicCallbacks32;
|
||||
std::unique_ptr<DynarmicCallbacks32> cb;
|
||||
JitCacheType jit_cache;
|
||||
std::shared_ptr<Dynarmic::A32::Jit> jit;
|
||||
std::size_t core_index;
|
||||
DynarmicExclusiveMonitor& exclusive_monitor;
|
||||
std::array<u32, 84> CP15_regs{};
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <dynarmic/A64/config.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic_64.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_manager.h"
|
||||
#include "core/core_timing.h"
|
||||
@@ -25,9 +25,9 @@ namespace Core {
|
||||
|
||||
using Vector = Dynarmic::A64::Vector;
|
||||
|
||||
class ARM_Dynarmic_Callbacks : public Dynarmic::A64::UserCallbacks {
|
||||
class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
|
||||
public:
|
||||
explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {}
|
||||
explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent) : parent(parent) {}
|
||||
|
||||
u8 MemoryRead8(u64 vaddr) override {
|
||||
return parent.system.Memory().Read8(vaddr);
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc,
|
||||
num_instructions, MemoryReadCode(pc));
|
||||
|
||||
ARM_Interface::ThreadContext ctx;
|
||||
ARM_Interface::ThreadContext64 ctx;
|
||||
parent.SaveContext(ctx);
|
||||
parent.inner_unicorn.LoadContext(ctx);
|
||||
parent.inner_unicorn.ExecuteInstructions(num_instructions);
|
||||
@@ -90,7 +90,7 @@ public:
|
||||
parent.jit->HaltExecution();
|
||||
parent.SetPC(pc);
|
||||
Kernel::Thread* const thread = parent.system.CurrentScheduler().GetCurrentThread();
|
||||
parent.SaveContext(thread->GetContext());
|
||||
parent.SaveContext(thread->GetContext64());
|
||||
GDBStub::Break();
|
||||
GDBStub::SendTrap(thread, 5);
|
||||
return;
|
||||
@@ -126,14 +126,14 @@ public:
|
||||
return Timing::CpuCyclesToClockCycles(parent.system.CoreTiming().GetTicks());
|
||||
}
|
||||
|
||||
ARM_Dynarmic& parent;
|
||||
ARM_Dynarmic_64& parent;
|
||||
std::size_t num_interpreted_instructions = 0;
|
||||
u64 tpidrro_el0 = 0;
|
||||
u64 tpidr_el0 = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit(Common::PageTable& page_table,
|
||||
std::size_t address_space_bits) const {
|
||||
std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable& page_table,
|
||||
std::size_t address_space_bits) const {
|
||||
Dynarmic::A64::UserConfig config;
|
||||
|
||||
// Callbacks
|
||||
@@ -159,79 +159,79 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit(Common::PageTable& pag
|
||||
// Unpredictable instructions
|
||||
config.define_unpredictable_behaviour = true;
|
||||
|
||||
return std::make_unique<Dynarmic::A64::Jit>(config);
|
||||
return std::make_shared<Dynarmic::A64::Jit>(config);
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(ARM_Jit_Dynarmic, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64));
|
||||
MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_64, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64));
|
||||
|
||||
void ARM_Dynarmic::Run() {
|
||||
MICROPROFILE_SCOPE(ARM_Jit_Dynarmic);
|
||||
void ARM_Dynarmic_64::Run() {
|
||||
MICROPROFILE_SCOPE(ARM_Jit_Dynarmic_64);
|
||||
|
||||
jit->Run();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::Step() {
|
||||
void ARM_Dynarmic_64::Step() {
|
||||
cb->InterpreterFallback(jit->GetPC(), 1);
|
||||
}
|
||||
|
||||
ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor,
|
||||
std::size_t core_index)
|
||||
ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, ExclusiveMonitor& exclusive_monitor,
|
||||
std::size_t core_index)
|
||||
: ARM_Interface{system},
|
||||
cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system},
|
||||
cb(std::make_unique<DynarmicCallbacks64>(*this)), inner_unicorn{system},
|
||||
core_index{core_index}, exclusive_monitor{
|
||||
dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
|
||||
|
||||
ARM_Dynarmic::~ARM_Dynarmic() = default;
|
||||
ARM_Dynarmic_64::~ARM_Dynarmic_64() = default;
|
||||
|
||||
void ARM_Dynarmic::SetPC(u64 pc) {
|
||||
void ARM_Dynarmic_64::SetPC(u64 pc) {
|
||||
jit->SetPC(pc);
|
||||
}
|
||||
|
||||
u64 ARM_Dynarmic::GetPC() const {
|
||||
u64 ARM_Dynarmic_64::GetPC() const {
|
||||
return jit->GetPC();
|
||||
}
|
||||
|
||||
u64 ARM_Dynarmic::GetReg(int index) const {
|
||||
u64 ARM_Dynarmic_64::GetReg(int index) const {
|
||||
return jit->GetRegister(index);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SetReg(int index, u64 value) {
|
||||
void ARM_Dynarmic_64::SetReg(int index, u64 value) {
|
||||
jit->SetRegister(index, value);
|
||||
}
|
||||
|
||||
u128 ARM_Dynarmic::GetVectorReg(int index) const {
|
||||
u128 ARM_Dynarmic_64::GetVectorReg(int index) const {
|
||||
return jit->GetVector(index);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SetVectorReg(int index, u128 value) {
|
||||
void ARM_Dynarmic_64::SetVectorReg(int index, u128 value) {
|
||||
jit->SetVector(index, value);
|
||||
}
|
||||
|
||||
u32 ARM_Dynarmic::GetPSTATE() const {
|
||||
u32 ARM_Dynarmic_64::GetPSTATE() const {
|
||||
return jit->GetPstate();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SetPSTATE(u32 pstate) {
|
||||
void ARM_Dynarmic_64::SetPSTATE(u32 pstate) {
|
||||
jit->SetPstate(pstate);
|
||||
}
|
||||
|
||||
u64 ARM_Dynarmic::GetTlsAddress() const {
|
||||
u64 ARM_Dynarmic_64::GetTlsAddress() const {
|
||||
return cb->tpidrro_el0;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SetTlsAddress(VAddr address) {
|
||||
void ARM_Dynarmic_64::SetTlsAddress(VAddr address) {
|
||||
cb->tpidrro_el0 = address;
|
||||
}
|
||||
|
||||
u64 ARM_Dynarmic::GetTPIDR_EL0() const {
|
||||
u64 ARM_Dynarmic_64::GetTPIDR_EL0() const {
|
||||
return cb->tpidr_el0;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SetTPIDR_EL0(u64 value) {
|
||||
void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) {
|
||||
cb->tpidr_el0 = value;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SaveContext(ThreadContext& ctx) {
|
||||
void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
|
||||
ctx.cpu_registers = jit->GetRegisters();
|
||||
ctx.sp = jit->GetSP();
|
||||
ctx.pc = jit->GetPC();
|
||||
@@ -242,7 +242,7 @@ void ARM_Dynarmic::SaveContext(ThreadContext& ctx) {
|
||||
ctx.tpidr = cb->tpidr_el0;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) {
|
||||
void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) {
|
||||
jit->SetRegisters(ctx.cpu_registers);
|
||||
jit->SetSP(ctx.sp);
|
||||
jit->SetPC(ctx.pc);
|
||||
@@ -253,25 +253,32 @@ void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) {
|
||||
SetTPIDR_EL0(ctx.tpidr);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::PrepareReschedule() {
|
||||
void ARM_Dynarmic_64::PrepareReschedule() {
|
||||
jit->HaltExecution();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::ClearInstructionCache() {
|
||||
void ARM_Dynarmic_64::ClearInstructionCache() {
|
||||
jit->ClearCache();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::ClearExclusiveState() {
|
||||
void ARM_Dynarmic_64::ClearExclusiveState() {
|
||||
jit->ClearExclusiveState();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::PageTableChanged(Common::PageTable& page_table,
|
||||
std::size_t new_address_space_size_in_bits) {
|
||||
void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
|
||||
std::size_t new_address_space_size_in_bits) {
|
||||
auto key = std::make_pair(&page_table, new_address_space_size_in_bits);
|
||||
auto iter = jit_cache.find(key);
|
||||
if (iter != jit_cache.end()) {
|
||||
jit = iter->second;
|
||||
return;
|
||||
}
|
||||
jit = MakeJit(page_table, new_address_space_size_in_bits);
|
||||
jit_cache.emplace(key, jit);
|
||||
}
|
||||
|
||||
DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count)
|
||||
: monitor(core_count), memory{memory_} {}
|
||||
DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count)
|
||||
: monitor(core_count), memory{memory} {}
|
||||
|
||||
DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default;
|
||||
|
||||
@@ -5,9 +5,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <dynarmic/A64/a64.h>
|
||||
#include <dynarmic/A64/exclusive_monitor.h>
|
||||
#include "common/common_types.h"
|
||||
#include "common/hash.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/arm/unicorn/arm_unicorn.h"
|
||||
@@ -18,14 +21,14 @@ class Memory;
|
||||
|
||||
namespace Core {
|
||||
|
||||
class ARM_Dynarmic_Callbacks;
|
||||
class DynarmicCallbacks64;
|
||||
class DynarmicExclusiveMonitor;
|
||||
class System;
|
||||
|
||||
class ARM_Dynarmic final : public ARM_Interface {
|
||||
class ARM_Dynarmic_64 final : public ARM_Interface {
|
||||
public:
|
||||
ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
|
||||
~ARM_Dynarmic() override;
|
||||
ARM_Dynarmic_64(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
|
||||
~ARM_Dynarmic_64() override;
|
||||
|
||||
void SetPC(u64 pc) override;
|
||||
u64 GetPC() const override;
|
||||
@@ -42,8 +45,10 @@ public:
|
||||
void SetTPIDR_EL0(u64 value) override;
|
||||
u64 GetTPIDR_EL0() const override;
|
||||
|
||||
void SaveContext(ThreadContext& ctx) override;
|
||||
void LoadContext(const ThreadContext& ctx) override;
|
||||
void SaveContext(ThreadContext32& ctx) override {}
|
||||
void SaveContext(ThreadContext64& ctx) override;
|
||||
void LoadContext(const ThreadContext32& ctx) override {}
|
||||
void LoadContext(const ThreadContext64& ctx) override;
|
||||
|
||||
void PrepareReschedule() override;
|
||||
void ClearExclusiveState() override;
|
||||
@@ -53,12 +58,17 @@ public:
|
||||
std::size_t new_address_space_size_in_bits) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable& page_table,
|
||||
std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable& page_table,
|
||||
std::size_t address_space_bits) const;
|
||||
|
||||
friend class ARM_Dynarmic_Callbacks;
|
||||
std::unique_ptr<ARM_Dynarmic_Callbacks> cb;
|
||||
std::unique_ptr<Dynarmic::A64::Jit> jit;
|
||||
using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
|
||||
using JitCacheType =
|
||||
std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A64::Jit>, Common::PairHash>;
|
||||
|
||||
friend class DynarmicCallbacks64;
|
||||
std::unique_ptr<DynarmicCallbacks64> cb;
|
||||
JitCacheType jit_cache;
|
||||
std::shared_ptr<Dynarmic::A64::Jit> jit;
|
||||
ARM_Unicorn inner_unicorn;
|
||||
|
||||
std::size_t core_index;
|
||||
@@ -67,7 +77,7 @@ private:
|
||||
|
||||
class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
|
||||
public:
|
||||
explicit DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count);
|
||||
explicit DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count);
|
||||
~DynarmicExclusiveMonitor() override;
|
||||
|
||||
void SetExclusive(std::size_t core_index, VAddr addr) override;
|
||||
@@ -80,7 +90,7 @@ public:
|
||||
bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) override;
|
||||
|
||||
private:
|
||||
friend class ARM_Dynarmic;
|
||||
friend class ARM_Dynarmic_64;
|
||||
Dynarmic::A64::ExclusiveMonitor monitor;
|
||||
Memory::Memory& memory;
|
||||
};
|
||||
80
src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
Normal file
80
src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/arm/dynarmic/arm_dynarmic_cp15.h"
|
||||
|
||||
using Callback = Dynarmic::A32::Coprocessor::Callback;
|
||||
using CallbackOrAccessOneWord = Dynarmic::A32::Coprocessor::CallbackOrAccessOneWord;
|
||||
using CallbackOrAccessTwoWords = Dynarmic::A32::Coprocessor::CallbackOrAccessTwoWords;
|
||||
|
||||
std::optional<Callback> DynarmicCP15::CompileInternalOperation(bool two, unsigned opc1,
|
||||
CoprocReg CRd, CoprocReg CRn,
|
||||
CoprocReg CRm, unsigned opc2) {
|
||||
return {};
|
||||
}
|
||||
|
||||
CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn,
|
||||
CoprocReg CRm, unsigned opc2) {
|
||||
// TODO(merry): Privileged CP15 registers
|
||||
|
||||
if (!two && CRn == CoprocReg::C7 && opc1 == 0 && CRm == CoprocReg::C5 && opc2 == 4) {
|
||||
// This is a dummy write, we ignore the value written here.
|
||||
return &CP15[static_cast<std::size_t>(CP15Register::CP15_FLUSH_PREFETCH_BUFFER)];
|
||||
}
|
||||
|
||||
if (!two && CRn == CoprocReg::C7 && opc1 == 0 && CRm == CoprocReg::C10) {
|
||||
switch (opc2) {
|
||||
case 4:
|
||||
// This is a dummy write, we ignore the value written here.
|
||||
return &CP15[static_cast<std::size_t>(CP15Register::CP15_DATA_SYNC_BARRIER)];
|
||||
case 5:
|
||||
// This is a dummy write, we ignore the value written here.
|
||||
return &CP15[static_cast<std::size_t>(CP15Register::CP15_DATA_MEMORY_BARRIER)];
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (!two && CRn == CoprocReg::C13 && opc1 == 0 && CRm == CoprocReg::C0 && opc2 == 2) {
|
||||
return &CP15[static_cast<std::size_t>(CP15Register::CP15_THREAD_UPRW)];
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
CallbackOrAccessTwoWords DynarmicCP15::CompileSendTwoWords(bool two, unsigned opc, CoprocReg CRm) {
|
||||
return {};
|
||||
}
|
||||
|
||||
CallbackOrAccessOneWord DynarmicCP15::CompileGetOneWord(bool two, unsigned opc1, CoprocReg CRn,
|
||||
CoprocReg CRm, unsigned opc2) {
|
||||
// TODO(merry): Privileged CP15 registers
|
||||
|
||||
if (!two && CRn == CoprocReg::C13 && opc1 == 0 && CRm == CoprocReg::C0) {
|
||||
switch (opc2) {
|
||||
case 2:
|
||||
return &CP15[static_cast<std::size_t>(CP15Register::CP15_THREAD_UPRW)];
|
||||
case 3:
|
||||
return &CP15[static_cast<std::size_t>(CP15Register::CP15_THREAD_URO)];
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<Callback> DynarmicCP15::CompileLoadWords(bool two, bool long_transfer, CoprocReg CRd,
|
||||
std::optional<u8> option) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd,
|
||||
std::optional<u8> option) {
|
||||
return {};
|
||||
}
|
||||
152
src/core/arm/dynarmic/arm_dynarmic_cp15.h
Normal file
152
src/core/arm/dynarmic/arm_dynarmic_cp15.h
Normal file
@@ -0,0 +1,152 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include <dynarmic/A32/coprocessor.h>
|
||||
#include "common/common_types.h"
|
||||
|
||||
enum class CP15Register {
|
||||
// c0 - Information registers
|
||||
CP15_MAIN_ID,
|
||||
CP15_CACHE_TYPE,
|
||||
CP15_TCM_STATUS,
|
||||
CP15_TLB_TYPE,
|
||||
CP15_CPU_ID,
|
||||
CP15_PROCESSOR_FEATURE_0,
|
||||
CP15_PROCESSOR_FEATURE_1,
|
||||
CP15_DEBUG_FEATURE_0,
|
||||
CP15_AUXILIARY_FEATURE_0,
|
||||
CP15_MEMORY_MODEL_FEATURE_0,
|
||||
CP15_MEMORY_MODEL_FEATURE_1,
|
||||
CP15_MEMORY_MODEL_FEATURE_2,
|
||||
CP15_MEMORY_MODEL_FEATURE_3,
|
||||
CP15_ISA_FEATURE_0,
|
||||
CP15_ISA_FEATURE_1,
|
||||
CP15_ISA_FEATURE_2,
|
||||
CP15_ISA_FEATURE_3,
|
||||
CP15_ISA_FEATURE_4,
|
||||
|
||||
// c1 - Control registers
|
||||
CP15_CONTROL,
|
||||
CP15_AUXILIARY_CONTROL,
|
||||
CP15_COPROCESSOR_ACCESS_CONTROL,
|
||||
|
||||
// c2 - Translation table registers
|
||||
CP15_TRANSLATION_BASE_TABLE_0,
|
||||
CP15_TRANSLATION_BASE_TABLE_1,
|
||||
CP15_TRANSLATION_BASE_CONTROL,
|
||||
CP15_DOMAIN_ACCESS_CONTROL,
|
||||
CP15_RESERVED,
|
||||
|
||||
// c5 - Fault status registers
|
||||
CP15_FAULT_STATUS,
|
||||
CP15_INSTR_FAULT_STATUS,
|
||||
CP15_COMBINED_DATA_FSR = CP15_FAULT_STATUS,
|
||||
CP15_INST_FSR,
|
||||
|
||||
// c6 - Fault Address registers
|
||||
CP15_FAULT_ADDRESS,
|
||||
CP15_COMBINED_DATA_FAR = CP15_FAULT_ADDRESS,
|
||||
CP15_WFAR,
|
||||
CP15_IFAR,
|
||||
|
||||
// c7 - Cache operation registers
|
||||
CP15_WAIT_FOR_INTERRUPT,
|
||||
CP15_PHYS_ADDRESS,
|
||||
CP15_INVALIDATE_INSTR_CACHE,
|
||||
CP15_INVALIDATE_INSTR_CACHE_USING_MVA,
|
||||
CP15_INVALIDATE_INSTR_CACHE_USING_INDEX,
|
||||
CP15_FLUSH_PREFETCH_BUFFER,
|
||||
CP15_FLUSH_BRANCH_TARGET_CACHE,
|
||||
CP15_FLUSH_BRANCH_TARGET_CACHE_ENTRY,
|
||||
CP15_INVALIDATE_DATA_CACHE,
|
||||
CP15_INVALIDATE_DATA_CACHE_LINE_USING_MVA,
|
||||
CP15_INVALIDATE_DATA_CACHE_LINE_USING_INDEX,
|
||||
CP15_INVALIDATE_DATA_AND_INSTR_CACHE,
|
||||
CP15_CLEAN_DATA_CACHE,
|
||||
CP15_CLEAN_DATA_CACHE_LINE_USING_MVA,
|
||||
CP15_CLEAN_DATA_CACHE_LINE_USING_INDEX,
|
||||
CP15_DATA_SYNC_BARRIER,
|
||||
CP15_DATA_MEMORY_BARRIER,
|
||||
CP15_CLEAN_AND_INVALIDATE_DATA_CACHE,
|
||||
CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_MVA,
|
||||
CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_INDEX,
|
||||
|
||||
// c8 - TLB operations
|
||||
CP15_INVALIDATE_ITLB,
|
||||
CP15_INVALIDATE_ITLB_SINGLE_ENTRY,
|
||||
CP15_INVALIDATE_ITLB_ENTRY_ON_ASID_MATCH,
|
||||
CP15_INVALIDATE_ITLB_ENTRY_ON_MVA,
|
||||
CP15_INVALIDATE_DTLB,
|
||||
CP15_INVALIDATE_DTLB_SINGLE_ENTRY,
|
||||
CP15_INVALIDATE_DTLB_ENTRY_ON_ASID_MATCH,
|
||||
CP15_INVALIDATE_DTLB_ENTRY_ON_MVA,
|
||||
CP15_INVALIDATE_UTLB,
|
||||
CP15_INVALIDATE_UTLB_SINGLE_ENTRY,
|
||||
CP15_INVALIDATE_UTLB_ENTRY_ON_ASID_MATCH,
|
||||
CP15_INVALIDATE_UTLB_ENTRY_ON_MVA,
|
||||
|
||||
// c9 - Data cache lockdown register
|
||||
CP15_DATA_CACHE_LOCKDOWN,
|
||||
|
||||
// c10 - TLB/Memory map registers
|
||||
CP15_TLB_LOCKDOWN,
|
||||
CP15_PRIMARY_REGION_REMAP,
|
||||
CP15_NORMAL_REGION_REMAP,
|
||||
|
||||
// c13 - Thread related registers
|
||||
CP15_PID,
|
||||
CP15_CONTEXT_ID,
|
||||
CP15_THREAD_UPRW, // Thread ID register - User/Privileged Read/Write
|
||||
CP15_THREAD_URO, // Thread ID register - User Read Only (Privileged R/W)
|
||||
CP15_THREAD_PRW, // Thread ID register - Privileged R/W only.
|
||||
|
||||
// c15 - Performance and TLB lockdown registers
|
||||
CP15_PERFORMANCE_MONITOR_CONTROL,
|
||||
CP15_CYCLE_COUNTER,
|
||||
CP15_COUNT_0,
|
||||
CP15_COUNT_1,
|
||||
CP15_READ_MAIN_TLB_LOCKDOWN_ENTRY,
|
||||
CP15_WRITE_MAIN_TLB_LOCKDOWN_ENTRY,
|
||||
CP15_MAIN_TLB_LOCKDOWN_VIRT_ADDRESS,
|
||||
CP15_MAIN_TLB_LOCKDOWN_PHYS_ADDRESS,
|
||||
CP15_MAIN_TLB_LOCKDOWN_ATTRIBUTE,
|
||||
CP15_TLB_DEBUG_CONTROL,
|
||||
|
||||
// Skyeye defined
|
||||
CP15_TLB_FAULT_ADDR,
|
||||
CP15_TLB_FAULT_STATUS,
|
||||
|
||||
// Not an actual register.
|
||||
// All registers should be defined above this.
|
||||
CP15_REGISTER_COUNT,
|
||||
};
|
||||
|
||||
class DynarmicCP15 final : public Dynarmic::A32::Coprocessor {
|
||||
public:
|
||||
using CoprocReg = Dynarmic::A32::CoprocReg;
|
||||
|
||||
explicit DynarmicCP15(u32* cp15) : CP15(cp15){};
|
||||
|
||||
std::optional<Callback> CompileInternalOperation(bool two, unsigned opc1, CoprocReg CRd,
|
||||
CoprocReg CRn, CoprocReg CRm,
|
||||
unsigned opc2) override;
|
||||
CallbackOrAccessOneWord CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn,
|
||||
CoprocReg CRm, unsigned opc2) override;
|
||||
CallbackOrAccessTwoWords CompileSendTwoWords(bool two, unsigned opc, CoprocReg CRm) override;
|
||||
CallbackOrAccessOneWord CompileGetOneWord(bool two, unsigned opc1, CoprocReg CRn, CoprocReg CRm,
|
||||
unsigned opc2) override;
|
||||
CallbackOrAccessTwoWords CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) override;
|
||||
std::optional<Callback> CompileLoadWords(bool two, bool long_transfer, CoprocReg CRd,
|
||||
std::optional<u8> option) override;
|
||||
std::optional<Callback> CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd,
|
||||
std::optional<u8> option) override;
|
||||
|
||||
private:
|
||||
u32* CP15{};
|
||||
};
|
||||
@@ -3,7 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "core/arm/dynarmic/arm_dynarmic.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic_64.h"
|
||||
#endif
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
@@ -53,7 +53,7 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si
|
||||
void* user_data) {
|
||||
auto* const system = static_cast<System*>(user_data);
|
||||
|
||||
ARM_Interface::ThreadContext ctx{};
|
||||
ARM_Interface::ThreadContext64 ctx{};
|
||||
system->CurrentArmInterface().SaveContext(ctx);
|
||||
ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x{:X}, pc=0x{:X}, lr=0x{:X}", addr,
|
||||
ctx.pc, ctx.cpu_registers[30]);
|
||||
@@ -179,7 +179,7 @@ void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) {
|
||||
}
|
||||
|
||||
Kernel::Thread* const thread = system.CurrentScheduler().GetCurrentThread();
|
||||
SaveContext(thread->GetContext());
|
||||
SaveContext(thread->GetContext64());
|
||||
if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) {
|
||||
last_bkpt_hit = false;
|
||||
GDBStub::Break();
|
||||
@@ -188,7 +188,7 @@ void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) {
|
||||
}
|
||||
}
|
||||
|
||||
void ARM_Unicorn::SaveContext(ThreadContext& ctx) {
|
||||
void ARM_Unicorn::SaveContext(ThreadContext64& ctx) {
|
||||
int uregs[32];
|
||||
void* tregs[32];
|
||||
|
||||
@@ -215,7 +215,7 @@ void ARM_Unicorn::SaveContext(ThreadContext& ctx) {
|
||||
CHECKED(uc_reg_read_batch(uc, uregs, tregs, 32));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::LoadContext(const ThreadContext& ctx) {
|
||||
void ARM_Unicorn::LoadContext(const ThreadContext64& ctx) {
|
||||
int uregs[32];
|
||||
void* tregs[32];
|
||||
|
||||
|
||||
@@ -30,8 +30,6 @@ public:
|
||||
void SetTlsAddress(VAddr address) override;
|
||||
void SetTPIDR_EL0(u64 value) override;
|
||||
u64 GetTPIDR_EL0() const override;
|
||||
void SaveContext(ThreadContext& ctx) override;
|
||||
void LoadContext(const ThreadContext& ctx) override;
|
||||
void PrepareReschedule() override;
|
||||
void ClearExclusiveState() override;
|
||||
void ExecuteInstructions(std::size_t num_instructions);
|
||||
@@ -41,6 +39,11 @@ public:
|
||||
void PageTableChanged(Common::PageTable&, std::size_t) override {}
|
||||
void RecordBreak(GDBStub::BreakpointAddress bkpt);
|
||||
|
||||
void SaveContext(ThreadContext32& ctx) override {}
|
||||
void SaveContext(ThreadContext64& ctx) override;
|
||||
void LoadContext(const ThreadContext32& ctx) override {}
|
||||
void LoadContext(const ThreadContext64& ctx) override;
|
||||
|
||||
private:
|
||||
static void InterruptHook(uc_engine* uc, u32 int_no, void* user_data);
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "core/file_sys/sdmc_factory.h"
|
||||
#include "core/file_sys/vfs_concat.h"
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
#include "core/frontend/scope_acquire_context.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hardware_interrupt_manager.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
@@ -165,7 +166,7 @@ struct System::Impl {
|
||||
service_manager = std::make_shared<Service::SM::ServiceManager>();
|
||||
|
||||
Service::Init(service_manager, system);
|
||||
GDBStub::Init();
|
||||
GDBStub::DeferStart();
|
||||
|
||||
renderer = VideoCore::CreateRenderer(emu_window, system);
|
||||
if (!renderer->Init()) {
|
||||
@@ -173,6 +174,7 @@ struct System::Impl {
|
||||
}
|
||||
interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
|
||||
gpu_core = VideoCore::CreateGPU(system);
|
||||
renderer->Rasterizer().SetupDirtyFlags();
|
||||
|
||||
is_powered_on = true;
|
||||
exit_lock = false;
|
||||
@@ -184,6 +186,8 @@ struct System::Impl {
|
||||
|
||||
ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
|
||||
const std::string& filepath) {
|
||||
Core::Frontend::ScopeAcquireContext acquire_context{emu_window};
|
||||
|
||||
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
|
||||
if (!app_loader) {
|
||||
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
|
||||
@@ -707,4 +711,12 @@ const Service::SM::ServiceManager& System::ServiceManager() const {
|
||||
return *impl->service_manager;
|
||||
}
|
||||
|
||||
void System::RegisterCoreThread(std::size_t id) {
|
||||
impl->kernel.RegisterCoreThread(id);
|
||||
}
|
||||
|
||||
void System::RegisterHostThread() {
|
||||
impl->kernel.RegisterHostThread();
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -360,6 +360,12 @@ public:
|
||||
|
||||
const CurrentBuildProcessID& GetCurrentProcessBuildID() const;
|
||||
|
||||
/// Register a host thread as an emulated CPU Core.
|
||||
void RegisterCoreThread(std::size_t id);
|
||||
|
||||
/// Register a host thread as an auxiliary thread.
|
||||
void RegisterHostThread();
|
||||
|
||||
private:
|
||||
System();
|
||||
|
||||
|
||||
@@ -6,9 +6,6 @@
|
||||
#include <mutex>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "core/arm/dynarmic/arm_dynarmic.h"
|
||||
#endif
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/arm/unicorn/arm_unicorn.h"
|
||||
#include "core/core.h"
|
||||
|
||||
@@ -26,9 +26,6 @@ public:
|
||||
|
||||
/// Releases (dunno if this is the "right" word) the context from the caller thread
|
||||
virtual void DoneCurrent() = 0;
|
||||
|
||||
/// Swap buffers to display the next frame
|
||||
virtual void SwapBuffers() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -48,8 +48,8 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) {
|
||||
u32 width, height;
|
||||
|
||||
if (Settings::values.use_docked_mode) {
|
||||
width = ScreenDocked::WidthDocked * res_scale;
|
||||
height = ScreenDocked::HeightDocked * res_scale;
|
||||
width = ScreenDocked::Width * res_scale;
|
||||
height = ScreenDocked::Height * res_scale;
|
||||
} else {
|
||||
width = ScreenUndocked::Width * res_scale;
|
||||
height = ScreenUndocked::Height * res_scale;
|
||||
|
||||
@@ -8,15 +8,15 @@
|
||||
|
||||
namespace Layout {
|
||||
|
||||
enum ScreenUndocked : u32 {
|
||||
Width = 1280,
|
||||
Height = 720,
|
||||
};
|
||||
namespace ScreenUndocked {
|
||||
constexpr u32 Width = 1280;
|
||||
constexpr u32 Height = 720;
|
||||
} // namespace ScreenUndocked
|
||||
|
||||
enum ScreenDocked : u32 {
|
||||
WidthDocked = 1920,
|
||||
HeightDocked = 1080,
|
||||
};
|
||||
namespace ScreenDocked {
|
||||
constexpr u32 Width = 1920;
|
||||
constexpr u32 Height = 1080;
|
||||
} // namespace ScreenDocked
|
||||
|
||||
enum class AspectRatio {
|
||||
Default,
|
||||
@@ -29,6 +29,7 @@ enum class AspectRatio {
|
||||
struct FramebufferLayout {
|
||||
u32 width{ScreenUndocked::Width};
|
||||
u32 height{ScreenUndocked::Height};
|
||||
bool is_srgb{};
|
||||
|
||||
Common::Rectangle<u32> screen;
|
||||
|
||||
|
||||
18
src/core/frontend/scope_acquire_context.cpp
Normal file
18
src/core/frontend/scope_acquire_context.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/frontend/scope_acquire_context.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
ScopeAcquireContext::ScopeAcquireContext(Core::Frontend::GraphicsContext& context)
|
||||
: context{context} {
|
||||
context.MakeCurrent();
|
||||
}
|
||||
ScopeAcquireContext::~ScopeAcquireContext() {
|
||||
context.DoneCurrent();
|
||||
}
|
||||
|
||||
} // namespace Core::Frontend
|
||||
@@ -8,16 +8,16 @@
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
class EmuWindow;
|
||||
class GraphicsContext;
|
||||
|
||||
/// Helper class to acquire/release window context within a given scope
|
||||
class ScopeAcquireWindowContext : NonCopyable {
|
||||
class ScopeAcquireContext : NonCopyable {
|
||||
public:
|
||||
explicit ScopeAcquireWindowContext(Core::Frontend::EmuWindow& window);
|
||||
~ScopeAcquireWindowContext();
|
||||
explicit ScopeAcquireContext(Core::Frontend::GraphicsContext& context);
|
||||
~ScopeAcquireContext();
|
||||
|
||||
private:
|
||||
Core::Frontend::EmuWindow& emu_window;
|
||||
Core::Frontend::GraphicsContext& context;
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
||||
@@ -1,18 +0,0 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/frontend/scope_acquire_window_context.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
ScopeAcquireWindowContext::ScopeAcquireWindowContext(Core::Frontend::EmuWindow& emu_window_)
|
||||
: emu_window{emu_window_} {
|
||||
emu_window.MakeCurrent();
|
||||
}
|
||||
ScopeAcquireWindowContext::~ScopeAcquireWindowContext() {
|
||||
emu_window.DoneCurrent();
|
||||
}
|
||||
|
||||
} // namespace Core::Frontend
|
||||
@@ -141,6 +141,7 @@ constexpr char target_xml[] =
|
||||
)";
|
||||
|
||||
int gdbserver_socket = -1;
|
||||
bool defer_start = false;
|
||||
|
||||
u8 command_buffer[GDB_BUFFER_SIZE];
|
||||
u32 command_length;
|
||||
@@ -217,7 +218,7 @@ static u64 RegRead(std::size_t id, Kernel::Thread* thread = nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto& thread_context = thread->GetContext();
|
||||
const auto& thread_context = thread->GetContext64();
|
||||
|
||||
if (id < SP_REGISTER) {
|
||||
return thread_context.cpu_registers[id];
|
||||
@@ -239,7 +240,7 @@ static void RegWrite(std::size_t id, u64 val, Kernel::Thread* thread = nullptr)
|
||||
return;
|
||||
}
|
||||
|
||||
auto& thread_context = thread->GetContext();
|
||||
auto& thread_context = thread->GetContext64();
|
||||
|
||||
if (id < SP_REGISTER) {
|
||||
thread_context.cpu_registers[id] = val;
|
||||
@@ -259,7 +260,7 @@ static u128 FpuRead(std::size_t id, Kernel::Thread* thread = nullptr) {
|
||||
return u128{0};
|
||||
}
|
||||
|
||||
auto& thread_context = thread->GetContext();
|
||||
auto& thread_context = thread->GetContext64();
|
||||
|
||||
if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
|
||||
return thread_context.vector_registers[id - UC_ARM64_REG_Q0];
|
||||
@@ -275,7 +276,7 @@ static void FpuWrite(std::size_t id, u128 val, Kernel::Thread* thread = nullptr)
|
||||
return;
|
||||
}
|
||||
|
||||
auto& thread_context = thread->GetContext();
|
||||
auto& thread_context = thread->GetContext64();
|
||||
|
||||
if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
|
||||
thread_context.vector_registers[id - UC_ARM64_REG_Q0] = val;
|
||||
@@ -916,7 +917,7 @@ static void WriteRegister() {
|
||||
// Update ARM context, skipping scheduler - no running threads at this point
|
||||
Core::System::GetInstance()
|
||||
.ArmInterface(current_core)
|
||||
.LoadContext(current_thread->GetContext());
|
||||
.LoadContext(current_thread->GetContext64());
|
||||
|
||||
SendReply("OK");
|
||||
}
|
||||
@@ -947,7 +948,7 @@ static void WriteRegisters() {
|
||||
// Update ARM context, skipping scheduler - no running threads at this point
|
||||
Core::System::GetInstance()
|
||||
.ArmInterface(current_core)
|
||||
.LoadContext(current_thread->GetContext());
|
||||
.LoadContext(current_thread->GetContext64());
|
||||
|
||||
SendReply("OK");
|
||||
}
|
||||
@@ -1019,7 +1020,7 @@ static void Step() {
|
||||
// Update ARM context, skipping scheduler - no running threads at this point
|
||||
Core::System::GetInstance()
|
||||
.ArmInterface(current_core)
|
||||
.LoadContext(current_thread->GetContext());
|
||||
.LoadContext(current_thread->GetContext64());
|
||||
}
|
||||
step_loop = true;
|
||||
halt_loop = true;
|
||||
@@ -1166,6 +1167,9 @@ static void RemoveBreakpoint() {
|
||||
|
||||
void HandlePacket() {
|
||||
if (!IsConnected()) {
|
||||
if (defer_start) {
|
||||
ToggleServer(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1256,6 +1260,10 @@ void ToggleServer(bool status) {
|
||||
}
|
||||
}
|
||||
|
||||
void DeferStart() {
|
||||
defer_start = true;
|
||||
}
|
||||
|
||||
static void Init(u16 port) {
|
||||
if (!server_enabled) {
|
||||
// Set the halt loop to false in case the user enabled the gdbstub mid-execution.
|
||||
@@ -1341,6 +1349,7 @@ void Shutdown() {
|
||||
if (!server_enabled) {
|
||||
return;
|
||||
}
|
||||
defer_start = false;
|
||||
|
||||
LOG_INFO(Debug_GDBStub, "Stopping GDB ...");
|
||||
if (gdbserver_socket != -1) {
|
||||
|
||||
@@ -43,6 +43,13 @@ void ToggleServer(bool status);
|
||||
/// Start the gdbstub server.
|
||||
void Init();
|
||||
|
||||
/**
|
||||
* Defer initialization of the gdbstub to the first packet processing functions.
|
||||
* This avoids a case where the gdbstub thread is frozen after initialization
|
||||
* and fails to respond in time to packets.
|
||||
*/
|
||||
void DeferStart();
|
||||
|
||||
/// Stop gdbstub server.
|
||||
void Shutdown();
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores
|
||||
|
||||
} // namespace Hardware
|
||||
|
||||
constexpr u32 INVALID_HOST_THREAD_ID = 0xFFFFFFFF;
|
||||
|
||||
struct EmuThreadHandle {
|
||||
u32 host_handle;
|
||||
u32 guest_handle;
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <atomic>
|
||||
#include <bitset>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "common/assert.h"
|
||||
@@ -15,6 +18,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/hardware_properties.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
@@ -25,6 +29,7 @@
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/hle/kernel/synchronization.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/memory.h"
|
||||
@@ -44,7 +49,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
|
||||
std::shared_ptr<Thread> thread =
|
||||
system.Kernel().RetrieveThreadFromWakeupCallbackHandleTable(proper_handle);
|
||||
system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle);
|
||||
if (thread == nullptr) {
|
||||
LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle);
|
||||
return;
|
||||
@@ -97,8 +102,8 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
|
||||
}
|
||||
|
||||
struct KernelCore::Impl {
|
||||
explicit Impl(Core::System& system)
|
||||
: system{system}, global_scheduler{system}, synchronization{system} {}
|
||||
explicit Impl(Core::System& system, KernelCore& kernel)
|
||||
: system{system}, global_scheduler{kernel}, synchronization{system}, time_manager{system} {}
|
||||
|
||||
void Initialize(KernelCore& kernel) {
|
||||
Shutdown();
|
||||
@@ -120,7 +125,7 @@ struct KernelCore::Impl {
|
||||
|
||||
system_resource_limit = nullptr;
|
||||
|
||||
thread_wakeup_callback_handle_table.Clear();
|
||||
global_handle_table.Clear();
|
||||
thread_wakeup_event_type = nullptr;
|
||||
preemption_event = nullptr;
|
||||
|
||||
@@ -138,8 +143,8 @@ struct KernelCore::Impl {
|
||||
|
||||
void InitializePhysicalCores() {
|
||||
exclusive_monitor =
|
||||
Core::MakeExclusiveMonitor(system.Memory(), global_scheduler.CpuCoresCount());
|
||||
for (std::size_t i = 0; i < global_scheduler.CpuCoresCount(); i++) {
|
||||
Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES);
|
||||
for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
|
||||
cores.emplace_back(system, i, *exclusive_monitor);
|
||||
}
|
||||
}
|
||||
@@ -181,9 +186,57 @@ struct KernelCore::Impl {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& core : cores) {
|
||||
core.SetIs64Bit(process->Is64BitProcess());
|
||||
}
|
||||
|
||||
system.Memory().SetCurrentPageTable(*process);
|
||||
}
|
||||
|
||||
void RegisterCoreThread(std::size_t core_id) {
|
||||
std::unique_lock lock{register_thread_mutex};
|
||||
const std::thread::id this_id = std::this_thread::get_id();
|
||||
const auto it = host_thread_ids.find(this_id);
|
||||
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
|
||||
ASSERT(it == host_thread_ids.end());
|
||||
ASSERT(!registered_core_threads[core_id]);
|
||||
host_thread_ids[this_id] = static_cast<u32>(core_id);
|
||||
registered_core_threads.set(core_id);
|
||||
}
|
||||
|
||||
void RegisterHostThread() {
|
||||
std::unique_lock lock{register_thread_mutex};
|
||||
const std::thread::id this_id = std::this_thread::get_id();
|
||||
const auto it = host_thread_ids.find(this_id);
|
||||
ASSERT(it == host_thread_ids.end());
|
||||
host_thread_ids[this_id] = registered_thread_ids++;
|
||||
}
|
||||
|
||||
u32 GetCurrentHostThreadID() const {
|
||||
const std::thread::id this_id = std::this_thread::get_id();
|
||||
const auto it = host_thread_ids.find(this_id);
|
||||
if (it == host_thread_ids.end()) {
|
||||
return Core::INVALID_HOST_THREAD_ID;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
Core::EmuThreadHandle GetCurrentEmuThreadID() const {
|
||||
Core::EmuThreadHandle result = Core::EmuThreadHandle::InvalidHandle();
|
||||
result.host_handle = GetCurrentHostThreadID();
|
||||
if (result.host_handle >= Core::Hardware::NUM_CPU_CORES) {
|
||||
return result;
|
||||
}
|
||||
const Kernel::Scheduler& sched = cores[result.host_handle].Scheduler();
|
||||
const Kernel::Thread* current = sched.GetCurrentThread();
|
||||
if (current != nullptr) {
|
||||
result.guest_handle = current->GetGlobalHandle();
|
||||
} else {
|
||||
result.guest_handle = InvalidHandle;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::atomic<u32> next_object_id{0};
|
||||
std::atomic<u64> next_kernel_process_id{Process::InitialKIPIDMin};
|
||||
std::atomic<u64> next_user_process_id{Process::ProcessIDMin};
|
||||
@@ -194,15 +247,16 @@ struct KernelCore::Impl {
|
||||
Process* current_process = nullptr;
|
||||
Kernel::GlobalScheduler global_scheduler;
|
||||
Kernel::Synchronization synchronization;
|
||||
Kernel::TimeManager time_manager;
|
||||
|
||||
std::shared_ptr<ResourceLimit> system_resource_limit;
|
||||
|
||||
std::shared_ptr<Core::Timing::EventType> thread_wakeup_event_type;
|
||||
std::shared_ptr<Core::Timing::EventType> preemption_event;
|
||||
|
||||
// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future,
|
||||
// allowing us to simply use a pool index or similar.
|
||||
Kernel::HandleTable thread_wakeup_callback_handle_table;
|
||||
// This is the kernel's handle table or supervisor handle table which
|
||||
// stores all the objects in place.
|
||||
Kernel::HandleTable global_handle_table;
|
||||
|
||||
/// Map of named ports managed by the kernel, which can be retrieved using
|
||||
/// the ConnectToPort SVC.
|
||||
@@ -211,11 +265,17 @@ struct KernelCore::Impl {
|
||||
std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
|
||||
std::vector<Kernel::PhysicalCore> cores;
|
||||
|
||||
// 0-3 IDs represent core threads, >3 represent others
|
||||
std::unordered_map<std::thread::id, u32> host_thread_ids;
|
||||
u32 registered_thread_ids{Core::Hardware::NUM_CPU_CORES};
|
||||
std::bitset<Core::Hardware::NUM_CPU_CORES> registered_core_threads;
|
||||
std::mutex register_thread_mutex;
|
||||
|
||||
// System context
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
KernelCore::KernelCore(Core::System& system) : impl{std::make_unique<Impl>(system)} {}
|
||||
KernelCore::KernelCore(Core::System& system) : impl{std::make_unique<Impl>(system, *this)} {}
|
||||
KernelCore::~KernelCore() {
|
||||
Shutdown();
|
||||
}
|
||||
@@ -232,9 +292,8 @@ std::shared_ptr<ResourceLimit> KernelCore::GetSystemResourceLimit() const {
|
||||
return impl->system_resource_limit;
|
||||
}
|
||||
|
||||
std::shared_ptr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable(
|
||||
Handle handle) const {
|
||||
return impl->thread_wakeup_callback_handle_table.Get<Thread>(handle);
|
||||
std::shared_ptr<Thread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const {
|
||||
return impl->global_handle_table.Get<Thread>(handle);
|
||||
}
|
||||
|
||||
void KernelCore::AppendNewProcess(std::shared_ptr<Process> process) {
|
||||
@@ -265,6 +324,14 @@ const Kernel::GlobalScheduler& KernelCore::GlobalScheduler() const {
|
||||
return impl->global_scheduler;
|
||||
}
|
||||
|
||||
Kernel::Scheduler& KernelCore::Scheduler(std::size_t id) {
|
||||
return impl->cores[id].Scheduler();
|
||||
}
|
||||
|
||||
const Kernel::Scheduler& KernelCore::Scheduler(std::size_t id) const {
|
||||
return impl->cores[id].Scheduler();
|
||||
}
|
||||
|
||||
Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) {
|
||||
return impl->cores[id];
|
||||
}
|
||||
@@ -281,6 +348,14 @@ const Kernel::Synchronization& KernelCore::Synchronization() const {
|
||||
return impl->synchronization;
|
||||
}
|
||||
|
||||
Kernel::TimeManager& KernelCore::TimeManager() {
|
||||
return impl->time_manager;
|
||||
}
|
||||
|
||||
const Kernel::TimeManager& KernelCore::TimeManager() const {
|
||||
return impl->time_manager;
|
||||
}
|
||||
|
||||
Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() {
|
||||
return *impl->exclusive_monitor;
|
||||
}
|
||||
@@ -338,12 +413,28 @@ const std::shared_ptr<Core::Timing::EventType>& KernelCore::ThreadWakeupCallback
|
||||
return impl->thread_wakeup_event_type;
|
||||
}
|
||||
|
||||
Kernel::HandleTable& KernelCore::ThreadWakeupCallbackHandleTable() {
|
||||
return impl->thread_wakeup_callback_handle_table;
|
||||
Kernel::HandleTable& KernelCore::GlobalHandleTable() {
|
||||
return impl->global_handle_table;
|
||||
}
|
||||
|
||||
const Kernel::HandleTable& KernelCore::ThreadWakeupCallbackHandleTable() const {
|
||||
return impl->thread_wakeup_callback_handle_table;
|
||||
const Kernel::HandleTable& KernelCore::GlobalHandleTable() const {
|
||||
return impl->global_handle_table;
|
||||
}
|
||||
|
||||
void KernelCore::RegisterCoreThread(std::size_t core_id) {
|
||||
impl->RegisterCoreThread(core_id);
|
||||
}
|
||||
|
||||
void KernelCore::RegisterHostThread() {
|
||||
impl->RegisterHostThread();
|
||||
}
|
||||
|
||||
u32 KernelCore::GetCurrentHostThreadID() const {
|
||||
return impl->GetCurrentHostThreadID();
|
||||
}
|
||||
|
||||
Core::EmuThreadHandle KernelCore::GetCurrentEmuThreadID() const {
|
||||
return impl->GetCurrentEmuThreadID();
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
namespace Core {
|
||||
struct EmuThreadHandle;
|
||||
class ExclusiveMonitor;
|
||||
class System;
|
||||
} // namespace Core
|
||||
@@ -29,8 +30,10 @@ class HandleTable;
|
||||
class PhysicalCore;
|
||||
class Process;
|
||||
class ResourceLimit;
|
||||
class Scheduler;
|
||||
class Synchronization;
|
||||
class Thread;
|
||||
class TimeManager;
|
||||
|
||||
/// Represents a single instance of the kernel.
|
||||
class KernelCore {
|
||||
@@ -64,7 +67,7 @@ public:
|
||||
std::shared_ptr<ResourceLimit> GetSystemResourceLimit() const;
|
||||
|
||||
/// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
|
||||
std::shared_ptr<Thread> RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const;
|
||||
std::shared_ptr<Thread> RetrieveThreadFromGlobalHandleTable(Handle handle) const;
|
||||
|
||||
/// Adds the given shared pointer to an internal list of active processes.
|
||||
void AppendNewProcess(std::shared_ptr<Process> process);
|
||||
@@ -87,6 +90,12 @@ public:
|
||||
/// Gets the sole instance of the global scheduler
|
||||
const Kernel::GlobalScheduler& GlobalScheduler() const;
|
||||
|
||||
/// Gets the sole instance of the Scheduler assoviated with cpu core 'id'
|
||||
Kernel::Scheduler& Scheduler(std::size_t id);
|
||||
|
||||
/// Gets the sole instance of the Scheduler assoviated with cpu core 'id'
|
||||
const Kernel::Scheduler& Scheduler(std::size_t id) const;
|
||||
|
||||
/// Gets the an instance of the respective physical CPU core.
|
||||
Kernel::PhysicalCore& PhysicalCore(std::size_t id);
|
||||
|
||||
@@ -99,6 +108,12 @@ public:
|
||||
/// Gets the an instance of the Synchronization Interface.
|
||||
const Kernel::Synchronization& Synchronization() const;
|
||||
|
||||
/// Gets the an instance of the TimeManager Interface.
|
||||
Kernel::TimeManager& TimeManager();
|
||||
|
||||
/// Gets the an instance of the TimeManager Interface.
|
||||
const Kernel::TimeManager& TimeManager() const;
|
||||
|
||||
/// Stops execution of 'id' core, in order to reschedule a new thread.
|
||||
void PrepareReschedule(std::size_t id);
|
||||
|
||||
@@ -120,6 +135,18 @@ public:
|
||||
/// Determines whether or not the given port is a valid named port.
|
||||
bool IsValidNamedPort(NamedPortTable::const_iterator port) const;
|
||||
|
||||
/// Gets the current host_thread/guest_thread handle.
|
||||
Core::EmuThreadHandle GetCurrentEmuThreadID() const;
|
||||
|
||||
/// Gets the current host_thread handle.
|
||||
u32 GetCurrentHostThreadID() const;
|
||||
|
||||
/// Register the current thread as a CPU Core Thread.
|
||||
void RegisterCoreThread(std::size_t core_id);
|
||||
|
||||
/// Register the current thread as a non CPU core thread.
|
||||
void RegisterHostThread();
|
||||
|
||||
private:
|
||||
friend class Object;
|
||||
friend class Process;
|
||||
@@ -140,11 +167,11 @@ private:
|
||||
/// Retrieves the event type used for thread wakeup callbacks.
|
||||
const std::shared_ptr<Core::Timing::EventType>& ThreadWakeupCallbackEventType() const;
|
||||
|
||||
/// Provides a reference to the thread wakeup callback handle table.
|
||||
Kernel::HandleTable& ThreadWakeupCallbackHandleTable();
|
||||
/// Provides a reference to the global handle table.
|
||||
Kernel::HandleTable& GlobalHandleTable();
|
||||
|
||||
/// Provides a const reference to the thread wakeup callback handle table.
|
||||
const Kernel::HandleTable& ThreadWakeupCallbackHandleTable() const;
|
||||
/// Provides a const reference to the global handle table.
|
||||
const Kernel::HandleTable& GlobalHandleTable() const;
|
||||
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "core/arm/dynarmic/arm_dynarmic.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic_32.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic_64.h"
|
||||
#endif
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/arm/unicorn/arm_unicorn.h"
|
||||
@@ -20,13 +21,17 @@ PhysicalCore::PhysicalCore(Core::System& system, std::size_t id,
|
||||
Core::ExclusiveMonitor& exclusive_monitor)
|
||||
: core_index{id} {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
arm_interface = std::make_unique<Core::ARM_Dynarmic>(system, exclusive_monitor, core_index);
|
||||
arm_interface_32 =
|
||||
std::make_unique<Core::ARM_Dynarmic_32>(system, exclusive_monitor, core_index);
|
||||
arm_interface_64 =
|
||||
std::make_unique<Core::ARM_Dynarmic_64>(system, exclusive_monitor, core_index);
|
||||
|
||||
#else
|
||||
arm_interface = std::make_shared<Core::ARM_Unicorn>(system);
|
||||
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
|
||||
#endif
|
||||
|
||||
scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface, core_index);
|
||||
scheduler = std::make_unique<Kernel::Scheduler>(system, core_index);
|
||||
}
|
||||
|
||||
PhysicalCore::~PhysicalCore() = default;
|
||||
@@ -48,4 +53,12 @@ void PhysicalCore::Shutdown() {
|
||||
scheduler->Shutdown();
|
||||
}
|
||||
|
||||
void PhysicalCore::SetIs64Bit(bool is_64_bit) {
|
||||
if (is_64_bit) {
|
||||
arm_interface = arm_interface_64.get();
|
||||
} else {
|
||||
arm_interface = arm_interface_32.get();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -68,10 +68,14 @@ public:
|
||||
return *scheduler;
|
||||
}
|
||||
|
||||
void SetIs64Bit(bool is_64_bit);
|
||||
|
||||
private:
|
||||
std::size_t core_index;
|
||||
std::unique_ptr<Core::ARM_Interface> arm_interface;
|
||||
std::unique_ptr<Core::ARM_Interface> arm_interface_32;
|
||||
std::unique_ptr<Core::ARM_Interface> arm_interface_64;
|
||||
std::unique_ptr<Kernel::Scheduler> scheduler;
|
||||
Core::ARM_Interface* arm_interface{};
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -42,7 +42,8 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) {
|
||||
|
||||
// Register 1 must be a handle to the main thread
|
||||
const Handle thread_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
|
||||
thread->GetContext().cpu_registers[1] = thread_handle;
|
||||
thread->GetContext32().cpu_registers[1] = thread_handle;
|
||||
thread->GetContext64().cpu_registers[1] = thread_handle;
|
||||
|
||||
// Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
|
||||
thread->ResumeFromWait();
|
||||
|
||||
@@ -18,10 +18,11 @@
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
GlobalScheduler::GlobalScheduler(Core::System& system) : system{system} {}
|
||||
GlobalScheduler::GlobalScheduler(KernelCore& kernel) : kernel{kernel} {}
|
||||
|
||||
GlobalScheduler::~GlobalScheduler() = default;
|
||||
|
||||
@@ -35,7 +36,7 @@ void GlobalScheduler::RemoveThread(std::shared_ptr<Thread> thread) {
|
||||
}
|
||||
|
||||
void GlobalScheduler::UnloadThread(std::size_t core) {
|
||||
Scheduler& sched = system.Scheduler(core);
|
||||
Scheduler& sched = kernel.Scheduler(core);
|
||||
sched.UnloadThread();
|
||||
}
|
||||
|
||||
@@ -50,7 +51,7 @@ void GlobalScheduler::SelectThread(std::size_t core) {
|
||||
sched.is_context_switch_pending = sched.selected_thread != sched.current_thread;
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
};
|
||||
Scheduler& sched = system.Scheduler(core);
|
||||
Scheduler& sched = kernel.Scheduler(core);
|
||||
Thread* current_thread = nullptr;
|
||||
// Step 1: Get top thread in schedule queue.
|
||||
current_thread = scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front();
|
||||
@@ -356,8 +357,34 @@ void GlobalScheduler::Shutdown() {
|
||||
thread_list.clear();
|
||||
}
|
||||
|
||||
Scheduler::Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, std::size_t core_id)
|
||||
: system(system), cpu_core(cpu_core), core_id(core_id) {}
|
||||
void GlobalScheduler::Lock() {
|
||||
Core::EmuThreadHandle current_thread = kernel.GetCurrentEmuThreadID();
|
||||
if (current_thread == current_owner) {
|
||||
++scope_lock;
|
||||
} else {
|
||||
inner_lock.lock();
|
||||
current_owner = current_thread;
|
||||
ASSERT(current_owner != Core::EmuThreadHandle::InvalidHandle());
|
||||
scope_lock = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalScheduler::Unlock() {
|
||||
if (--scope_lock != 0) {
|
||||
ASSERT(scope_lock > 0);
|
||||
return;
|
||||
}
|
||||
for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
|
||||
SelectThread(i);
|
||||
}
|
||||
current_owner = Core::EmuThreadHandle::InvalidHandle();
|
||||
scope_lock = 1;
|
||||
inner_lock.unlock();
|
||||
// TODO(Blinkhawk): Setup the interrupts and change context on current core.
|
||||
}
|
||||
|
||||
Scheduler::Scheduler(Core::System& system, std::size_t core_id)
|
||||
: system{system}, core_id{core_id} {}
|
||||
|
||||
Scheduler::~Scheduler() = default;
|
||||
|
||||
@@ -395,9 +422,10 @@ void Scheduler::UnloadThread() {
|
||||
|
||||
// Save context for previous thread
|
||||
if (previous_thread) {
|
||||
cpu_core.SaveContext(previous_thread->GetContext());
|
||||
system.ArmInterface(core_id).SaveContext(previous_thread->GetContext32());
|
||||
system.ArmInterface(core_id).SaveContext(previous_thread->GetContext64());
|
||||
// Save the TPIDR_EL0 system register in case it was modified.
|
||||
previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
|
||||
previous_thread->SetTPIDR_EL0(system.ArmInterface(core_id).GetTPIDR_EL0());
|
||||
|
||||
if (previous_thread->GetStatus() == ThreadStatus::Running) {
|
||||
// This is only the case when a reschedule is triggered without the current thread
|
||||
@@ -424,9 +452,10 @@ void Scheduler::SwitchContext() {
|
||||
|
||||
// Save context for previous thread
|
||||
if (previous_thread) {
|
||||
cpu_core.SaveContext(previous_thread->GetContext());
|
||||
system.ArmInterface(core_id).SaveContext(previous_thread->GetContext32());
|
||||
system.ArmInterface(core_id).SaveContext(previous_thread->GetContext64());
|
||||
// Save the TPIDR_EL0 system register in case it was modified.
|
||||
previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
|
||||
previous_thread->SetTPIDR_EL0(system.ArmInterface(core_id).GetTPIDR_EL0());
|
||||
|
||||
if (previous_thread->GetStatus() == ThreadStatus::Running) {
|
||||
// This is only the case when a reschedule is triggered without the current thread
|
||||
@@ -454,9 +483,10 @@ void Scheduler::SwitchContext() {
|
||||
system.Kernel().MakeCurrentProcess(thread_owner_process);
|
||||
}
|
||||
|
||||
cpu_core.LoadContext(new_thread->GetContext());
|
||||
cpu_core.SetTlsAddress(new_thread->GetTLSAddress());
|
||||
cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
|
||||
system.ArmInterface(core_id).LoadContext(new_thread->GetContext32());
|
||||
system.ArmInterface(core_id).LoadContext(new_thread->GetContext64());
|
||||
system.ArmInterface(core_id).SetTlsAddress(new_thread->GetTLSAddress());
|
||||
system.ArmInterface(core_id).SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
|
||||
} else {
|
||||
current_thread = nullptr;
|
||||
// Note: We do not reset the current process and current page table when idling because
|
||||
@@ -485,4 +515,27 @@ void Scheduler::Shutdown() {
|
||||
selected_thread = nullptr;
|
||||
}
|
||||
|
||||
SchedulerLock::SchedulerLock(KernelCore& kernel) : kernel{kernel} {
|
||||
kernel.GlobalScheduler().Lock();
|
||||
}
|
||||
|
||||
SchedulerLock::~SchedulerLock() {
|
||||
kernel.GlobalScheduler().Unlock();
|
||||
}
|
||||
|
||||
SchedulerLockAndSleep::SchedulerLockAndSleep(KernelCore& kernel, Handle& event_handle,
|
||||
Thread* time_task, s64 nanoseconds)
|
||||
: SchedulerLock{kernel}, event_handle{event_handle}, time_task{time_task}, nanoseconds{
|
||||
nanoseconds} {
|
||||
event_handle = InvalidHandle;
|
||||
}
|
||||
|
||||
SchedulerLockAndSleep::~SchedulerLockAndSleep() {
|
||||
if (sleep_cancelled) {
|
||||
return;
|
||||
}
|
||||
auto& time_manager = kernel.TimeManager();
|
||||
time_manager.ScheduleTimeEvent(event_handle, time_task, nanoseconds);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
@@ -20,11 +21,13 @@ class System;
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
class Process;
|
||||
class SchedulerLock;
|
||||
|
||||
class GlobalScheduler final {
|
||||
public:
|
||||
explicit GlobalScheduler(Core::System& system);
|
||||
explicit GlobalScheduler(KernelCore& kernel);
|
||||
~GlobalScheduler();
|
||||
|
||||
/// Adds a new thread to the scheduler
|
||||
@@ -138,6 +141,14 @@ public:
|
||||
void Shutdown();
|
||||
|
||||
private:
|
||||
friend class SchedulerLock;
|
||||
|
||||
/// Lock the scheduler to the current thread.
|
||||
void Lock();
|
||||
|
||||
/// Unlocks the scheduler, reselects threads, interrupts cores for rescheduling
|
||||
/// and reschedules current core if needed.
|
||||
void Unlock();
|
||||
/**
|
||||
* Transfers a thread into an specific core. If the destination_core is -1
|
||||
* it will be unscheduled from its source code and added into its suggested
|
||||
@@ -158,14 +169,19 @@ private:
|
||||
// ordered from Core 0 to Core 3.
|
||||
std::array<u32, Core::Hardware::NUM_CPU_CORES> preemption_priorities = {59, 59, 59, 62};
|
||||
|
||||
/// Scheduler lock mechanisms.
|
||||
std::mutex inner_lock{}; // TODO(Blinkhawk): Replace for a SpinLock
|
||||
std::atomic<s64> scope_lock{};
|
||||
Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()};
|
||||
|
||||
/// Lists all thread ids that aren't deleted/etc.
|
||||
std::vector<std::shared_ptr<Thread>> thread_list;
|
||||
Core::System& system;
|
||||
KernelCore& kernel;
|
||||
};
|
||||
|
||||
class Scheduler final {
|
||||
public:
|
||||
explicit Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, std::size_t core_id);
|
||||
explicit Scheduler(Core::System& system, std::size_t core_id);
|
||||
~Scheduler();
|
||||
|
||||
/// Returns whether there are any threads that are ready to run.
|
||||
@@ -219,7 +235,6 @@ private:
|
||||
std::shared_ptr<Thread> selected_thread = nullptr;
|
||||
|
||||
Core::System& system;
|
||||
Core::ARM_Interface& cpu_core;
|
||||
u64 last_context_switch_time = 0;
|
||||
u64 idle_selection_count = 0;
|
||||
const std::size_t core_id;
|
||||
@@ -227,4 +242,30 @@ private:
|
||||
bool is_context_switch_pending = false;
|
||||
};
|
||||
|
||||
class SchedulerLock {
|
||||
public:
|
||||
explicit SchedulerLock(KernelCore& kernel);
|
||||
~SchedulerLock();
|
||||
|
||||
protected:
|
||||
KernelCore& kernel;
|
||||
};
|
||||
|
||||
class SchedulerLockAndSleep : public SchedulerLock {
|
||||
public:
|
||||
explicit SchedulerLockAndSleep(KernelCore& kernel, Handle& event_handle, Thread* time_task,
|
||||
s64 nanoseconds);
|
||||
~SchedulerLockAndSleep();
|
||||
|
||||
void CancelSleep() {
|
||||
sleep_cancelled = true;
|
||||
}
|
||||
|
||||
private:
|
||||
Handle& event_handle;
|
||||
Thread* time_task;
|
||||
s64 nanoseconds;
|
||||
bool sleep_cancelled{};
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -187,6 +187,13 @@ static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_s
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static ResultCode SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size) {
|
||||
VAddr temp_heap_addr{};
|
||||
const ResultCode result{SetHeapSize(system, &temp_heap_addr, heap_size)};
|
||||
*heap_addr = static_cast<u32>(temp_heap_addr);
|
||||
return result;
|
||||
}
|
||||
|
||||
static ResultCode SetMemoryPermission(Core::System& system, VAddr addr, u64 size, u32 prot) {
|
||||
LOG_TRACE(Kernel_SVC, "called, addr=0x{:X}, size=0x{:X}, prot=0x{:X}", addr, size, prot);
|
||||
|
||||
@@ -371,6 +378,12 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static ResultCode ConnectToNamedPort32(Core::System& system, Handle* out_handle,
|
||||
u32 port_name_address) {
|
||||
|
||||
return ConnectToNamedPort(system, out_handle, port_name_address);
|
||||
}
|
||||
|
||||
/// Makes a blocking IPC call to an OS service.
|
||||
static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
|
||||
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
|
||||
@@ -390,6 +403,10 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
|
||||
return session->SendSyncRequest(SharedFrom(thread), system.Memory());
|
||||
}
|
||||
|
||||
static ResultCode SendSyncRequest32(Core::System& system, Handle handle) {
|
||||
return SendSyncRequest(system, handle);
|
||||
}
|
||||
|
||||
/// Get the ID for the specified thread.
|
||||
static ResultCode GetThreadId(Core::System& system, u64* thread_id, Handle thread_handle) {
|
||||
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
|
||||
@@ -405,6 +422,17 @@ static ResultCode GetThreadId(Core::System& system, u64* thread_id, Handle threa
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static ResultCode GetThreadId32(Core::System& system, u32* thread_id_low, u32* thread_id_high,
|
||||
Handle thread_handle) {
|
||||
u64 thread_id{};
|
||||
const ResultCode result{GetThreadId(system, &thread_id, thread_handle)};
|
||||
|
||||
*thread_id_low = static_cast<u32>(thread_id >> 32);
|
||||
*thread_id_high = static_cast<u32>(thread_id & std::numeric_limits<u32>::max());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Gets the ID of the specified process or a specified thread's owning process.
|
||||
static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle handle) {
|
||||
LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle);
|
||||
@@ -479,6 +507,12 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr
|
||||
return result;
|
||||
}
|
||||
|
||||
static ResultCode WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address,
|
||||
s32 handle_count, u32 timeout_high, Handle* index) {
|
||||
const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)};
|
||||
return WaitSynchronization(system, index, handles_address, handle_count, nano_seconds);
|
||||
}
|
||||
|
||||
/// Resumes a thread waiting on WaitSynchronization
|
||||
static ResultCode CancelSynchronization(Core::System& system, Handle thread_handle) {
|
||||
LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle);
|
||||
@@ -917,6 +951,18 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
|
||||
}
|
||||
}
|
||||
|
||||
static ResultCode GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low,
|
||||
u32 info_id, u32 handle, u32 sub_id_high) {
|
||||
const u64 sub_id{static_cast<u64>(sub_id_low | (static_cast<u64>(sub_id_high) << 32))};
|
||||
u64 res_value{};
|
||||
|
||||
const ResultCode result{GetInfo(system, &res_value, info_id, handle, sub_id)};
|
||||
*result_high = static_cast<u32>(res_value >> 32);
|
||||
*result_low = static_cast<u32>(res_value & std::numeric_limits<u32>::max());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Maps memory at a desired address
|
||||
static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
|
||||
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
|
||||
@@ -1058,7 +1104,7 @@ static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, H
|
||||
return ERR_BUSY;
|
||||
}
|
||||
|
||||
Core::ARM_Interface::ThreadContext ctx = thread->GetContext();
|
||||
Core::ARM_Interface::ThreadContext64 ctx = thread->GetContext64();
|
||||
// Mask away mode bits, interrupt bits, IL bit, and other reserved bits.
|
||||
ctx.pstate &= 0xFF0FFE20;
|
||||
|
||||
@@ -1088,6 +1134,10 @@ static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static ResultCode GetThreadPriority32(Core::System& system, u32* priority, Handle handle) {
|
||||
return GetThreadPriority(system, priority, handle);
|
||||
}
|
||||
|
||||
/// Sets the priority for the specified thread
|
||||
static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 priority) {
|
||||
LOG_TRACE(Kernel_SVC, "called");
|
||||
@@ -1259,6 +1309,11 @@ static ResultCode QueryMemory(Core::System& system, VAddr memory_info_address,
|
||||
query_address);
|
||||
}
|
||||
|
||||
static ResultCode QueryMemory32(Core::System& system, u32 memory_info_address,
|
||||
u32 page_info_address, u32 query_address) {
|
||||
return QueryMemory(system, memory_info_address, page_info_address, query_address);
|
||||
}
|
||||
|
||||
static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
|
||||
u64 src_address, u64 size) {
|
||||
LOG_DEBUG(Kernel_SVC,
|
||||
@@ -1675,6 +1730,10 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_
|
||||
}
|
||||
}
|
||||
|
||||
static void SignalProcessWideKey32(Core::System& system, u32 condition_variable_addr, s32 target) {
|
||||
SignalProcessWideKey(system, condition_variable_addr, target);
|
||||
}
|
||||
|
||||
// Wait for an address (via Address Arbiter)
|
||||
static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, s32 value,
|
||||
s64 timeout) {
|
||||
@@ -1760,6 +1819,10 @@ static ResultCode CloseHandle(Core::System& system, Handle handle) {
|
||||
return handle_table.Close(handle);
|
||||
}
|
||||
|
||||
static ResultCode CloseHandle32(Core::System& system, Handle handle) {
|
||||
return CloseHandle(system, handle);
|
||||
}
|
||||
|
||||
/// Clears the signaled state of an event or process.
|
||||
static ResultCode ResetSignal(Core::System& system, Handle handle) {
|
||||
LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
|
||||
@@ -2317,69 +2380,196 @@ struct FunctionDef {
|
||||
};
|
||||
} // namespace
|
||||
|
||||
static const FunctionDef SVC_Table[] = {
|
||||
static const FunctionDef SVC_Table_32[] = {
|
||||
{0x00, nullptr, "Unknown"},
|
||||
{0x01, SvcWrap<SetHeapSize>, "SetHeapSize"},
|
||||
{0x02, SvcWrap<SetMemoryPermission>, "SetMemoryPermission"},
|
||||
{0x03, SvcWrap<SetMemoryAttribute>, "SetMemoryAttribute"},
|
||||
{0x04, SvcWrap<MapMemory>, "MapMemory"},
|
||||
{0x05, SvcWrap<UnmapMemory>, "UnmapMemory"},
|
||||
{0x06, SvcWrap<QueryMemory>, "QueryMemory"},
|
||||
{0x07, SvcWrap<ExitProcess>, "ExitProcess"},
|
||||
{0x08, SvcWrap<CreateThread>, "CreateThread"},
|
||||
{0x09, SvcWrap<StartThread>, "StartThread"},
|
||||
{0x0A, SvcWrap<ExitThread>, "ExitThread"},
|
||||
{0x0B, SvcWrap<SleepThread>, "SleepThread"},
|
||||
{0x0C, SvcWrap<GetThreadPriority>, "GetThreadPriority"},
|
||||
{0x0D, SvcWrap<SetThreadPriority>, "SetThreadPriority"},
|
||||
{0x0E, SvcWrap<GetThreadCoreMask>, "GetThreadCoreMask"},
|
||||
{0x0F, SvcWrap<SetThreadCoreMask>, "SetThreadCoreMask"},
|
||||
{0x10, SvcWrap<GetCurrentProcessorNumber>, "GetCurrentProcessorNumber"},
|
||||
{0x11, SvcWrap<SignalEvent>, "SignalEvent"},
|
||||
{0x12, SvcWrap<ClearEvent>, "ClearEvent"},
|
||||
{0x13, SvcWrap<MapSharedMemory>, "MapSharedMemory"},
|
||||
{0x14, SvcWrap<UnmapSharedMemory>, "UnmapSharedMemory"},
|
||||
{0x15, SvcWrap<CreateTransferMemory>, "CreateTransferMemory"},
|
||||
{0x16, SvcWrap<CloseHandle>, "CloseHandle"},
|
||||
{0x17, SvcWrap<ResetSignal>, "ResetSignal"},
|
||||
{0x18, SvcWrap<WaitSynchronization>, "WaitSynchronization"},
|
||||
{0x19, SvcWrap<CancelSynchronization>, "CancelSynchronization"},
|
||||
{0x1A, SvcWrap<ArbitrateLock>, "ArbitrateLock"},
|
||||
{0x1B, SvcWrap<ArbitrateUnlock>, "ArbitrateUnlock"},
|
||||
{0x1C, SvcWrap<WaitProcessWideKeyAtomic>, "WaitProcessWideKeyAtomic"},
|
||||
{0x1D, SvcWrap<SignalProcessWideKey>, "SignalProcessWideKey"},
|
||||
{0x1E, SvcWrap<GetSystemTick>, "GetSystemTick"},
|
||||
{0x1F, SvcWrap<ConnectToNamedPort>, "ConnectToNamedPort"},
|
||||
{0x01, SvcWrap32<SetHeapSize32>, "SetHeapSize32"},
|
||||
{0x02, nullptr, "Unknown"},
|
||||
{0x03, nullptr, "SetMemoryAttribute32"},
|
||||
{0x04, nullptr, "MapMemory32"},
|
||||
{0x05, nullptr, "UnmapMemory32"},
|
||||
{0x06, SvcWrap32<QueryMemory32>, "QueryMemory32"},
|
||||
{0x07, nullptr, "ExitProcess32"},
|
||||
{0x08, nullptr, "CreateThread32"},
|
||||
{0x09, nullptr, "StartThread32"},
|
||||
{0x0a, nullptr, "ExitThread32"},
|
||||
{0x0b, nullptr, "SleepThread32"},
|
||||
{0x0c, SvcWrap32<GetThreadPriority32>, "GetThreadPriority32"},
|
||||
{0x0d, nullptr, "SetThreadPriority32"},
|
||||
{0x0e, nullptr, "GetThreadCoreMask32"},
|
||||
{0x0f, nullptr, "SetThreadCoreMask32"},
|
||||
{0x10, nullptr, "GetCurrentProcessorNumber32"},
|
||||
{0x11, nullptr, "SignalEvent32"},
|
||||
{0x12, nullptr, "ClearEvent32"},
|
||||
{0x13, nullptr, "MapSharedMemory32"},
|
||||
{0x14, nullptr, "UnmapSharedMemory32"},
|
||||
{0x15, nullptr, "CreateTransferMemory32"},
|
||||
{0x16, SvcWrap32<CloseHandle32>, "CloseHandle32"},
|
||||
{0x17, nullptr, "ResetSignal32"},
|
||||
{0x18, SvcWrap32<WaitSynchronization32>, "WaitSynchronization32"},
|
||||
{0x19, nullptr, "CancelSynchronization32"},
|
||||
{0x1a, nullptr, "ArbitrateLock32"},
|
||||
{0x1b, nullptr, "ArbitrateUnlock32"},
|
||||
{0x1c, nullptr, "WaitProcessWideKeyAtomic32"},
|
||||
{0x1d, SvcWrap32<SignalProcessWideKey32>, "SignalProcessWideKey32"},
|
||||
{0x1e, nullptr, "GetSystemTick32"},
|
||||
{0x1f, SvcWrap32<ConnectToNamedPort32>, "ConnectToNamedPort32"},
|
||||
{0x20, nullptr, "Unknown"},
|
||||
{0x21, SvcWrap32<SendSyncRequest32>, "SendSyncRequest32"},
|
||||
{0x22, nullptr, "SendSyncRequestWithUserBuffer32"},
|
||||
{0x23, nullptr, "Unknown"},
|
||||
{0x24, nullptr, "GetProcessId32"},
|
||||
{0x25, SvcWrap32<GetThreadId32>, "GetThreadId32"},
|
||||
{0x26, nullptr, "Break32"},
|
||||
{0x27, nullptr, "OutputDebugString32"},
|
||||
{0x28, nullptr, "Unknown"},
|
||||
{0x29, SvcWrap32<GetInfo32>, "GetInfo32"},
|
||||
{0x2a, nullptr, "Unknown"},
|
||||
{0x2b, nullptr, "Unknown"},
|
||||
{0x2c, nullptr, "MapPhysicalMemory32"},
|
||||
{0x2d, nullptr, "UnmapPhysicalMemory32"},
|
||||
{0x2e, nullptr, "Unknown"},
|
||||
{0x2f, nullptr, "Unknown"},
|
||||
{0x30, nullptr, "Unknown"},
|
||||
{0x31, nullptr, "Unknown"},
|
||||
{0x32, nullptr, "SetThreadActivity32"},
|
||||
{0x33, nullptr, "GetThreadContext32"},
|
||||
{0x34, nullptr, "WaitForAddress32"},
|
||||
{0x35, nullptr, "SignalToAddress32"},
|
||||
{0x36, nullptr, "Unknown"},
|
||||
{0x37, nullptr, "Unknown"},
|
||||
{0x38, nullptr, "Unknown"},
|
||||
{0x39, nullptr, "Unknown"},
|
||||
{0x3a, nullptr, "Unknown"},
|
||||
{0x3b, nullptr, "Unknown"},
|
||||
{0x3c, nullptr, "Unknown"},
|
||||
{0x3d, nullptr, "Unknown"},
|
||||
{0x3e, nullptr, "Unknown"},
|
||||
{0x3f, nullptr, "Unknown"},
|
||||
{0x40, nullptr, "CreateSession32"},
|
||||
{0x41, nullptr, "AcceptSession32"},
|
||||
{0x42, nullptr, "Unknown"},
|
||||
{0x43, nullptr, "ReplyAndReceive32"},
|
||||
{0x44, nullptr, "Unknown"},
|
||||
{0x45, nullptr, "CreateEvent32"},
|
||||
{0x46, nullptr, "Unknown"},
|
||||
{0x47, nullptr, "Unknown"},
|
||||
{0x48, nullptr, "Unknown"},
|
||||
{0x49, nullptr, "Unknown"},
|
||||
{0x4a, nullptr, "Unknown"},
|
||||
{0x4b, nullptr, "Unknown"},
|
||||
{0x4c, nullptr, "Unknown"},
|
||||
{0x4d, nullptr, "Unknown"},
|
||||
{0x4e, nullptr, "Unknown"},
|
||||
{0x4f, nullptr, "Unknown"},
|
||||
{0x50, nullptr, "Unknown"},
|
||||
{0x51, nullptr, "Unknown"},
|
||||
{0x52, nullptr, "Unknown"},
|
||||
{0x53, nullptr, "Unknown"},
|
||||
{0x54, nullptr, "Unknown"},
|
||||
{0x55, nullptr, "Unknown"},
|
||||
{0x56, nullptr, "Unknown"},
|
||||
{0x57, nullptr, "Unknown"},
|
||||
{0x58, nullptr, "Unknown"},
|
||||
{0x59, nullptr, "Unknown"},
|
||||
{0x5a, nullptr, "Unknown"},
|
||||
{0x5b, nullptr, "Unknown"},
|
||||
{0x5c, nullptr, "Unknown"},
|
||||
{0x5d, nullptr, "Unknown"},
|
||||
{0x5e, nullptr, "Unknown"},
|
||||
{0x5F, nullptr, "FlushProcessDataCache32"},
|
||||
{0x60, nullptr, "Unknown"},
|
||||
{0x61, nullptr, "Unknown"},
|
||||
{0x62, nullptr, "Unknown"},
|
||||
{0x63, nullptr, "Unknown"},
|
||||
{0x64, nullptr, "Unknown"},
|
||||
{0x65, nullptr, "GetProcessList32"},
|
||||
{0x66, nullptr, "Unknown"},
|
||||
{0x67, nullptr, "Unknown"},
|
||||
{0x68, nullptr, "Unknown"},
|
||||
{0x69, nullptr, "Unknown"},
|
||||
{0x6A, nullptr, "Unknown"},
|
||||
{0x6B, nullptr, "Unknown"},
|
||||
{0x6C, nullptr, "Unknown"},
|
||||
{0x6D, nullptr, "Unknown"},
|
||||
{0x6E, nullptr, "Unknown"},
|
||||
{0x6f, nullptr, "GetSystemInfo32"},
|
||||
{0x70, nullptr, "CreatePort32"},
|
||||
{0x71, nullptr, "ManageNamedPort32"},
|
||||
{0x72, nullptr, "ConnectToPort32"},
|
||||
{0x73, nullptr, "SetProcessMemoryPermission32"},
|
||||
{0x74, nullptr, "Unknown"},
|
||||
{0x75, nullptr, "Unknown"},
|
||||
{0x76, nullptr, "Unknown"},
|
||||
{0x77, nullptr, "MapProcessCodeMemory32"},
|
||||
{0x78, nullptr, "UnmapProcessCodeMemory32"},
|
||||
{0x79, nullptr, "Unknown"},
|
||||
{0x7A, nullptr, "Unknown"},
|
||||
{0x7B, nullptr, "TerminateProcess32"},
|
||||
};
|
||||
|
||||
static const FunctionDef SVC_Table_64[] = {
|
||||
{0x00, nullptr, "Unknown"},
|
||||
{0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"},
|
||||
{0x02, SvcWrap64<SetMemoryPermission>, "SetMemoryPermission"},
|
||||
{0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"},
|
||||
{0x04, SvcWrap64<MapMemory>, "MapMemory"},
|
||||
{0x05, SvcWrap64<UnmapMemory>, "UnmapMemory"},
|
||||
{0x06, SvcWrap64<QueryMemory>, "QueryMemory"},
|
||||
{0x07, SvcWrap64<ExitProcess>, "ExitProcess"},
|
||||
{0x08, SvcWrap64<CreateThread>, "CreateThread"},
|
||||
{0x09, SvcWrap64<StartThread>, "StartThread"},
|
||||
{0x0A, SvcWrap64<ExitThread>, "ExitThread"},
|
||||
{0x0B, SvcWrap64<SleepThread>, "SleepThread"},
|
||||
{0x0C, SvcWrap64<GetThreadPriority>, "GetThreadPriority"},
|
||||
{0x0D, SvcWrap64<SetThreadPriority>, "SetThreadPriority"},
|
||||
{0x0E, SvcWrap64<GetThreadCoreMask>, "GetThreadCoreMask"},
|
||||
{0x0F, SvcWrap64<SetThreadCoreMask>, "SetThreadCoreMask"},
|
||||
{0x10, SvcWrap64<GetCurrentProcessorNumber>, "GetCurrentProcessorNumber"},
|
||||
{0x11, SvcWrap64<SignalEvent>, "SignalEvent"},
|
||||
{0x12, SvcWrap64<ClearEvent>, "ClearEvent"},
|
||||
{0x13, SvcWrap64<MapSharedMemory>, "MapSharedMemory"},
|
||||
{0x14, SvcWrap64<UnmapSharedMemory>, "UnmapSharedMemory"},
|
||||
{0x15, SvcWrap64<CreateTransferMemory>, "CreateTransferMemory"},
|
||||
{0x16, SvcWrap64<CloseHandle>, "CloseHandle"},
|
||||
{0x17, SvcWrap64<ResetSignal>, "ResetSignal"},
|
||||
{0x18, SvcWrap64<WaitSynchronization>, "WaitSynchronization"},
|
||||
{0x19, SvcWrap64<CancelSynchronization>, "CancelSynchronization"},
|
||||
{0x1A, SvcWrap64<ArbitrateLock>, "ArbitrateLock"},
|
||||
{0x1B, SvcWrap64<ArbitrateUnlock>, "ArbitrateUnlock"},
|
||||
{0x1C, SvcWrap64<WaitProcessWideKeyAtomic>, "WaitProcessWideKeyAtomic"},
|
||||
{0x1D, SvcWrap64<SignalProcessWideKey>, "SignalProcessWideKey"},
|
||||
{0x1E, SvcWrap64<GetSystemTick>, "GetSystemTick"},
|
||||
{0x1F, SvcWrap64<ConnectToNamedPort>, "ConnectToNamedPort"},
|
||||
{0x20, nullptr, "SendSyncRequestLight"},
|
||||
{0x21, SvcWrap<SendSyncRequest>, "SendSyncRequest"},
|
||||
{0x21, SvcWrap64<SendSyncRequest>, "SendSyncRequest"},
|
||||
{0x22, nullptr, "SendSyncRequestWithUserBuffer"},
|
||||
{0x23, nullptr, "SendAsyncRequestWithUserBuffer"},
|
||||
{0x24, SvcWrap<GetProcessId>, "GetProcessId"},
|
||||
{0x25, SvcWrap<GetThreadId>, "GetThreadId"},
|
||||
{0x26, SvcWrap<Break>, "Break"},
|
||||
{0x27, SvcWrap<OutputDebugString>, "OutputDebugString"},
|
||||
{0x24, SvcWrap64<GetProcessId>, "GetProcessId"},
|
||||
{0x25, SvcWrap64<GetThreadId>, "GetThreadId"},
|
||||
{0x26, SvcWrap64<Break>, "Break"},
|
||||
{0x27, SvcWrap64<OutputDebugString>, "OutputDebugString"},
|
||||
{0x28, nullptr, "ReturnFromException"},
|
||||
{0x29, SvcWrap<GetInfo>, "GetInfo"},
|
||||
{0x29, SvcWrap64<GetInfo>, "GetInfo"},
|
||||
{0x2A, nullptr, "FlushEntireDataCache"},
|
||||
{0x2B, nullptr, "FlushDataCache"},
|
||||
{0x2C, SvcWrap<MapPhysicalMemory>, "MapPhysicalMemory"},
|
||||
{0x2D, SvcWrap<UnmapPhysicalMemory>, "UnmapPhysicalMemory"},
|
||||
{0x2C, SvcWrap64<MapPhysicalMemory>, "MapPhysicalMemory"},
|
||||
{0x2D, SvcWrap64<UnmapPhysicalMemory>, "UnmapPhysicalMemory"},
|
||||
{0x2E, nullptr, "GetFutureThreadInfo"},
|
||||
{0x2F, nullptr, "GetLastThreadInfo"},
|
||||
{0x30, SvcWrap<GetResourceLimitLimitValue>, "GetResourceLimitLimitValue"},
|
||||
{0x31, SvcWrap<GetResourceLimitCurrentValue>, "GetResourceLimitCurrentValue"},
|
||||
{0x32, SvcWrap<SetThreadActivity>, "SetThreadActivity"},
|
||||
{0x33, SvcWrap<GetThreadContext>, "GetThreadContext"},
|
||||
{0x34, SvcWrap<WaitForAddress>, "WaitForAddress"},
|
||||
{0x35, SvcWrap<SignalToAddress>, "SignalToAddress"},
|
||||
{0x30, SvcWrap64<GetResourceLimitLimitValue>, "GetResourceLimitLimitValue"},
|
||||
{0x31, SvcWrap64<GetResourceLimitCurrentValue>, "GetResourceLimitCurrentValue"},
|
||||
{0x32, SvcWrap64<SetThreadActivity>, "SetThreadActivity"},
|
||||
{0x33, SvcWrap64<GetThreadContext>, "GetThreadContext"},
|
||||
{0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"},
|
||||
{0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"},
|
||||
{0x36, nullptr, "SynchronizePreemptionState"},
|
||||
{0x37, nullptr, "Unknown"},
|
||||
{0x38, nullptr, "Unknown"},
|
||||
{0x39, nullptr, "Unknown"},
|
||||
{0x3A, nullptr, "Unknown"},
|
||||
{0x3B, nullptr, "Unknown"},
|
||||
{0x3C, SvcWrap<KernelDebug>, "KernelDebug"},
|
||||
{0x3D, SvcWrap<ChangeKernelTraceState>, "ChangeKernelTraceState"},
|
||||
{0x3C, SvcWrap64<KernelDebug>, "KernelDebug"},
|
||||
{0x3D, SvcWrap64<ChangeKernelTraceState>, "ChangeKernelTraceState"},
|
||||
{0x3E, nullptr, "Unknown"},
|
||||
{0x3F, nullptr, "Unknown"},
|
||||
{0x40, nullptr, "CreateSession"},
|
||||
@@ -2387,7 +2577,7 @@ static const FunctionDef SVC_Table[] = {
|
||||
{0x42, nullptr, "ReplyAndReceiveLight"},
|
||||
{0x43, nullptr, "ReplyAndReceive"},
|
||||
{0x44, nullptr, "ReplyAndReceiveWithUserBuffer"},
|
||||
{0x45, SvcWrap<CreateEvent>, "CreateEvent"},
|
||||
{0x45, SvcWrap64<CreateEvent>, "CreateEvent"},
|
||||
{0x46, nullptr, "Unknown"},
|
||||
{0x47, nullptr, "Unknown"},
|
||||
{0x48, nullptr, "MapPhysicalMemoryUnsafe"},
|
||||
@@ -2398,9 +2588,9 @@ static const FunctionDef SVC_Table[] = {
|
||||
{0x4D, nullptr, "SleepSystem"},
|
||||
{0x4E, nullptr, "ReadWriteRegister"},
|
||||
{0x4F, nullptr, "SetProcessActivity"},
|
||||
{0x50, SvcWrap<CreateSharedMemory>, "CreateSharedMemory"},
|
||||
{0x51, SvcWrap<MapTransferMemory>, "MapTransferMemory"},
|
||||
{0x52, SvcWrap<UnmapTransferMemory>, "UnmapTransferMemory"},
|
||||
{0x50, SvcWrap64<CreateSharedMemory>, "CreateSharedMemory"},
|
||||
{0x51, SvcWrap64<MapTransferMemory>, "MapTransferMemory"},
|
||||
{0x52, SvcWrap64<UnmapTransferMemory>, "UnmapTransferMemory"},
|
||||
{0x53, nullptr, "CreateInterruptEvent"},
|
||||
{0x54, nullptr, "QueryPhysicalAddress"},
|
||||
{0x55, nullptr, "QueryIoMapping"},
|
||||
@@ -2419,8 +2609,8 @@ static const FunctionDef SVC_Table[] = {
|
||||
{0x62, nullptr, "TerminateDebugProcess"},
|
||||
{0x63, nullptr, "GetDebugEvent"},
|
||||
{0x64, nullptr, "ContinueDebugEvent"},
|
||||
{0x65, SvcWrap<GetProcessList>, "GetProcessList"},
|
||||
{0x66, SvcWrap<GetThreadList>, "GetThreadList"},
|
||||
{0x65, SvcWrap64<GetProcessList>, "GetProcessList"},
|
||||
{0x66, SvcWrap64<GetThreadList>, "GetThreadList"},
|
||||
{0x67, nullptr, "GetDebugThreadContext"},
|
||||
{0x68, nullptr, "SetDebugThreadContext"},
|
||||
{0x69, nullptr, "QueryDebugProcessMemory"},
|
||||
@@ -2436,24 +2626,32 @@ static const FunctionDef SVC_Table[] = {
|
||||
{0x73, nullptr, "SetProcessMemoryPermission"},
|
||||
{0x74, nullptr, "MapProcessMemory"},
|
||||
{0x75, nullptr, "UnmapProcessMemory"},
|
||||
{0x76, SvcWrap<QueryProcessMemory>, "QueryProcessMemory"},
|
||||
{0x77, SvcWrap<MapProcessCodeMemory>, "MapProcessCodeMemory"},
|
||||
{0x78, SvcWrap<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"},
|
||||
{0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"},
|
||||
{0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"},
|
||||
{0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"},
|
||||
{0x79, nullptr, "CreateProcess"},
|
||||
{0x7A, nullptr, "StartProcess"},
|
||||
{0x7B, nullptr, "TerminateProcess"},
|
||||
{0x7C, SvcWrap<GetProcessInfo>, "GetProcessInfo"},
|
||||
{0x7D, SvcWrap<CreateResourceLimit>, "CreateResourceLimit"},
|
||||
{0x7E, SvcWrap<SetResourceLimitLimitValue>, "SetResourceLimitLimitValue"},
|
||||
{0x7C, SvcWrap64<GetProcessInfo>, "GetProcessInfo"},
|
||||
{0x7D, SvcWrap64<CreateResourceLimit>, "CreateResourceLimit"},
|
||||
{0x7E, SvcWrap64<SetResourceLimitLimitValue>, "SetResourceLimitLimitValue"},
|
||||
{0x7F, nullptr, "CallSecureMonitor"},
|
||||
};
|
||||
|
||||
static const FunctionDef* GetSVCInfo(u32 func_num) {
|
||||
if (func_num >= std::size(SVC_Table)) {
|
||||
static const FunctionDef* GetSVCInfo32(u32 func_num) {
|
||||
if (func_num >= std::size(SVC_Table_32)) {
|
||||
LOG_ERROR(Kernel_SVC, "Unknown svc=0x{:02X}", func_num);
|
||||
return nullptr;
|
||||
}
|
||||
return &SVC_Table[func_num];
|
||||
return &SVC_Table_32[func_num];
|
||||
}
|
||||
|
||||
static const FunctionDef* GetSVCInfo64(u32 func_num) {
|
||||
if (func_num >= std::size(SVC_Table_64)) {
|
||||
LOG_ERROR(Kernel_SVC, "Unknown svc=0x{:02X}", func_num);
|
||||
return nullptr;
|
||||
}
|
||||
return &SVC_Table_64[func_num];
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70));
|
||||
@@ -2464,7 +2662,8 @@ void CallSVC(Core::System& system, u32 immediate) {
|
||||
// Lock the global kernel mutex when we enter the kernel HLE.
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
|
||||
const FunctionDef* info = GetSVCInfo(immediate);
|
||||
const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate)
|
||||
: GetSVCInfo32(immediate);
|
||||
if (info) {
|
||||
if (info->func) {
|
||||
info->func(system);
|
||||
|
||||
@@ -15,6 +15,10 @@ static inline u64 Param(const Core::System& system, int n) {
|
||||
return system.CurrentArmInterface().GetReg(n);
|
||||
}
|
||||
|
||||
static inline u32 Param32(const Core::System& system, int n) {
|
||||
return static_cast<u32>(system.CurrentArmInterface().GetReg(n));
|
||||
}
|
||||
|
||||
/**
|
||||
* HLE a function return from the current ARM userland process
|
||||
* @param system System context
|
||||
@@ -24,40 +28,44 @@ static inline void FuncReturn(Core::System& system, u64 result) {
|
||||
system.CurrentArmInterface().SetReg(0, result);
|
||||
}
|
||||
|
||||
static inline void FuncReturn32(Core::System& system, u32 result) {
|
||||
system.CurrentArmInterface().SetReg(0, (u64)result);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Function wrappers that return type ResultCode
|
||||
|
||||
template <ResultCode func(Core::System&, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(system, func(system, Param(system, 0)).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u64, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(system, func(system, Param(system, 0), Param(system, 1)).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u32, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(
|
||||
system,
|
||||
func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u32, u64, u64, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
|
||||
Param(system, 2), Param(system, 3))
|
||||
.raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u32*)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
u32 param = 0;
|
||||
const u32 retval = func(system, ¶m).raw;
|
||||
system.CurrentArmInterface().SetReg(1, param);
|
||||
@@ -65,7 +73,7 @@ void SvcWrap(Core::System& system) {
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u32*, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
u32 param_1 = 0;
|
||||
const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1))).raw;
|
||||
system.CurrentArmInterface().SetReg(1, param_1);
|
||||
@@ -73,7 +81,7 @@ void SvcWrap(Core::System& system) {
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u32*, u32*)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
u32 param_1 = 0;
|
||||
u32 param_2 = 0;
|
||||
const u32 retval = func(system, ¶m_1, ¶m_2).raw;
|
||||
@@ -86,7 +94,7 @@ void SvcWrap(Core::System& system) {
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u32*, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
u32 param_1 = 0;
|
||||
const u32 retval = func(system, ¶m_1, Param(system, 1)).raw;
|
||||
system.CurrentArmInterface().SetReg(1, param_1);
|
||||
@@ -94,7 +102,7 @@ void SvcWrap(Core::System& system) {
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u32*, u64, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
u32 param_1 = 0;
|
||||
const u32 retval =
|
||||
func(system, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2))).raw;
|
||||
@@ -104,7 +112,7 @@ void SvcWrap(Core::System& system) {
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u64*, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
u64 param_1 = 0;
|
||||
const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1))).raw;
|
||||
|
||||
@@ -113,12 +121,12 @@ void SvcWrap(Core::System& system) {
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u64, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1))).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u64*, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
u64 param_1 = 0;
|
||||
const u32 retval = func(system, ¶m_1, Param(system, 1)).raw;
|
||||
|
||||
@@ -127,7 +135,7 @@ void SvcWrap(Core::System& system) {
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u64*, u32, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
u64 param_1 = 0;
|
||||
const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1)),
|
||||
static_cast<u32>(Param(system, 2)))
|
||||
@@ -138,19 +146,19 @@ void SvcWrap(Core::System& system) {
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u32, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1)).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u32, u32, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)),
|
||||
static_cast<u32>(Param(system, 1)), Param(system, 2))
|
||||
.raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u32, u32*, u64*)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
u32 param_1 = 0;
|
||||
u64 param_2 = 0;
|
||||
const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), ¶m_1, ¶m_2);
|
||||
@@ -161,54 +169,54 @@ void SvcWrap(Core::System& system) {
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u64, u64, u32, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
|
||||
static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3)))
|
||||
.raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u64, u64, u32, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
|
||||
static_cast<u32>(Param(system, 2)), Param(system, 3))
|
||||
.raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u32, u64, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
|
||||
static_cast<u32>(Param(system, 2)))
|
||||
.raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u64, u64, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(system, func(system, Param(system, 0), Param(system, 1), Param(system, 2)).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u64, u64, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(
|
||||
system,
|
||||
func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2))).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u32, u64, u64, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
|
||||
Param(system, 2), static_cast<u32>(Param(system, 3)))
|
||||
.raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u32, u64, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(
|
||||
system,
|
||||
func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u32*, u64, u64, s64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
u32 param_1 = 0;
|
||||
const u32 retval = func(system, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2)),
|
||||
static_cast<s64>(Param(system, 3)))
|
||||
@@ -219,14 +227,14 @@ void SvcWrap(Core::System& system) {
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u64, u64, u32, s64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
|
||||
static_cast<u32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
|
||||
.raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u64*, u64, u64, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
u64 param_1 = 0;
|
||||
const u32 retval =
|
||||
func(system, ¶m_1, Param(system, 1), Param(system, 2), Param(system, 3)).raw;
|
||||
@@ -236,7 +244,7 @@ void SvcWrap(Core::System& system) {
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u32*, u64, u64, u64, u32, s32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
u32 param_1 = 0;
|
||||
const u32 retval = func(system, ¶m_1, Param(system, 1), Param(system, 2), Param(system, 3),
|
||||
static_cast<u32>(Param(system, 4)), static_cast<s32>(Param(system, 5)))
|
||||
@@ -247,7 +255,7 @@ void SvcWrap(Core::System& system) {
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u32*, u64, u64, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
u32 param_1 = 0;
|
||||
const u32 retval = func(system, ¶m_1, Param(system, 1), Param(system, 2),
|
||||
static_cast<u32>(Param(system, 3)))
|
||||
@@ -258,7 +266,7 @@ void SvcWrap(Core::System& system) {
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, Handle*, u64, u32, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
u32 param_1 = 0;
|
||||
const u32 retval = func(system, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2)),
|
||||
static_cast<u32>(Param(system, 3)))
|
||||
@@ -269,14 +277,14 @@ void SvcWrap(Core::System& system) {
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u64, u32, s32, s64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
|
||||
static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
|
||||
.raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u64, u32, s32, s32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
|
||||
static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3)))
|
||||
.raw);
|
||||
@@ -286,7 +294,7 @@ void SvcWrap(Core::System& system) {
|
||||
// Function wrappers that return type u32
|
||||
|
||||
template <u32 func(Core::System&)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(system, func(system));
|
||||
}
|
||||
|
||||
@@ -294,7 +302,7 @@ void SvcWrap(Core::System& system) {
|
||||
// Function wrappers that return type u64
|
||||
|
||||
template <u64 func(Core::System&)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(system, func(system));
|
||||
}
|
||||
|
||||
@@ -302,44 +310,110 @@ void SvcWrap(Core::System& system) {
|
||||
/// Function wrappers that return type void
|
||||
|
||||
template <void func(Core::System&)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
func(system);
|
||||
}
|
||||
|
||||
template <void func(Core::System&, u32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
func(system, static_cast<u32>(Param(system, 0)));
|
||||
}
|
||||
|
||||
template <void func(Core::System&, u32, u64, u64, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2),
|
||||
Param(system, 3));
|
||||
}
|
||||
|
||||
template <void func(Core::System&, s64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
func(system, static_cast<s64>(Param(system, 0)));
|
||||
}
|
||||
|
||||
template <void func(Core::System&, u64, s32)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
func(system, Param(system, 0), static_cast<s32>(Param(system, 1)));
|
||||
}
|
||||
|
||||
template <void func(Core::System&, u64, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
func(system, Param(system, 0), Param(system, 1));
|
||||
}
|
||||
|
||||
template <void func(Core::System&, u64, u64, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
func(system, Param(system, 0), Param(system, 1), Param(system, 2));
|
||||
}
|
||||
|
||||
template <void func(Core::System&, u32, u64, u64)>
|
||||
void SvcWrap(Core::System& system) {
|
||||
void SvcWrap64(Core::System& system) {
|
||||
func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2));
|
||||
}
|
||||
|
||||
// Used by QueryMemory32
|
||||
template <ResultCode func(Core::System&, u32, u32, u32)>
|
||||
void SvcWrap32(Core::System& system) {
|
||||
FuncReturn32(system,
|
||||
func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2)).raw);
|
||||
}
|
||||
|
||||
// Used by GetInfo32
|
||||
template <ResultCode func(Core::System&, u32*, u32*, u32, u32, u32, u32)>
|
||||
void SvcWrap32(Core::System& system) {
|
||||
u32 param_1 = 0;
|
||||
u32 param_2 = 0;
|
||||
|
||||
const u32 retval = func(system, ¶m_1, ¶m_2, Param32(system, 0), Param32(system, 1),
|
||||
Param32(system, 2), Param32(system, 3))
|
||||
.raw;
|
||||
|
||||
system.CurrentArmInterface().SetReg(1, param_1);
|
||||
system.CurrentArmInterface().SetReg(2, param_2);
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
// Used by GetThreadPriority32, ConnectToNamedPort32
|
||||
template <ResultCode func(Core::System&, u32*, u32)>
|
||||
void SvcWrap32(Core::System& system) {
|
||||
u32 param_1 = 0;
|
||||
const u32 retval = func(system, ¶m_1, Param32(system, 1)).raw;
|
||||
system.CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
// Used by GetThreadId32
|
||||
template <ResultCode func(Core::System&, u32*, u32*, u32)>
|
||||
void SvcWrap32(Core::System& system) {
|
||||
u32 param_1 = 0;
|
||||
u32 param_2 = 0;
|
||||
|
||||
const u32 retval = func(system, ¶m_1, ¶m_2, Param32(system, 1)).raw;
|
||||
system.CurrentArmInterface().SetReg(1, param_1);
|
||||
system.CurrentArmInterface().SetReg(2, param_2);
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
// Used by SignalProcessWideKey32
|
||||
template <void func(Core::System&, u32, s32)>
|
||||
void SvcWrap32(Core::System& system) {
|
||||
func(system, static_cast<u32>(Param(system, 0)), static_cast<s32>(Param(system, 1)));
|
||||
}
|
||||
|
||||
// Used by SendSyncRequest32
|
||||
template <ResultCode func(Core::System&, u32)>
|
||||
void SvcWrap32(Core::System& system) {
|
||||
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
|
||||
}
|
||||
|
||||
// Used by WaitSynchronization32
|
||||
template <ResultCode func(Core::System&, u32, u32, s32, u32, Handle*)>
|
||||
void SvcWrap32(Core::System& system) {
|
||||
u32 param_1 = 0;
|
||||
const u32 retval = func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2),
|
||||
Param32(system, 3), ¶m_1)
|
||||
.raw;
|
||||
system.CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -46,9 +46,9 @@ Thread::~Thread() = default;
|
||||
void Thread::Stop() {
|
||||
// Cancel any outstanding wakeup events for this thread
|
||||
Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(),
|
||||
callback_handle);
|
||||
kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle);
|
||||
callback_handle = 0;
|
||||
global_handle);
|
||||
kernel.GlobalHandleTable().Close(global_handle);
|
||||
global_handle = 0;
|
||||
SetStatus(ThreadStatus::Dead);
|
||||
Signal();
|
||||
|
||||
@@ -73,12 +73,12 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
|
||||
// thread-safe version of ScheduleEvent.
|
||||
const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds});
|
||||
Core::System::GetInstance().CoreTiming().ScheduleEvent(
|
||||
cycles, kernel.ThreadWakeupCallbackEventType(), callback_handle);
|
||||
cycles, kernel.ThreadWakeupCallbackEventType(), global_handle);
|
||||
}
|
||||
|
||||
void Thread::CancelWakeupTimer() {
|
||||
Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(),
|
||||
callback_handle);
|
||||
global_handle);
|
||||
}
|
||||
|
||||
void Thread::ResumeFromWait() {
|
||||
@@ -133,15 +133,16 @@ void Thread::CancelWait() {
|
||||
ResumeFromWait();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets a thread context, making it ready to be scheduled and run by the CPU
|
||||
* @param context Thread context to reset
|
||||
* @param stack_top Address of the top of the stack
|
||||
* @param entry_point Address of entry point for execution
|
||||
* @param arg User argument for thread
|
||||
*/
|
||||
static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAddr stack_top,
|
||||
VAddr entry_point, u64 arg) {
|
||||
static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top,
|
||||
u32 entry_point, u32 arg) {
|
||||
context = {};
|
||||
context.cpu_registers[0] = arg;
|
||||
context.cpu_registers[15] = entry_point;
|
||||
context.cpu_registers[13] = stack_top;
|
||||
}
|
||||
|
||||
static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context, VAddr stack_top,
|
||||
VAddr entry_point, u64 arg) {
|
||||
context = {};
|
||||
context.cpu_registers[0] = arg;
|
||||
context.pc = entry_point;
|
||||
@@ -190,7 +191,7 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::strin
|
||||
thread->condvar_wait_address = 0;
|
||||
thread->wait_handle = 0;
|
||||
thread->name = std::move(name);
|
||||
thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
|
||||
thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap();
|
||||
thread->owner_process = &owner_process;
|
||||
auto& scheduler = kernel.GlobalScheduler();
|
||||
scheduler.AddThread(thread);
|
||||
@@ -198,9 +199,9 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::strin
|
||||
|
||||
thread->owner_process->RegisterThread(thread.get());
|
||||
|
||||
// TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
|
||||
// to initialize the context
|
||||
ResetThreadContext(thread->context, stack_top, entry_point, arg);
|
||||
ResetThreadContext32(thread->context_32, static_cast<u32>(stack_top),
|
||||
static_cast<u32>(entry_point), static_cast<u32>(arg));
|
||||
ResetThreadContext64(thread->context_64, stack_top, entry_point, arg);
|
||||
|
||||
return MakeResult<std::shared_ptr<Thread>>(std::move(thread));
|
||||
}
|
||||
@@ -213,11 +214,13 @@ void Thread::SetPriority(u32 priority) {
|
||||
}
|
||||
|
||||
void Thread::SetWaitSynchronizationResult(ResultCode result) {
|
||||
context.cpu_registers[0] = result.raw;
|
||||
context_32.cpu_registers[0] = result.raw;
|
||||
context_64.cpu_registers[0] = result.raw;
|
||||
}
|
||||
|
||||
void Thread::SetWaitSynchronizationOutput(s32 output) {
|
||||
context.cpu_registers[1] = output;
|
||||
context_32.cpu_registers[1] = output;
|
||||
context_64.cpu_registers[1] = output;
|
||||
}
|
||||
|
||||
s32 Thread::GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const {
|
||||
|
||||
@@ -102,7 +102,8 @@ public:
|
||||
|
||||
using MutexWaitingThreads = std::vector<std::shared_ptr<Thread>>;
|
||||
|
||||
using ThreadContext = Core::ARM_Interface::ThreadContext;
|
||||
using ThreadContext32 = Core::ARM_Interface::ThreadContext32;
|
||||
using ThreadContext64 = Core::ARM_Interface::ThreadContext64;
|
||||
|
||||
using ThreadSynchronizationObjects = std::vector<std::shared_ptr<SynchronizationObject>>;
|
||||
|
||||
@@ -273,12 +274,20 @@ public:
|
||||
return status == ThreadStatus::WaitSynch;
|
||||
}
|
||||
|
||||
ThreadContext& GetContext() {
|
||||
return context;
|
||||
ThreadContext32& GetContext32() {
|
||||
return context_32;
|
||||
}
|
||||
|
||||
const ThreadContext& GetContext() const {
|
||||
return context;
|
||||
const ThreadContext32& GetContext32() const {
|
||||
return context_32;
|
||||
}
|
||||
|
||||
ThreadContext64& GetContext64() {
|
||||
return context_64;
|
||||
}
|
||||
|
||||
const ThreadContext64& GetContext64() const {
|
||||
return context_64;
|
||||
}
|
||||
|
||||
ThreadStatus GetStatus() const {
|
||||
@@ -453,6 +462,10 @@ public:
|
||||
is_sync_cancelled = value;
|
||||
}
|
||||
|
||||
Handle GetGlobalHandle() const {
|
||||
return global_handle;
|
||||
}
|
||||
|
||||
private:
|
||||
void SetSchedulingStatus(ThreadSchedStatus new_status);
|
||||
void SetCurrentPriority(u32 new_priority);
|
||||
@@ -462,7 +475,8 @@ private:
|
||||
void AdjustSchedulingOnPriority(u32 old_priority);
|
||||
void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core);
|
||||
|
||||
Core::ARM_Interface::ThreadContext context{};
|
||||
ThreadContext32 context_32{};
|
||||
ThreadContext64 context_64{};
|
||||
|
||||
u64 thread_id = 0;
|
||||
|
||||
@@ -514,7 +528,7 @@ private:
|
||||
VAddr arb_wait_address{0};
|
||||
|
||||
/// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
|
||||
Handle callback_handle = 0;
|
||||
Handle global_handle = 0;
|
||||
|
||||
/// Callback that will be invoked when the thread is resumed from a waiting state. If the thread
|
||||
/// was waiting via WaitSynchronization then the object will be the last object that became
|
||||
|
||||
44
src/core/hle/kernel/time_manager.cpp
Normal file
44
src/core/hle/kernel/time_manager.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
TimeManager::TimeManager(Core::System& system) : system{system} {
|
||||
time_manager_event_type = Core::Timing::CreateEvent(
|
||||
"Kernel::TimeManagerCallback", [this](u64 thread_handle, [[maybe_unused]] s64 cycles_late) {
|
||||
Handle proper_handle = static_cast<Handle>(thread_handle);
|
||||
std::shared_ptr<Thread> thread =
|
||||
this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle);
|
||||
thread->ResumeFromWait();
|
||||
});
|
||||
}
|
||||
|
||||
void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds) {
|
||||
if (nanoseconds > 0) {
|
||||
ASSERT(timetask);
|
||||
event_handle = timetask->GetGlobalHandle();
|
||||
const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds});
|
||||
system.CoreTiming().ScheduleEvent(cycles, time_manager_event_type, event_handle);
|
||||
} else {
|
||||
event_handle = InvalidHandle;
|
||||
}
|
||||
}
|
||||
|
||||
void TimeManager::UnscheduleTimeEvent(Handle event_handle) {
|
||||
if (event_handle == InvalidHandle) {
|
||||
return;
|
||||
}
|
||||
system.CoreTiming().UnscheduleEvent(time_manager_event_type, event_handle);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
43
src/core/hle/kernel/time_manager.h
Normal file
43
src/core/hle/kernel/time_manager.h
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
} // namespace Core
|
||||
|
||||
namespace Core::Timing {
|
||||
struct EventType;
|
||||
} // namespace Core::Timing
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class Thread;
|
||||
|
||||
/**
|
||||
* The `TimeManager` takes care of scheduling time events on threads and executes their TimeUp
|
||||
* method when the event is triggered.
|
||||
*/
|
||||
class TimeManager {
|
||||
public:
|
||||
explicit TimeManager(Core::System& system);
|
||||
|
||||
/// Schedule a time event on `timetask` thread that will expire in 'nanoseconds'
|
||||
/// returns a non-invalid handle in `event_handle` if correctly scheduled
|
||||
void ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds);
|
||||
|
||||
/// Unschedule an existing time event
|
||||
void UnscheduleTimeEvent(Handle event_handle);
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
std::shared_ptr<Core::Timing::EventType> time_manager_event_type;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -52,6 +52,11 @@ enum class LaunchParameterKind : u32 {
|
||||
AccountPreselectedUser = 2,
|
||||
};
|
||||
|
||||
enum class VrMode : u8 {
|
||||
Disabled = 0,
|
||||
Enabled = 1,
|
||||
};
|
||||
|
||||
constexpr u32 LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC = 0xC79497CA;
|
||||
|
||||
struct LaunchParameterAccountPreselectedUser {
|
||||
@@ -605,11 +610,11 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system,
|
||||
{30, nullptr, "GetHomeButtonReaderLockAccessor"},
|
||||
{31, nullptr, "GetReaderLockAccessorEx"},
|
||||
{40, nullptr, "GetCradleFwVersion"},
|
||||
{50, nullptr, "IsVrModeEnabled"},
|
||||
{51, nullptr, "SetVrModeEnabled"},
|
||||
{52, nullptr, "SwitchLcdBacklight"},
|
||||
{50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"},
|
||||
{51, &ICommonStateGetter::SetVrModeEnabled, "SetVrModeEnabled"},
|
||||
{52, &ICommonStateGetter::SetLcdBacklighOffEnabled, "SetLcdBacklighOffEnabled"},
|
||||
{53, nullptr, "BeginVrModeEx"},
|
||||
{54, nullptr, "EndVrModeEx"},
|
||||
{54, &ICommonStateGetter::EndVrModeEx, "EndVrModeEx"},
|
||||
{55, nullptr, "IsInControllerFirmwareUpdateSection"},
|
||||
{60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"},
|
||||
{61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"},
|
||||
@@ -636,7 +641,6 @@ void ICommonStateGetter::GetBootMode(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
rb.Push<u8>(static_cast<u8>(Service::PM::SystemBootMode::Normal)); // Normal boot mode
|
||||
}
|
||||
|
||||
@@ -660,6 +664,7 @@ void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
|
||||
rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
|
||||
return;
|
||||
}
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
|
||||
}
|
||||
@@ -672,6 +677,48 @@ void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(static_cast<u8>(FocusState::InFocus));
|
||||
}
|
||||
|
||||
void ICommonStateGetter::IsVrModeEnabled(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushEnum(VrMode::Disabled);
|
||||
}
|
||||
|
||||
void ICommonStateGetter::SetVrModeEnabled(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto is_vr_mode_enabled = rp.Pop<bool>();
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called. is_vr_mode_enabled={}", is_vr_mode_enabled);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
if (!is_vr_mode_enabled) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
} else {
|
||||
// TODO: Find better error code for this
|
||||
UNIMPLEMENTED_MSG("is_vr_mode_enabled={}", is_vr_mode_enabled);
|
||||
rb.Push(RESULT_UNKNOWN);
|
||||
}
|
||||
}
|
||||
|
||||
void ICommonStateGetter::SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto is_lcd_backlight_off_enabled = rp.Pop<bool>();
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called. is_lcd_backlight_off_enabled={}",
|
||||
is_lcd_backlight_off_enabled);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ICommonStateGetter::EndVrModeEx(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
|
||||
@@ -182,6 +182,10 @@ private:
|
||||
void GetOperationMode(Kernel::HLERequestContext& ctx);
|
||||
void GetPerformanceMode(Kernel::HLERequestContext& ctx);
|
||||
void GetBootMode(Kernel::HLERequestContext& ctx);
|
||||
void IsVrModeEnabled(Kernel::HLERequestContext& ctx);
|
||||
void SetVrModeEnabled(Kernel::HLERequestContext& ctx);
|
||||
void SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx);
|
||||
void EndVrModeEx(Kernel::HLERequestContext& ctx);
|
||||
void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx);
|
||||
void SetCpuBoostMode(Kernel::HLERequestContext& ctx);
|
||||
|
||||
|
||||
@@ -254,6 +254,12 @@ void WebBrowser::Execute() {
|
||||
|
||||
if (status != RESULT_SUCCESS) {
|
||||
complete = true;
|
||||
|
||||
// This is a workaround in order not to softlock yuzu when an error happens during the
|
||||
// webapplet init. In order to avoid an svcBreak, the status is set to RESULT_SUCCESS
|
||||
Finalize();
|
||||
status = RESULT_SUCCESS;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -200,7 +200,8 @@ private:
|
||||
DownloadResult DownloadInternal(const std::string& resolved_path, u32 timeout_seconds,
|
||||
const std::string& content_type_name) {
|
||||
if (client == nullptr) {
|
||||
client = std::make_unique<httplib::SSLClient>(BOXCAT_HOSTNAME, PORT, timeout_seconds);
|
||||
client = std::make_unique<httplib::SSLClient>(BOXCAT_HOSTNAME, PORT);
|
||||
client->set_timeout_sec(timeout_seconds);
|
||||
}
|
||||
|
||||
httplib::Headers headers{
|
||||
@@ -448,8 +449,8 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title)
|
||||
|
||||
Boxcat::StatusResult Boxcat::GetStatus(std::optional<std::string>& global,
|
||||
std::map<std::string, EventStatus>& games) {
|
||||
httplib::SSLClient client{BOXCAT_HOSTNAME, static_cast<int>(PORT),
|
||||
static_cast<int>(TIMEOUT_SECONDS)};
|
||||
httplib::SSLClient client{BOXCAT_HOSTNAME, static_cast<int>(PORT)};
|
||||
client.set_timeout_sec(static_cast<int>(TIMEOUT_SECONDS));
|
||||
|
||||
httplib::Headers headers{
|
||||
{std::string("Game-Assets-API-Version"), std::string(BOXCAT_API_VERSION)},
|
||||
|
||||
@@ -235,7 +235,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
|
||||
{303, nullptr, "ActivateSevenSixAxisSensor"},
|
||||
{304, nullptr, "StartSevenSixAxisSensor"},
|
||||
{305, nullptr, "StopSevenSixAxisSensor"},
|
||||
{306, nullptr, "InitializeSevenSixAxisSensor"},
|
||||
{306, &Hid::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"},
|
||||
{307, nullptr, "FinalizeSevenSixAxisSensor"},
|
||||
{308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
|
||||
{309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
|
||||
@@ -853,6 +853,13 @@ void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
class HidDbg final : public ServiceFramework<HidDbg> {
|
||||
public:
|
||||
explicit HidDbg() : ServiceFramework{"hid:dbg"} {
|
||||
|
||||
@@ -128,6 +128,7 @@ private:
|
||||
void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx);
|
||||
void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
|
||||
void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<IAppletResource> applet_resource;
|
||||
Core::System& system;
|
||||
|
||||
@@ -191,8 +191,6 @@ void NVFlinger::Compose() {
|
||||
// Search for a queued buffer and acquire it
|
||||
auto buffer = buffer_queue.AcquireBuffer();
|
||||
|
||||
MicroProfileFlip();
|
||||
|
||||
if (!buffer) {
|
||||
continue;
|
||||
}
|
||||
@@ -206,6 +204,8 @@ void NVFlinger::Compose() {
|
||||
gpu.WaitFence(fence.id, fence.value);
|
||||
}
|
||||
|
||||
MicroProfileFlip();
|
||||
|
||||
// Now send the buffer to the GPU for drawing.
|
||||
// TODO(Subv): Support more than just disp0. The display device selection is probably based
|
||||
// on which display we're drawing (Default, Internal, External, etc)
|
||||
|
||||
@@ -111,6 +111,14 @@ void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) {
|
||||
rb.PushEnum(available_language_codes[Settings::values.language_index]);
|
||||
}
|
||||
|
||||
void SET::GetRegionCode(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SET, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(Settings::values.region_index);
|
||||
}
|
||||
|
||||
SET::SET() : ServiceFramework("set") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
@@ -118,7 +126,7 @@ SET::SET() : ServiceFramework("set") {
|
||||
{1, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"},
|
||||
{2, &SET::MakeLanguageCode, "MakeLanguageCode"},
|
||||
{3, &SET::GetAvailableLanguageCodeCount, "GetAvailableLanguageCodeCount"},
|
||||
{4, nullptr, "GetRegionCode"},
|
||||
{4, &SET::GetRegionCode, "GetRegionCode"},
|
||||
{5, &SET::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"},
|
||||
{6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"},
|
||||
{7, nullptr, "GetKeyCodeMap"},
|
||||
|
||||
@@ -43,6 +43,7 @@ private:
|
||||
void GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx);
|
||||
void GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx);
|
||||
void GetQuestFlag(Kernel::HLERequestContext& ctx);
|
||||
void GetRegionCode(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
} // namespace Service::Set
|
||||
|
||||
@@ -44,7 +44,7 @@ void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u16>(0x500);
|
||||
rb.Push<u16>(0x1000);
|
||||
}
|
||||
|
||||
Controller::Controller() : ServiceFramework("IpcController") {
|
||||
|
||||
@@ -53,7 +53,7 @@ static std::vector<std::string> BuildLocationNameCache(Core::System& system) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<char> raw_data(binary_list->GetSize());
|
||||
std::vector<char> raw_data(binary_list->GetSize() + 1);
|
||||
binary_list->ReadBytes<char>(raw_data.data(), binary_list->GetSize());
|
||||
|
||||
std::stringstream data_stream{raw_data.data()};
|
||||
|
||||
@@ -129,12 +129,6 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
|
||||
}
|
||||
metadata.Print();
|
||||
|
||||
const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
|
||||
if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit ||
|
||||
arch_bits == FileSys::ProgramAddressSpaceType::Is32BitNoMap) {
|
||||
return {ResultStatus::Error32BitISA, {}};
|
||||
}
|
||||
|
||||
if (process.LoadFromMetadata(metadata).IsError()) {
|
||||
return {ResultStatus::ErrorUnableToParseKernelMetadata, {}};
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ json GetProcessorStateDataAuto(Core::System& system) {
|
||||
const auto& vm_manager{process->VMManager()};
|
||||
auto& arm{system.CurrentArmInterface()};
|
||||
|
||||
Core::ARM_Interface::ThreadContext context{};
|
||||
Core::ARM_Interface::ThreadContext64 context{};
|
||||
arm.SaveContext(context);
|
||||
|
||||
return GetProcessorStateData(process->Is64BitProcess() ? "AArch64" : "AArch32",
|
||||
|
||||
@@ -86,6 +86,7 @@ void LogSettings() {
|
||||
LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0));
|
||||
LogSetting("System_CurrentUser", Settings::values.current_user);
|
||||
LogSetting("System_LanguageIndex", Settings::values.language_index);
|
||||
LogSetting("System_RegionIndex", Settings::values.region_index);
|
||||
LogSetting("Core_UseMultiCore", Settings::values.use_multi_core);
|
||||
LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor);
|
||||
LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit);
|
||||
@@ -94,6 +95,7 @@ void LogSettings() {
|
||||
LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation);
|
||||
LogSetting("Renderer_UseAsynchronousGpuEmulation",
|
||||
Settings::values.use_asynchronous_gpu_emulation);
|
||||
LogSetting("Renderer_UseVsync", Settings::values.use_vsync);
|
||||
LogSetting("Audio_OutputEngine", Settings::values.sink_id);
|
||||
LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
|
||||
LogSetting("Audio_OutputDevice", Settings::values.audio_device_id);
|
||||
|
||||
@@ -387,6 +387,8 @@ struct Values {
|
||||
|
||||
s32 current_user;
|
||||
s32 language_index;
|
||||
s32 region_index;
|
||||
s32 sound_index;
|
||||
|
||||
// Controls
|
||||
std::array<PlayerInput, 10> players;
|
||||
@@ -430,11 +432,13 @@ struct Values {
|
||||
|
||||
float resolution_factor;
|
||||
int aspect_ratio;
|
||||
int max_anisotropy;
|
||||
bool use_frame_limit;
|
||||
u16 frame_limit;
|
||||
bool use_disk_shader_cache;
|
||||
bool use_accurate_gpu_emulation;
|
||||
bool use_asynchronous_gpu_emulation;
|
||||
bool use_vsync;
|
||||
bool force_30fps_mode;
|
||||
|
||||
float bg_red;
|
||||
|
||||
@@ -188,6 +188,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
|
||||
Settings::values.use_accurate_gpu_emulation);
|
||||
AddField(field_type, "Renderer_UseAsynchronousGpuEmulation",
|
||||
Settings::values.use_asynchronous_gpu_emulation);
|
||||
AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync);
|
||||
AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,8 +32,16 @@ public:
|
||||
SocketCallback callback)
|
||||
: callback(std::move(callback)), timer(io_service),
|
||||
socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id),
|
||||
pad_index(pad_index),
|
||||
send_endpoint(udp::endpoint(boost::asio::ip::make_address_v4(host), port)) {}
|
||||
pad_index(pad_index) {
|
||||
boost::system::error_code ec{};
|
||||
auto ipv4 = boost::asio::ip::make_address_v4(host, ec);
|
||||
if (ec.value() != boost::system::errc::success) {
|
||||
LOG_ERROR(Input, "Invalid IPv4 address \"{}\" provided to socket", host);
|
||||
ipv4 = boost::asio::ip::address_v4{};
|
||||
}
|
||||
|
||||
send_endpoint = {udp::endpoint(ipv4, port)};
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
io_service.stop();
|
||||
@@ -85,17 +93,18 @@ private:
|
||||
}
|
||||
|
||||
void HandleSend(const boost::system::error_code& error) {
|
||||
boost::system::error_code _ignored{};
|
||||
// Send a request for getting port info for the pad
|
||||
Request::PortInfo port_info{1, {pad_index, 0, 0, 0}};
|
||||
const auto port_message = Request::Create(port_info, client_id);
|
||||
std::memcpy(&send_buffer1, &port_message, PORT_INFO_SIZE);
|
||||
socket.send_to(boost::asio::buffer(send_buffer1), send_endpoint);
|
||||
socket.send_to(boost::asio::buffer(send_buffer1), send_endpoint, {}, _ignored);
|
||||
|
||||
// Send a request for getting pad data for the pad
|
||||
Request::PadData pad_data{Request::PadData::Flags::Id, pad_index, EMPTY_MAC_ADDRESS};
|
||||
const auto pad_message = Request::Create(pad_data, client_id);
|
||||
std::memcpy(send_buffer2.data(), &pad_message, PAD_DATA_SIZE);
|
||||
socket.send_to(boost::asio::buffer(send_buffer2), send_endpoint);
|
||||
socket.send_to(boost::asio::buffer(send_buffer2), send_endpoint, {}, _ignored);
|
||||
StartSend(timer.expiry());
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ namespace Response {
|
||||
*/
|
||||
std::optional<Type> Validate(u8* data, std::size_t size) {
|
||||
if (size < sizeof(Header)) {
|
||||
LOG_DEBUG(Input, "Invalid UDP packet received");
|
||||
return std::nullopt;
|
||||
}
|
||||
Header header{};
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
|
||||
#include "common/param_package.h"
|
||||
@@ -44,7 +45,7 @@ public:
|
||||
std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override {
|
||||
{
|
||||
std::lock_guard guard(status->update_mutex);
|
||||
status->touch_calibration.emplace();
|
||||
status->touch_calibration = DeviceStatus::CalibrationData{};
|
||||
// These default values work well for DS4 but probably not other touch inputs
|
||||
status->touch_calibration->min_x = params.Get("min_x", 100);
|
||||
status->touch_calibration->min_y = params.Get("min_y", 50);
|
||||
|
||||
@@ -2,6 +2,8 @@ add_library(video_core STATIC
|
||||
buffer_cache/buffer_block.h
|
||||
buffer_cache/buffer_cache.h
|
||||
buffer_cache/map_interval.h
|
||||
dirty_flags.cpp
|
||||
dirty_flags.h
|
||||
dma_pusher.cpp
|
||||
dma_pusher.h
|
||||
engines/const_buffer_engine_interface.h
|
||||
@@ -37,6 +39,7 @@ add_library(video_core STATIC
|
||||
memory_manager.h
|
||||
morton.cpp
|
||||
morton.h
|
||||
query_cache.h
|
||||
rasterizer_accelerated.cpp
|
||||
rasterizer_accelerated.h
|
||||
rasterizer_cache.cpp
|
||||
@@ -62,18 +65,18 @@ add_library(video_core STATIC
|
||||
renderer_opengl/gl_shader_decompiler.h
|
||||
renderer_opengl/gl_shader_disk_cache.cpp
|
||||
renderer_opengl/gl_shader_disk_cache.h
|
||||
renderer_opengl/gl_shader_gen.cpp
|
||||
renderer_opengl/gl_shader_gen.h
|
||||
renderer_opengl/gl_shader_manager.cpp
|
||||
renderer_opengl/gl_shader_manager.h
|
||||
renderer_opengl/gl_shader_util.cpp
|
||||
renderer_opengl/gl_shader_util.h
|
||||
renderer_opengl/gl_state.cpp
|
||||
renderer_opengl/gl_state.h
|
||||
renderer_opengl/gl_state_tracker.cpp
|
||||
renderer_opengl/gl_state_tracker.h
|
||||
renderer_opengl/gl_stream_buffer.cpp
|
||||
renderer_opengl/gl_stream_buffer.h
|
||||
renderer_opengl/gl_texture_cache.cpp
|
||||
renderer_opengl/gl_texture_cache.h
|
||||
renderer_opengl/gl_query_cache.cpp
|
||||
renderer_opengl/gl_query_cache.h
|
||||
renderer_opengl/maxwell_to_gl.h
|
||||
renderer_opengl/renderer_opengl.cpp
|
||||
renderer_opengl/renderer_opengl.h
|
||||
@@ -113,8 +116,6 @@ add_library(video_core STATIC
|
||||
shader/ast.h
|
||||
shader/compiler_settings.cpp
|
||||
shader/compiler_settings.h
|
||||
shader/const_buffer_locker.cpp
|
||||
shader/const_buffer_locker.h
|
||||
shader/control_flow.cpp
|
||||
shader/control_flow.h
|
||||
shader/decode.cpp
|
||||
@@ -123,9 +124,13 @@ add_library(video_core STATIC
|
||||
shader/node_helper.cpp
|
||||
shader/node_helper.h
|
||||
shader/node.h
|
||||
shader/registry.cpp
|
||||
shader/registry.h
|
||||
shader/shader_ir.cpp
|
||||
shader/shader_ir.h
|
||||
shader/track.cpp
|
||||
shader/transform_feedback.cpp
|
||||
shader/transform_feedback.h
|
||||
surface.cpp
|
||||
surface.h
|
||||
texture_cache/format_lookup_table.cpp
|
||||
@@ -177,6 +182,8 @@ if (ENABLE_VULKAN)
|
||||
renderer_vulkan/vk_memory_manager.h
|
||||
renderer_vulkan/vk_pipeline_cache.cpp
|
||||
renderer_vulkan/vk_pipeline_cache.h
|
||||
renderer_vulkan/vk_query_cache.cpp
|
||||
renderer_vulkan/vk_query_cache.h
|
||||
renderer_vulkan/vk_rasterizer.cpp
|
||||
renderer_vulkan/vk_rasterizer.h
|
||||
renderer_vulkan/vk_renderpass_cache.cpp
|
||||
@@ -193,6 +200,8 @@ if (ENABLE_VULKAN)
|
||||
renderer_vulkan/vk_shader_util.h
|
||||
renderer_vulkan/vk_staging_buffer_pool.cpp
|
||||
renderer_vulkan/vk_staging_buffer_pool.h
|
||||
renderer_vulkan/vk_state_tracker.cpp
|
||||
renderer_vulkan/vk_state_tracker.h
|
||||
renderer_vulkan/vk_stream_buffer.cpp
|
||||
renderer_vulkan/vk_stream_buffer.h
|
||||
renderer_vulkan/vk_swapchain.cpp
|
||||
|
||||
38
src/video_core/dirty_flags.cpp
Normal file
38
src/video_core/dirty_flags.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/dirty_flags.h"
|
||||
|
||||
#define OFF(field_name) MAXWELL3D_REG_INDEX(field_name)
|
||||
#define NUM(field_name) (sizeof(::Tegra::Engines::Maxwell3D::Regs::field_name) / sizeof(u32))
|
||||
|
||||
namespace VideoCommon::Dirty {
|
||||
|
||||
using Tegra::Engines::Maxwell3D;
|
||||
|
||||
void SetupDirtyRenderTargets(Tegra::Engines::Maxwell3D::DirtyState::Tables& tables) {
|
||||
static constexpr std::size_t num_per_rt = NUM(rt[0]);
|
||||
static constexpr std::size_t begin = OFF(rt);
|
||||
static constexpr std::size_t num = num_per_rt * Maxwell3D::Regs::NumRenderTargets;
|
||||
for (std::size_t rt = 0; rt < Maxwell3D::Regs::NumRenderTargets; ++rt) {
|
||||
FillBlock(tables[0], begin + rt * num_per_rt, num_per_rt, ColorBuffer0 + rt);
|
||||
}
|
||||
FillBlock(tables[1], begin, num, RenderTargets);
|
||||
|
||||
static constexpr std::array zeta_flags{ZetaBuffer, RenderTargets};
|
||||
for (std::size_t i = 0; i < std::size(zeta_flags); ++i) {
|
||||
const u8 flag = zeta_flags[i];
|
||||
auto& table = tables[i];
|
||||
table[OFF(zeta_enable)] = flag;
|
||||
table[OFF(zeta_width)] = flag;
|
||||
table[OFF(zeta_height)] = flag;
|
||||
FillBlock(table, OFF(zeta), NUM(zeta), flag);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Dirty
|
||||
49
src/video_core/dirty_flags.h
Normal file
49
src/video_core/dirty_flags.h
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
|
||||
namespace VideoCommon::Dirty {
|
||||
|
||||
enum : u8 {
|
||||
NullEntry = 0,
|
||||
|
||||
RenderTargets,
|
||||
ColorBuffer0,
|
||||
ColorBuffer1,
|
||||
ColorBuffer2,
|
||||
ColorBuffer3,
|
||||
ColorBuffer4,
|
||||
ColorBuffer5,
|
||||
ColorBuffer6,
|
||||
ColorBuffer7,
|
||||
ZetaBuffer,
|
||||
|
||||
LastCommonEntry,
|
||||
};
|
||||
|
||||
template <typename Integer>
|
||||
void FillBlock(Tegra::Engines::Maxwell3D::DirtyState::Table& table, std::size_t begin,
|
||||
std::size_t num, Integer dirty_index) {
|
||||
const auto it = std::begin(table) + begin;
|
||||
std::fill(it, it + num, static_cast<u8>(dirty_index));
|
||||
}
|
||||
|
||||
template <typename Integer1, typename Integer2>
|
||||
void FillBlock(Tegra::Engines::Maxwell3D::DirtyState::Tables& tables, std::size_t begin,
|
||||
std::size_t num, Integer1 index_a, Integer2 index_b) {
|
||||
FillBlock(tables[0], begin, num, index_a);
|
||||
FillBlock(tables[1], begin, num, index_b);
|
||||
}
|
||||
|
||||
void SetupDirtyRenderTargets(Tegra::Engines::Maxwell3D::DirtyState::Tables& tables);
|
||||
|
||||
} // namespace VideoCommon::Dirty
|
||||
@@ -22,7 +22,7 @@ void DmaPusher::DispatchCalls() {
|
||||
MICROPROFILE_SCOPE(DispatchCalls);
|
||||
|
||||
// On entering GPU code, assume all memory may be touched by the ARM core.
|
||||
gpu.Maxwell3D().dirty.OnMemoryWrite();
|
||||
gpu.Maxwell3D().OnMemoryWrite();
|
||||
|
||||
dma_pushbuffer_subindex = 0;
|
||||
|
||||
|
||||
@@ -16,11 +16,16 @@ namespace Tegra::Engines {
|
||||
|
||||
struct SamplerDescriptor {
|
||||
union {
|
||||
BitField<0, 20, Tegra::Shader::TextureType> texture_type;
|
||||
BitField<20, 1, u32> is_array;
|
||||
BitField<21, 1, u32> is_buffer;
|
||||
BitField<22, 1, u32> is_shadow;
|
||||
u32 raw{};
|
||||
u32 raw = 0;
|
||||
BitField<0, 2, Tegra::Shader::TextureType> texture_type;
|
||||
BitField<2, 3, Tegra::Texture::ComponentType> r_type;
|
||||
BitField<5, 1, u32> is_array;
|
||||
BitField<6, 1, u32> is_buffer;
|
||||
BitField<7, 1, u32> is_shadow;
|
||||
BitField<8, 3, Tegra::Texture::ComponentType> g_type;
|
||||
BitField<11, 3, Tegra::Texture::ComponentType> b_type;
|
||||
BitField<14, 3, Tegra::Texture::ComponentType> a_type;
|
||||
BitField<17, 7, Tegra::Texture::TextureFormat> format;
|
||||
};
|
||||
|
||||
bool operator==(const SamplerDescriptor& rhs) const noexcept {
|
||||
@@ -31,68 +36,50 @@ struct SamplerDescriptor {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
static SamplerDescriptor FromTicTexture(Tegra::Texture::TextureType tic_texture_type) {
|
||||
static SamplerDescriptor FromTIC(const Tegra::Texture::TICEntry& tic) {
|
||||
using Tegra::Shader::TextureType;
|
||||
SamplerDescriptor result;
|
||||
switch (tic_texture_type) {
|
||||
|
||||
result.format.Assign(tic.format.Value());
|
||||
result.r_type.Assign(tic.r_type.Value());
|
||||
result.g_type.Assign(tic.g_type.Value());
|
||||
result.b_type.Assign(tic.b_type.Value());
|
||||
result.a_type.Assign(tic.a_type.Value());
|
||||
|
||||
switch (tic.texture_type.Value()) {
|
||||
case Tegra::Texture::TextureType::Texture1D:
|
||||
result.texture_type.Assign(Tegra::Shader::TextureType::Texture1D);
|
||||
result.is_array.Assign(0);
|
||||
result.is_buffer.Assign(0);
|
||||
result.is_shadow.Assign(0);
|
||||
result.texture_type.Assign(TextureType::Texture1D);
|
||||
return result;
|
||||
case Tegra::Texture::TextureType::Texture2D:
|
||||
result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D);
|
||||
result.is_array.Assign(0);
|
||||
result.is_buffer.Assign(0);
|
||||
result.is_shadow.Assign(0);
|
||||
result.texture_type.Assign(TextureType::Texture2D);
|
||||
return result;
|
||||
case Tegra::Texture::TextureType::Texture3D:
|
||||
result.texture_type.Assign(Tegra::Shader::TextureType::Texture3D);
|
||||
result.is_array.Assign(0);
|
||||
result.is_buffer.Assign(0);
|
||||
result.is_shadow.Assign(0);
|
||||
result.texture_type.Assign(TextureType::Texture3D);
|
||||
return result;
|
||||
case Tegra::Texture::TextureType::TextureCubemap:
|
||||
result.texture_type.Assign(Tegra::Shader::TextureType::TextureCube);
|
||||
result.is_array.Assign(0);
|
||||
result.is_buffer.Assign(0);
|
||||
result.is_shadow.Assign(0);
|
||||
result.texture_type.Assign(TextureType::TextureCube);
|
||||
return result;
|
||||
case Tegra::Texture::TextureType::Texture1DArray:
|
||||
result.texture_type.Assign(Tegra::Shader::TextureType::Texture1D);
|
||||
result.texture_type.Assign(TextureType::Texture1D);
|
||||
result.is_array.Assign(1);
|
||||
result.is_buffer.Assign(0);
|
||||
result.is_shadow.Assign(0);
|
||||
return result;
|
||||
case Tegra::Texture::TextureType::Texture2DArray:
|
||||
result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D);
|
||||
result.texture_type.Assign(TextureType::Texture2D);
|
||||
result.is_array.Assign(1);
|
||||
result.is_buffer.Assign(0);
|
||||
result.is_shadow.Assign(0);
|
||||
return result;
|
||||
case Tegra::Texture::TextureType::Texture1DBuffer:
|
||||
result.texture_type.Assign(Tegra::Shader::TextureType::Texture1D);
|
||||
result.is_array.Assign(0);
|
||||
result.texture_type.Assign(TextureType::Texture1D);
|
||||
result.is_buffer.Assign(1);
|
||||
result.is_shadow.Assign(0);
|
||||
return result;
|
||||
case Tegra::Texture::TextureType::Texture2DNoMipmap:
|
||||
result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D);
|
||||
result.is_array.Assign(0);
|
||||
result.is_buffer.Assign(0);
|
||||
result.is_shadow.Assign(0);
|
||||
result.texture_type.Assign(TextureType::Texture2D);
|
||||
return result;
|
||||
case Tegra::Texture::TextureType::TextureCubeArray:
|
||||
result.texture_type.Assign(Tegra::Shader::TextureType::TextureCube);
|
||||
result.texture_type.Assign(TextureType::TextureCube);
|
||||
result.is_array.Assign(1);
|
||||
result.is_buffer.Assign(0);
|
||||
result.is_shadow.Assign(0);
|
||||
return result;
|
||||
default:
|
||||
result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D);
|
||||
result.is_array.Assign(0);
|
||||
result.is_buffer.Assign(0);
|
||||
result.is_shadow.Assign(0);
|
||||
result.texture_type.Assign(TextureType::Texture2D);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ void KeplerCompute::CallMethod(const GPU::MethodCall& method_call) {
|
||||
const bool is_last_call = method_call.IsLastCall();
|
||||
upload_state.ProcessData(method_call.argument, is_last_call);
|
||||
if (is_last_call) {
|
||||
system.GPU().Maxwell3D().dirty.OnMemoryWrite();
|
||||
system.GPU().Maxwell3D().OnMemoryWrite();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -89,7 +89,7 @@ SamplerDescriptor KeplerCompute::AccessBindlessSampler(ShaderType stage, u64 con
|
||||
|
||||
const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(tex_info_address)};
|
||||
const Texture::FullTextureInfo tex_info = GetTextureInfo(tex_handle);
|
||||
SamplerDescriptor result = SamplerDescriptor::FromTicTexture(tex_info.tic.texture_type.Value());
|
||||
SamplerDescriptor result = SamplerDescriptor::FromTIC(tex_info.tic);
|
||||
result.is_shadow.Assign(tex_info.tsc.depth_compare_enabled.Value());
|
||||
return result;
|
||||
}
|
||||
@@ -119,14 +119,6 @@ Texture::TICEntry KeplerCompute::GetTICEntry(u32 tic_index) const {
|
||||
Texture::TICEntry tic_entry;
|
||||
memory_manager.ReadBlockUnsafe(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry));
|
||||
|
||||
const auto r_type{tic_entry.r_type.Value()};
|
||||
const auto g_type{tic_entry.g_type.Value()};
|
||||
const auto b_type{tic_entry.b_type.Value()};
|
||||
const auto a_type{tic_entry.a_type.Value()};
|
||||
|
||||
// TODO(Subv): Different data types for separate components are not supported
|
||||
DEBUG_ASSERT(r_type == g_type && r_type == b_type && r_type == a_type);
|
||||
|
||||
return tic_entry;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ void KeplerMemory::CallMethod(const GPU::MethodCall& method_call) {
|
||||
const bool is_last_call = method_call.IsLastCall();
|
||||
upload_state.ProcessData(method_call.argument, is_last_call);
|
||||
if (is_last_call) {
|
||||
system.GPU().Maxwell3D().dirty.OnMemoryWrite();
|
||||
system.GPU().Maxwell3D().OnMemoryWrite();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
@@ -16,6 +17,8 @@
|
||||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
using VideoCore::QueryType;
|
||||
|
||||
/// First register id that is actually a Macro call.
|
||||
constexpr u32 MacroRegistersStart = 0xE00;
|
||||
|
||||
@@ -23,7 +26,8 @@ Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& raste
|
||||
MemoryManager& memory_manager)
|
||||
: system{system}, rasterizer{rasterizer}, memory_manager{memory_manager},
|
||||
macro_interpreter{*this}, upload_state{memory_manager, regs.upload} {
|
||||
InitDirtySettings();
|
||||
dirty.flags.flip();
|
||||
|
||||
InitializeRegisterDefaults();
|
||||
}
|
||||
|
||||
@@ -72,8 +76,8 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
regs.stencil_back_mask = 0xFFFFFFFF;
|
||||
|
||||
regs.depth_test_func = Regs::ComparisonOp::Always;
|
||||
regs.cull.front_face = Regs::Cull::FrontFace::CounterClockWise;
|
||||
regs.cull.cull_face = Regs::Cull::CullFace::Back;
|
||||
regs.front_face = Regs::FrontFace::CounterClockWise;
|
||||
regs.cull_face = Regs::CullFace::Back;
|
||||
|
||||
// TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
|
||||
// register carrying a default value. Assume it's OpenGL's default (1).
|
||||
@@ -92,7 +96,9 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
regs.rasterize_enable = 1;
|
||||
regs.rt_separate_frag_data = 1;
|
||||
regs.framebuffer_srgb = 1;
|
||||
regs.cull.front_face = Maxwell3D::Regs::Cull::FrontFace::ClockWise;
|
||||
regs.front_face = Maxwell3D::Regs::FrontFace::ClockWise;
|
||||
|
||||
shadow_state = regs;
|
||||
|
||||
mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_end_gl)] = true;
|
||||
mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)] = true;
|
||||
@@ -100,164 +106,6 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
mme_inline[MAXWELL3D_REG_INDEX(index_array.count)] = true;
|
||||
}
|
||||
|
||||
#define DIRTY_REGS_POS(field_name) static_cast<u8>(offsetof(Maxwell3D::DirtyRegs, field_name))
|
||||
|
||||
void Maxwell3D::InitDirtySettings() {
|
||||
const auto set_block = [this](std::size_t start, std::size_t range, u8 position) {
|
||||
const auto start_itr = dirty_pointers.begin() + start;
|
||||
const auto end_itr = start_itr + range;
|
||||
std::fill(start_itr, end_itr, position);
|
||||
};
|
||||
dirty.regs.fill(true);
|
||||
|
||||
// Init Render Targets
|
||||
constexpr u32 registers_per_rt = sizeof(regs.rt[0]) / sizeof(u32);
|
||||
constexpr u32 rt_start_reg = MAXWELL3D_REG_INDEX(rt);
|
||||
constexpr u32 rt_end_reg = rt_start_reg + registers_per_rt * 8;
|
||||
u8 rt_dirty_reg = DIRTY_REGS_POS(render_target);
|
||||
for (u32 rt_reg = rt_start_reg; rt_reg < rt_end_reg; rt_reg += registers_per_rt) {
|
||||
set_block(rt_reg, registers_per_rt, rt_dirty_reg);
|
||||
++rt_dirty_reg;
|
||||
}
|
||||
constexpr u32 depth_buffer_flag = DIRTY_REGS_POS(depth_buffer);
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(zeta_enable)] = depth_buffer_flag;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(zeta_width)] = depth_buffer_flag;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(zeta_height)] = depth_buffer_flag;
|
||||
constexpr u32 registers_in_zeta = sizeof(regs.zeta) / sizeof(u32);
|
||||
constexpr u32 zeta_reg = MAXWELL3D_REG_INDEX(zeta);
|
||||
set_block(zeta_reg, registers_in_zeta, depth_buffer_flag);
|
||||
|
||||
// Init Vertex Arrays
|
||||
constexpr u32 vertex_array_start = MAXWELL3D_REG_INDEX(vertex_array);
|
||||
constexpr u32 vertex_array_size = sizeof(regs.vertex_array[0]) / sizeof(u32);
|
||||
constexpr u32 vertex_array_end = vertex_array_start + vertex_array_size * Regs::NumVertexArrays;
|
||||
u8 va_dirty_reg = DIRTY_REGS_POS(vertex_array);
|
||||
u8 vi_dirty_reg = DIRTY_REGS_POS(vertex_instance);
|
||||
for (u32 vertex_reg = vertex_array_start; vertex_reg < vertex_array_end;
|
||||
vertex_reg += vertex_array_size) {
|
||||
set_block(vertex_reg, 3, va_dirty_reg);
|
||||
// The divisor concerns vertex array instances
|
||||
dirty_pointers[static_cast<std::size_t>(vertex_reg) + 3] = vi_dirty_reg;
|
||||
++va_dirty_reg;
|
||||
++vi_dirty_reg;
|
||||
}
|
||||
constexpr u32 vertex_limit_start = MAXWELL3D_REG_INDEX(vertex_array_limit);
|
||||
constexpr u32 vertex_limit_size = sizeof(regs.vertex_array_limit[0]) / sizeof(u32);
|
||||
constexpr u32 vertex_limit_end = vertex_limit_start + vertex_limit_size * Regs::NumVertexArrays;
|
||||
va_dirty_reg = DIRTY_REGS_POS(vertex_array);
|
||||
for (u32 vertex_reg = vertex_limit_start; vertex_reg < vertex_limit_end;
|
||||
vertex_reg += vertex_limit_size) {
|
||||
set_block(vertex_reg, vertex_limit_size, va_dirty_reg);
|
||||
va_dirty_reg++;
|
||||
}
|
||||
constexpr u32 vertex_instance_start = MAXWELL3D_REG_INDEX(instanced_arrays);
|
||||
constexpr u32 vertex_instance_size =
|
||||
sizeof(regs.instanced_arrays.is_instanced[0]) / sizeof(u32);
|
||||
constexpr u32 vertex_instance_end =
|
||||
vertex_instance_start + vertex_instance_size * Regs::NumVertexArrays;
|
||||
vi_dirty_reg = DIRTY_REGS_POS(vertex_instance);
|
||||
for (u32 vertex_reg = vertex_instance_start; vertex_reg < vertex_instance_end;
|
||||
vertex_reg += vertex_instance_size) {
|
||||
set_block(vertex_reg, vertex_instance_size, vi_dirty_reg);
|
||||
vi_dirty_reg++;
|
||||
}
|
||||
set_block(MAXWELL3D_REG_INDEX(vertex_attrib_format), regs.vertex_attrib_format.size(),
|
||||
DIRTY_REGS_POS(vertex_attrib_format));
|
||||
|
||||
// Init Shaders
|
||||
constexpr u32 shader_registers_count =
|
||||
sizeof(regs.shader_config[0]) * Regs::MaxShaderProgram / sizeof(u32);
|
||||
set_block(MAXWELL3D_REG_INDEX(shader_config[0]), shader_registers_count,
|
||||
DIRTY_REGS_POS(shaders));
|
||||
|
||||
// State
|
||||
|
||||
// Viewport
|
||||
constexpr u8 viewport_dirty_reg = DIRTY_REGS_POS(viewport);
|
||||
constexpr u32 viewport_start = MAXWELL3D_REG_INDEX(viewports);
|
||||
constexpr u32 viewport_size = sizeof(regs.viewports) / sizeof(u32);
|
||||
set_block(viewport_start, viewport_size, viewport_dirty_reg);
|
||||
constexpr u32 view_volume_start = MAXWELL3D_REG_INDEX(view_volume_clip_control);
|
||||
constexpr u32 view_volume_size = sizeof(regs.view_volume_clip_control) / sizeof(u32);
|
||||
set_block(view_volume_start, view_volume_size, viewport_dirty_reg);
|
||||
|
||||
// Viewport transformation
|
||||
constexpr u32 viewport_trans_start = MAXWELL3D_REG_INDEX(viewport_transform);
|
||||
constexpr u32 viewport_trans_size = sizeof(regs.viewport_transform) / sizeof(u32);
|
||||
set_block(viewport_trans_start, viewport_trans_size, DIRTY_REGS_POS(viewport_transform));
|
||||
|
||||
// Cullmode
|
||||
constexpr u32 cull_mode_start = MAXWELL3D_REG_INDEX(cull);
|
||||
constexpr u32 cull_mode_size = sizeof(regs.cull) / sizeof(u32);
|
||||
set_block(cull_mode_start, cull_mode_size, DIRTY_REGS_POS(cull_mode));
|
||||
|
||||
// Screen y control
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(screen_y_control)] = DIRTY_REGS_POS(screen_y_control);
|
||||
|
||||
// Primitive Restart
|
||||
constexpr u32 primitive_restart_start = MAXWELL3D_REG_INDEX(primitive_restart);
|
||||
constexpr u32 primitive_restart_size = sizeof(regs.primitive_restart) / sizeof(u32);
|
||||
set_block(primitive_restart_start, primitive_restart_size, DIRTY_REGS_POS(primitive_restart));
|
||||
|
||||
// Depth Test
|
||||
constexpr u8 depth_test_dirty_reg = DIRTY_REGS_POS(depth_test);
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(depth_test_enable)] = depth_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(depth_write_enabled)] = depth_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(depth_test_func)] = depth_test_dirty_reg;
|
||||
|
||||
// Stencil Test
|
||||
constexpr u32 stencil_test_dirty_reg = DIRTY_REGS_POS(stencil_test);
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_enable)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_func_func)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_func_ref)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_func_mask)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_op_fail)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_op_zfail)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_op_zpass)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_mask)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_two_side_enable)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_func_func)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_func_ref)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_func_mask)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_op_fail)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_op_zfail)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_op_zpass)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_mask)] = stencil_test_dirty_reg;
|
||||
|
||||
// Color Mask
|
||||
constexpr u8 color_mask_dirty_reg = DIRTY_REGS_POS(color_mask);
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(color_mask_common)] = color_mask_dirty_reg;
|
||||
set_block(MAXWELL3D_REG_INDEX(color_mask), sizeof(regs.color_mask) / sizeof(u32),
|
||||
color_mask_dirty_reg);
|
||||
// Blend State
|
||||
constexpr u8 blend_state_dirty_reg = DIRTY_REGS_POS(blend_state);
|
||||
set_block(MAXWELL3D_REG_INDEX(blend_color), sizeof(regs.blend_color) / sizeof(u32),
|
||||
blend_state_dirty_reg);
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(independent_blend_enable)] = blend_state_dirty_reg;
|
||||
set_block(MAXWELL3D_REG_INDEX(blend), sizeof(regs.blend) / sizeof(u32), blend_state_dirty_reg);
|
||||
set_block(MAXWELL3D_REG_INDEX(independent_blend), sizeof(regs.independent_blend) / sizeof(u32),
|
||||
blend_state_dirty_reg);
|
||||
|
||||
// Scissor State
|
||||
constexpr u8 scissor_test_dirty_reg = DIRTY_REGS_POS(scissor_test);
|
||||
set_block(MAXWELL3D_REG_INDEX(scissor_test), sizeof(regs.scissor_test) / sizeof(u32),
|
||||
scissor_test_dirty_reg);
|
||||
|
||||
// Polygon Offset
|
||||
constexpr u8 polygon_offset_dirty_reg = DIRTY_REGS_POS(polygon_offset);
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_fill_enable)] = polygon_offset_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_line_enable)] = polygon_offset_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_point_enable)] = polygon_offset_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_units)] = polygon_offset_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_factor)] = polygon_offset_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_clamp)] = polygon_offset_dirty_reg;
|
||||
|
||||
// Depth bounds
|
||||
constexpr u8 depth_bounds_values_dirty_reg = DIRTY_REGS_POS(depth_bounds_values);
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(depth_bounds[0])] = depth_bounds_values_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(depth_bounds[1])] = depth_bounds_values_dirty_reg;
|
||||
}
|
||||
|
||||
void Maxwell3D::CallMacroMethod(u32 method, std::size_t num_parameters, const u32* parameters) {
|
||||
// Reset the current macro.
|
||||
executing_macro = 0;
|
||||
@@ -314,31 +162,34 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
||||
"Invalid Maxwell3D register, increase the size of the Regs structure");
|
||||
|
||||
if (regs.reg_array[method] != method_call.argument) {
|
||||
regs.reg_array[method] = method_call.argument;
|
||||
const std::size_t dirty_reg = dirty_pointers[method];
|
||||
if (dirty_reg) {
|
||||
dirty.regs[dirty_reg] = true;
|
||||
if (dirty_reg >= DIRTY_REGS_POS(vertex_array) &&
|
||||
dirty_reg < DIRTY_REGS_POS(vertex_array_buffers)) {
|
||||
dirty.vertex_array_buffers = true;
|
||||
} else if (dirty_reg >= DIRTY_REGS_POS(vertex_instance) &&
|
||||
dirty_reg < DIRTY_REGS_POS(vertex_instances)) {
|
||||
dirty.vertex_instances = true;
|
||||
} else if (dirty_reg >= DIRTY_REGS_POS(render_target) &&
|
||||
dirty_reg < DIRTY_REGS_POS(render_settings)) {
|
||||
dirty.render_settings = true;
|
||||
}
|
||||
u32 arg = method_call.argument;
|
||||
// Keep track of the register value in shadow_state when requested.
|
||||
if (shadow_state.shadow_ram_control == Regs::ShadowRamControl::Track ||
|
||||
shadow_state.shadow_ram_control == Regs::ShadowRamControl::TrackWithFilter) {
|
||||
shadow_state.reg_array[method] = arg;
|
||||
} else if (shadow_state.shadow_ram_control == Regs::ShadowRamControl::Replay) {
|
||||
arg = shadow_state.reg_array[method];
|
||||
}
|
||||
|
||||
if (regs.reg_array[method] != arg) {
|
||||
regs.reg_array[method] = arg;
|
||||
|
||||
for (const auto& table : dirty.tables) {
|
||||
dirty.flags[table[method]] = true;
|
||||
}
|
||||
}
|
||||
|
||||
switch (method) {
|
||||
case MAXWELL3D_REG_INDEX(shadow_ram_control): {
|
||||
shadow_state.shadow_ram_control = static_cast<Regs::ShadowRamControl>(method_call.argument);
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(macros.data): {
|
||||
ProcessMacroUpload(method_call.argument);
|
||||
ProcessMacroUpload(arg);
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(macros.bind): {
|
||||
ProcessMacroBind(method_call.argument);
|
||||
ProcessMacroBind(arg);
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(firmware[4]): {
|
||||
@@ -400,6 +251,10 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
|
||||
ProcessQueryCondition();
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(counter_reset): {
|
||||
ProcessCounterReset();
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(sync_info): {
|
||||
ProcessSyncPoint();
|
||||
break;
|
||||
@@ -410,9 +265,9 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(data_upload): {
|
||||
const bool is_last_call = method_call.IsLastCall();
|
||||
upload_state.ProcessData(method_call.argument, is_last_call);
|
||||
upload_state.ProcessData(arg, is_last_call);
|
||||
if (is_last_call) {
|
||||
dirty.OnMemoryWrite();
|
||||
OnMemoryWrite();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -482,7 +337,7 @@ void Maxwell3D::FlushMMEInlineDraw() {
|
||||
|
||||
const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed;
|
||||
if (ShouldExecute()) {
|
||||
rasterizer.DrawMultiBatch(is_indexed);
|
||||
rasterizer.Draw(is_indexed, true);
|
||||
}
|
||||
|
||||
// TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
|
||||
@@ -544,40 +399,28 @@ void Maxwell3D::ProcessQueryGet() {
|
||||
"Units other than CROP are unimplemented");
|
||||
|
||||
switch (regs.query.query_get.operation) {
|
||||
case Regs::QueryOperation::Release: {
|
||||
const u64 result = regs.query.query_sequence;
|
||||
StampQueryResult(result, regs.query.query_get.short_query == 0);
|
||||
case Regs::QueryOperation::Release:
|
||||
StampQueryResult(regs.query.query_sequence, regs.query.query_get.short_query == 0);
|
||||
break;
|
||||
}
|
||||
case Regs::QueryOperation::Acquire: {
|
||||
// Todo(Blinkhawk): Under this operation, the GPU waits for the CPU
|
||||
// to write a value that matches the current payload.
|
||||
case Regs::QueryOperation::Acquire:
|
||||
// TODO(Blinkhawk): Under this operation, the GPU waits for the CPU to write a value that
|
||||
// matches the current payload.
|
||||
UNIMPLEMENTED_MSG("Unimplemented query operation ACQUIRE");
|
||||
break;
|
||||
}
|
||||
case Regs::QueryOperation::Counter: {
|
||||
u64 result{};
|
||||
switch (regs.query.query_get.select) {
|
||||
case Regs::QuerySelect::Zero:
|
||||
result = 0;
|
||||
break;
|
||||
default:
|
||||
result = 1;
|
||||
UNIMPLEMENTED_MSG("Unimplemented query select type {}",
|
||||
static_cast<u32>(regs.query.query_get.select.Value()));
|
||||
case Regs::QueryOperation::Counter:
|
||||
if (const std::optional<u64> result = GetQueryResult()) {
|
||||
// If the query returns an empty optional it means it's cached and deferred.
|
||||
// In this case we have a non-empty result, so we stamp it immediately.
|
||||
StampQueryResult(*result, regs.query.query_get.short_query == 0);
|
||||
}
|
||||
StampQueryResult(result, regs.query.query_get.short_query == 0);
|
||||
break;
|
||||
}
|
||||
case Regs::QueryOperation::Trap: {
|
||||
case Regs::QueryOperation::Trap:
|
||||
UNIMPLEMENTED_MSG("Unimplemented query operation TRAP");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unknown query operation");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessQueryCondition() {
|
||||
@@ -593,20 +436,20 @@ void Maxwell3D::ProcessQueryCondition() {
|
||||
}
|
||||
case Regs::ConditionMode::ResNonZero: {
|
||||
Regs::QueryCompare cmp;
|
||||
memory_manager.ReadBlockUnsafe(condition_address, &cmp, sizeof(cmp));
|
||||
memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp));
|
||||
execute_on = cmp.initial_sequence != 0U && cmp.initial_mode != 0U;
|
||||
break;
|
||||
}
|
||||
case Regs::ConditionMode::Equal: {
|
||||
Regs::QueryCompare cmp;
|
||||
memory_manager.ReadBlockUnsafe(condition_address, &cmp, sizeof(cmp));
|
||||
memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp));
|
||||
execute_on =
|
||||
cmp.initial_sequence == cmp.current_sequence && cmp.initial_mode == cmp.current_mode;
|
||||
break;
|
||||
}
|
||||
case Regs::ConditionMode::NotEqual: {
|
||||
Regs::QueryCompare cmp;
|
||||
memory_manager.ReadBlockUnsafe(condition_address, &cmp, sizeof(cmp));
|
||||
memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp));
|
||||
execute_on =
|
||||
cmp.initial_sequence != cmp.current_sequence || cmp.initial_mode != cmp.current_mode;
|
||||
break;
|
||||
@@ -619,6 +462,18 @@ void Maxwell3D::ProcessQueryCondition() {
|
||||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessCounterReset() {
|
||||
switch (regs.counter_reset) {
|
||||
case Regs::CounterReset::SampleCnt:
|
||||
rasterizer.ResetCounter(QueryType::SamplesPassed);
|
||||
break;
|
||||
default:
|
||||
LOG_WARNING(Render_OpenGL, "Unimplemented counter reset={}",
|
||||
static_cast<int>(regs.counter_reset));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessSyncPoint() {
|
||||
const u32 sync_point = regs.sync_info.sync_point.Value();
|
||||
const u32 increment = regs.sync_info.increment.Value();
|
||||
@@ -647,7 +502,7 @@ void Maxwell3D::DrawArrays() {
|
||||
|
||||
const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count};
|
||||
if (ShouldExecute()) {
|
||||
rasterizer.DrawBatch(is_indexed);
|
||||
rasterizer.Draw(is_indexed, false);
|
||||
}
|
||||
|
||||
// TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
|
||||
@@ -661,6 +516,22 @@ void Maxwell3D::DrawArrays() {
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<u64> Maxwell3D::GetQueryResult() {
|
||||
switch (regs.query.query_get.select) {
|
||||
case Regs::QuerySelect::Zero:
|
||||
return 0;
|
||||
case Regs::QuerySelect::SamplesPassed:
|
||||
// Deferred.
|
||||
rasterizer.Query(regs.query.QueryAddress(), VideoCore::QueryType::SamplesPassed,
|
||||
system.GPU().GetTicks());
|
||||
return {};
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented query select type {}",
|
||||
static_cast<u32>(regs.query.query_get.select.Value()));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessCBBind(std::size_t stage_index) {
|
||||
// Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage.
|
||||
auto& shader = state.shader_stages[stage_index];
|
||||
@@ -704,7 +575,7 @@ void Maxwell3D::FinishCBData() {
|
||||
|
||||
const u32 id = cb_data_state.id;
|
||||
memory_manager.WriteBlock(address, cb_data_state.buffer[id].data(), size);
|
||||
dirty.OnMemoryWrite();
|
||||
OnMemoryWrite();
|
||||
|
||||
cb_data_state.id = null_cb_data;
|
||||
cb_data_state.current = null_cb_data;
|
||||
@@ -782,7 +653,7 @@ SamplerDescriptor Maxwell3D::AccessBindlessSampler(ShaderType stage, u64 const_b
|
||||
|
||||
const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(tex_info_address)};
|
||||
const Texture::FullTextureInfo tex_info = GetTextureInfo(tex_handle);
|
||||
SamplerDescriptor result = SamplerDescriptor::FromTicTexture(tex_info.tic.texture_type.Value());
|
||||
SamplerDescriptor result = SamplerDescriptor::FromTIC(tex_info.tic);
|
||||
result.is_shadow.Assign(tex_info.tsc.depth_compare_enabled.Value());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
@@ -65,6 +67,7 @@ public:
|
||||
static constexpr std::size_t NumVaryings = 31;
|
||||
static constexpr std::size_t NumImages = 8; // TODO(Rodrigo): Investigate this number
|
||||
static constexpr std::size_t NumClipDistances = 8;
|
||||
static constexpr std::size_t NumTransformFeedbackBuffers = 4;
|
||||
static constexpr std::size_t MaxShaderProgram = 6;
|
||||
static constexpr std::size_t MaxShaderStage = 5;
|
||||
// Maximum number of const buffers per shader stage.
|
||||
@@ -409,21 +412,36 @@ public:
|
||||
Linear = 1,
|
||||
};
|
||||
|
||||
struct Cull {
|
||||
enum class FrontFace : u32 {
|
||||
ClockWise = 0x0900,
|
||||
CounterClockWise = 0x0901,
|
||||
};
|
||||
enum class CounterReset : u32 {
|
||||
SampleCnt = 0x01,
|
||||
Unk02 = 0x02,
|
||||
Unk03 = 0x03,
|
||||
Unk04 = 0x04,
|
||||
EmittedPrimitives = 0x10, // Not tested
|
||||
Unk11 = 0x11,
|
||||
Unk12 = 0x12,
|
||||
Unk13 = 0x13,
|
||||
Unk15 = 0x15,
|
||||
Unk16 = 0x16,
|
||||
Unk17 = 0x17,
|
||||
Unk18 = 0x18,
|
||||
Unk1A = 0x1A,
|
||||
Unk1B = 0x1B,
|
||||
Unk1C = 0x1C,
|
||||
Unk1D = 0x1D,
|
||||
Unk1E = 0x1E,
|
||||
GeneratedPrimitives = 0x1F,
|
||||
};
|
||||
|
||||
enum class CullFace : u32 {
|
||||
Front = 0x0404,
|
||||
Back = 0x0405,
|
||||
FrontAndBack = 0x0408,
|
||||
};
|
||||
enum class FrontFace : u32 {
|
||||
ClockWise = 0x0900,
|
||||
CounterClockWise = 0x0901,
|
||||
};
|
||||
|
||||
u32 enabled;
|
||||
FrontFace front_face;
|
||||
CullFace cull_face;
|
||||
enum class CullFace : u32 {
|
||||
Front = 0x0404,
|
||||
Back = 0x0405,
|
||||
FrontAndBack = 0x0408,
|
||||
};
|
||||
|
||||
struct Blend {
|
||||
@@ -507,6 +525,23 @@ public:
|
||||
FractionalEven = 2,
|
||||
};
|
||||
|
||||
enum class PolygonMode : u32 {
|
||||
Point = 0x1b00,
|
||||
Line = 0x1b01,
|
||||
Fill = 0x1b02,
|
||||
};
|
||||
|
||||
enum class ShadowRamControl : u32 {
|
||||
// write value to shadow ram
|
||||
Track = 0,
|
||||
// write value to shadow ram ( with validation ??? )
|
||||
TrackWithFilter = 1,
|
||||
// only write to real hw register
|
||||
Passthrough = 2,
|
||||
// write value from shadow ram to real hw register
|
||||
Replay = 3,
|
||||
};
|
||||
|
||||
struct RenderTargetConfig {
|
||||
u32 address_high;
|
||||
u32 address_low;
|
||||
@@ -520,7 +555,7 @@ public:
|
||||
BitField<12, 1, InvMemoryLayout> type;
|
||||
} memory_layout;
|
||||
union {
|
||||
BitField<0, 16, u32> array_mode;
|
||||
BitField<0, 16, u32> layers;
|
||||
BitField<16, 1, u32> volume;
|
||||
};
|
||||
u32 layer_stride;
|
||||
@@ -552,7 +587,7 @@ public:
|
||||
f32 translate_z;
|
||||
INSERT_UNION_PADDING_WORDS(2);
|
||||
|
||||
Common::Rectangle<s32> GetRect() const {
|
||||
Common::Rectangle<f32> GetRect() const {
|
||||
return {
|
||||
GetX(), // left
|
||||
GetY() + GetHeight(), // top
|
||||
@@ -561,20 +596,20 @@ public:
|
||||
};
|
||||
};
|
||||
|
||||
s32 GetX() const {
|
||||
return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x)));
|
||||
f32 GetX() const {
|
||||
return std::max(0.0f, translate_x - std::fabs(scale_x));
|
||||
}
|
||||
|
||||
s32 GetY() const {
|
||||
return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y)));
|
||||
f32 GetY() const {
|
||||
return std::max(0.0f, translate_y - std::fabs(scale_y));
|
||||
}
|
||||
|
||||
s32 GetWidth() const {
|
||||
return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX();
|
||||
f32 GetWidth() const {
|
||||
return translate_x + std::fabs(scale_x) - GetX();
|
||||
}
|
||||
|
||||
s32 GetHeight() const {
|
||||
return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY();
|
||||
f32 GetHeight() const {
|
||||
return translate_y + std::fabs(scale_y) - GetY();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -604,6 +639,29 @@ public:
|
||||
float depth_range_far;
|
||||
};
|
||||
|
||||
struct TransformFeedbackBinding {
|
||||
u32 buffer_enable;
|
||||
u32 address_high;
|
||||
u32 address_low;
|
||||
s32 buffer_size;
|
||||
s32 buffer_offset;
|
||||
INSERT_UNION_PADDING_WORDS(3);
|
||||
|
||||
GPUVAddr Address() const {
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
|
||||
address_low);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(TransformFeedbackBinding) == 32);
|
||||
|
||||
struct TransformFeedbackLayout {
|
||||
u32 stream;
|
||||
u32 varying_count;
|
||||
u32 stride;
|
||||
INSERT_UNION_PADDING_WORDS(1);
|
||||
};
|
||||
static_assert(sizeof(TransformFeedbackLayout) == 16);
|
||||
|
||||
bool IsShaderConfigEnabled(std::size_t index) const {
|
||||
// The VertexB is always enabled.
|
||||
if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) {
|
||||
@@ -612,6 +670,10 @@ public:
|
||||
return shader_config[index].enable != 0;
|
||||
}
|
||||
|
||||
bool IsShaderConfigEnabled(Regs::ShaderProgram type) const {
|
||||
return IsShaderConfigEnabled(static_cast<std::size_t>(type));
|
||||
}
|
||||
|
||||
union {
|
||||
struct {
|
||||
INSERT_UNION_PADDING_WORDS(0x45);
|
||||
@@ -623,7 +685,9 @@ public:
|
||||
u32 bind;
|
||||
} macros;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x17);
|
||||
ShadowRamControl shadow_ram_control;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x16);
|
||||
|
||||
Upload::Registers upload;
|
||||
struct {
|
||||
@@ -660,7 +724,13 @@ public:
|
||||
|
||||
u32 rasterize_enable;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0xF1);
|
||||
std::array<TransformFeedbackBinding, NumTransformFeedbackBuffers> tfb_bindings;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0xC0);
|
||||
|
||||
std::array<TransformFeedbackLayout, NumTransformFeedbackBuffers> tfb_layouts;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x1);
|
||||
|
||||
u32 tfb_enabled;
|
||||
|
||||
@@ -688,7 +758,12 @@ public:
|
||||
|
||||
s32 clear_stencil;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x7);
|
||||
INSERT_UNION_PADDING_WORDS(0x2);
|
||||
|
||||
PolygonMode polygon_mode_front;
|
||||
PolygonMode polygon_mode_back;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x3);
|
||||
|
||||
u32 polygon_offset_point_enable;
|
||||
u32 polygon_offset_line_enable;
|
||||
@@ -747,7 +822,11 @@ public:
|
||||
BitField<12, 4, u32> viewport;
|
||||
} clear_flags;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x19);
|
||||
INSERT_UNION_PADDING_WORDS(0x10);
|
||||
|
||||
u32 fill_rectangle;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x8);
|
||||
|
||||
std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format;
|
||||
|
||||
@@ -778,8 +857,12 @@ public:
|
||||
|
||||
u32 zeta_width;
|
||||
u32 zeta_height;
|
||||
union {
|
||||
BitField<0, 16, u32> zeta_layers;
|
||||
BitField<16, 1, u32> zeta_volume;
|
||||
};
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x27);
|
||||
INSERT_UNION_PADDING_WORDS(0x26);
|
||||
|
||||
u32 depth_test_enable;
|
||||
|
||||
@@ -846,18 +929,9 @@ public:
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x35);
|
||||
|
||||
union {
|
||||
BitField<0, 1, u32> c0;
|
||||
BitField<1, 1, u32> c1;
|
||||
BitField<2, 1, u32> c2;
|
||||
BitField<3, 1, u32> c3;
|
||||
BitField<4, 1, u32> c4;
|
||||
BitField<5, 1, u32> c5;
|
||||
BitField<6, 1, u32> c6;
|
||||
BitField<7, 1, u32> c7;
|
||||
} clip_distance_enabled;
|
||||
u32 clip_distance_enabled;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x1);
|
||||
u32 samplecnt_enable;
|
||||
|
||||
float point_size;
|
||||
|
||||
@@ -865,7 +939,11 @@ public:
|
||||
|
||||
u32 point_sprite_enable;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x5);
|
||||
INSERT_UNION_PADDING_WORDS(0x3);
|
||||
|
||||
CounterReset counter_reset;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x1);
|
||||
|
||||
u32 zeta_enable;
|
||||
|
||||
@@ -1030,7 +1108,9 @@ public:
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(1);
|
||||
|
||||
Cull cull;
|
||||
u32 cull_test_enabled;
|
||||
FrontFace front_face;
|
||||
CullFace cull_face;
|
||||
|
||||
u32 pixel_center_integer;
|
||||
|
||||
@@ -1169,7 +1249,11 @@ public:
|
||||
|
||||
u32 tex_cb_index;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x395);
|
||||
INSERT_UNION_PADDING_WORDS(0x7D);
|
||||
|
||||
std::array<std::array<u8, 128>, NumTransformFeedbackBuffers> tfb_varying_locs;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x298);
|
||||
|
||||
struct {
|
||||
/// Compressed address of a buffer that holds information about bound SSBOs.
|
||||
@@ -1192,7 +1276,12 @@ public:
|
||||
};
|
||||
std::array<u32, NUM_REGS> reg_array;
|
||||
};
|
||||
} regs{};
|
||||
};
|
||||
|
||||
Regs regs{};
|
||||
|
||||
/// Store temporary hw register values, used by some calls to restore state after a operation
|
||||
Regs shadow_state;
|
||||
|
||||
static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "Maxwell3D Regs has wrong size");
|
||||
static_assert(std::is_trivially_copyable_v<Regs>, "Maxwell3D Regs must be trivially copyable");
|
||||
@@ -1208,79 +1297,6 @@ public:
|
||||
|
||||
State state{};
|
||||
|
||||
struct DirtyRegs {
|
||||
static constexpr std::size_t NUM_REGS = 256;
|
||||
static_assert(NUM_REGS - 1 <= std::numeric_limits<u8>::max());
|
||||
|
||||
union {
|
||||
struct {
|
||||
bool null_dirty;
|
||||
|
||||
// Vertex Attributes
|
||||
bool vertex_attrib_format;
|
||||
|
||||
// Vertex Arrays
|
||||
std::array<bool, 32> vertex_array;
|
||||
|
||||
bool vertex_array_buffers;
|
||||
|
||||
// Vertex Instances
|
||||
std::array<bool, 32> vertex_instance;
|
||||
|
||||
bool vertex_instances;
|
||||
|
||||
// Render Targets
|
||||
std::array<bool, 8> render_target;
|
||||
bool depth_buffer;
|
||||
|
||||
bool render_settings;
|
||||
|
||||
// Shaders
|
||||
bool shaders;
|
||||
|
||||
// Rasterizer State
|
||||
bool viewport;
|
||||
bool clip_coefficient;
|
||||
bool cull_mode;
|
||||
bool primitive_restart;
|
||||
bool depth_test;
|
||||
bool stencil_test;
|
||||
bool blend_state;
|
||||
bool scissor_test;
|
||||
bool transform_feedback;
|
||||
bool color_mask;
|
||||
bool polygon_offset;
|
||||
bool depth_bounds_values;
|
||||
|
||||
// Complementary
|
||||
bool viewport_transform;
|
||||
bool screen_y_control;
|
||||
|
||||
bool memory_general;
|
||||
};
|
||||
std::array<bool, NUM_REGS> regs;
|
||||
};
|
||||
|
||||
void ResetVertexArrays() {
|
||||
vertex_array.fill(true);
|
||||
vertex_array_buffers = true;
|
||||
}
|
||||
|
||||
void ResetRenderTargets() {
|
||||
depth_buffer = true;
|
||||
render_target.fill(true);
|
||||
render_settings = true;
|
||||
}
|
||||
|
||||
void OnMemoryWrite() {
|
||||
shaders = true;
|
||||
memory_general = true;
|
||||
ResetRenderTargets();
|
||||
ResetVertexArrays();
|
||||
}
|
||||
|
||||
} dirty{};
|
||||
|
||||
/// Reads a register value located at the input method address
|
||||
u32 GetRegisterValue(u32 method) const;
|
||||
|
||||
@@ -1326,6 +1342,11 @@ public:
|
||||
return execute_on;
|
||||
}
|
||||
|
||||
/// Notify a memory write has happened.
|
||||
void OnMemoryWrite() {
|
||||
dirty.flags |= dirty.on_write_stores;
|
||||
}
|
||||
|
||||
enum class MMEDrawMode : u32 {
|
||||
Undefined,
|
||||
Array,
|
||||
@@ -1341,6 +1362,16 @@ public:
|
||||
u32 gl_end_count{};
|
||||
} mme_draw;
|
||||
|
||||
struct DirtyState {
|
||||
using Flags = std::bitset<std::numeric_limits<u8>::max()>;
|
||||
using Table = std::array<u8, Regs::NUM_REGS>;
|
||||
using Tables = std::array<Table, 2>;
|
||||
|
||||
Flags flags;
|
||||
Flags on_write_stores;
|
||||
Tables tables{};
|
||||
} dirty;
|
||||
|
||||
private:
|
||||
void InitializeRegisterDefaults();
|
||||
|
||||
@@ -1387,8 +1418,6 @@ private:
|
||||
/// Retrieves information about a specific TSC entry from the TSC buffer.
|
||||
Texture::TSCEntry GetTSCEntry(u32 tsc_index) const;
|
||||
|
||||
void InitDirtySettings();
|
||||
|
||||
/**
|
||||
* Call a macro on this engine.
|
||||
* @param method Method to call
|
||||
@@ -1412,12 +1441,15 @@ private:
|
||||
/// Handles a write to the QUERY_GET register.
|
||||
void ProcessQueryGet();
|
||||
|
||||
// Writes the query result accordingly
|
||||
/// Writes the query result accordingly.
|
||||
void StampQueryResult(u64 payload, bool long_query);
|
||||
|
||||
// Handles Conditional Rendering
|
||||
/// Handles conditional rendering.
|
||||
void ProcessQueryCondition();
|
||||
|
||||
/// Handles counter resets.
|
||||
void ProcessCounterReset();
|
||||
|
||||
/// Handles writes to syncing register.
|
||||
void ProcessSyncPoint();
|
||||
|
||||
@@ -1434,6 +1466,9 @@ private:
|
||||
|
||||
// Handles a instance drawcall from MME
|
||||
void StepInstance(MMEDrawMode expected_mode, u32 count);
|
||||
|
||||
/// Returns a query's value or an empty object if the value will be deferred through a cache.
|
||||
std::optional<u64> GetQueryResult();
|
||||
};
|
||||
|
||||
#define ASSERT_REG_POSITION(field_name, position) \
|
||||
@@ -1441,6 +1476,7 @@ private:
|
||||
"Field " #field_name " has invalid position")
|
||||
|
||||
ASSERT_REG_POSITION(macros, 0x45);
|
||||
ASSERT_REG_POSITION(shadow_ram_control, 0x49);
|
||||
ASSERT_REG_POSITION(upload, 0x60);
|
||||
ASSERT_REG_POSITION(exec_upload, 0x6C);
|
||||
ASSERT_REG_POSITION(data_upload, 0x6D);
|
||||
@@ -1449,6 +1485,8 @@ ASSERT_REG_POSITION(tess_mode, 0xC8);
|
||||
ASSERT_REG_POSITION(tess_level_outer, 0xC9);
|
||||
ASSERT_REG_POSITION(tess_level_inner, 0xCD);
|
||||
ASSERT_REG_POSITION(rasterize_enable, 0xDF);
|
||||
ASSERT_REG_POSITION(tfb_bindings, 0xE0);
|
||||
ASSERT_REG_POSITION(tfb_layouts, 0x1C0);
|
||||
ASSERT_REG_POSITION(tfb_enabled, 0x1D1);
|
||||
ASSERT_REG_POSITION(rt, 0x200);
|
||||
ASSERT_REG_POSITION(viewport_transform, 0x280);
|
||||
@@ -1458,6 +1496,8 @@ ASSERT_REG_POSITION(depth_mode, 0x35F);
|
||||
ASSERT_REG_POSITION(clear_color[0], 0x360);
|
||||
ASSERT_REG_POSITION(clear_depth, 0x364);
|
||||
ASSERT_REG_POSITION(clear_stencil, 0x368);
|
||||
ASSERT_REG_POSITION(polygon_mode_front, 0x36B);
|
||||
ASSERT_REG_POSITION(polygon_mode_back, 0x36C);
|
||||
ASSERT_REG_POSITION(polygon_offset_point_enable, 0x370);
|
||||
ASSERT_REG_POSITION(polygon_offset_line_enable, 0x371);
|
||||
ASSERT_REG_POSITION(polygon_offset_fill_enable, 0x372);
|
||||
@@ -1471,10 +1511,12 @@ ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB);
|
||||
ASSERT_REG_POSITION(depth_bounds, 0x3E7);
|
||||
ASSERT_REG_POSITION(zeta, 0x3F8);
|
||||
ASSERT_REG_POSITION(clear_flags, 0x43E);
|
||||
ASSERT_REG_POSITION(fill_rectangle, 0x44F);
|
||||
ASSERT_REG_POSITION(vertex_attrib_format, 0x458);
|
||||
ASSERT_REG_POSITION(rt_control, 0x487);
|
||||
ASSERT_REG_POSITION(zeta_width, 0x48a);
|
||||
ASSERT_REG_POSITION(zeta_height, 0x48b);
|
||||
ASSERT_REG_POSITION(zeta_layers, 0x48c);
|
||||
ASSERT_REG_POSITION(depth_test_enable, 0x4B3);
|
||||
ASSERT_REG_POSITION(independent_blend_enable, 0x4B9);
|
||||
ASSERT_REG_POSITION(depth_write_enabled, 0x4BA);
|
||||
@@ -1499,8 +1541,10 @@ ASSERT_REG_POSITION(screen_y_control, 0x4EB);
|
||||
ASSERT_REG_POSITION(vb_element_base, 0x50D);
|
||||
ASSERT_REG_POSITION(vb_base_instance, 0x50E);
|
||||
ASSERT_REG_POSITION(clip_distance_enabled, 0x544);
|
||||
ASSERT_REG_POSITION(samplecnt_enable, 0x545);
|
||||
ASSERT_REG_POSITION(point_size, 0x546);
|
||||
ASSERT_REG_POSITION(point_sprite_enable, 0x548);
|
||||
ASSERT_REG_POSITION(counter_reset, 0x54C);
|
||||
ASSERT_REG_POSITION(zeta_enable, 0x54E);
|
||||
ASSERT_REG_POSITION(multisample_control, 0x54F);
|
||||
ASSERT_REG_POSITION(condition, 0x554);
|
||||
@@ -1522,7 +1566,9 @@ ASSERT_REG_POSITION(index_array, 0x5F2);
|
||||
ASSERT_REG_POSITION(polygon_offset_clamp, 0x61F);
|
||||
ASSERT_REG_POSITION(instanced_arrays, 0x620);
|
||||
ASSERT_REG_POSITION(vp_point_size, 0x644);
|
||||
ASSERT_REG_POSITION(cull, 0x646);
|
||||
ASSERT_REG_POSITION(cull_test_enabled, 0x646);
|
||||
ASSERT_REG_POSITION(front_face, 0x647);
|
||||
ASSERT_REG_POSITION(cull_face, 0x648);
|
||||
ASSERT_REG_POSITION(pixel_center_integer, 0x649);
|
||||
ASSERT_REG_POSITION(viewport_transform_enabled, 0x64B);
|
||||
ASSERT_REG_POSITION(view_volume_clip_control, 0x64F);
|
||||
@@ -1539,6 +1585,7 @@ ASSERT_REG_POSITION(firmware, 0x8C0);
|
||||
ASSERT_REG_POSITION(const_buffer, 0x8E0);
|
||||
ASSERT_REG_POSITION(cb_bind[0], 0x904);
|
||||
ASSERT_REG_POSITION(tex_cb_index, 0x982);
|
||||
ASSERT_REG_POSITION(tfb_varying_locs, 0xA00);
|
||||
ASSERT_REG_POSITION(ssbo_info, 0xD18);
|
||||
ASSERT_REG_POSITION(tex_info_buffers.address[0], 0xD2A);
|
||||
ASSERT_REG_POSITION(tex_info_buffers.size[0], 0xD2F);
|
||||
|
||||
@@ -57,7 +57,7 @@ void MaxwellDMA::HandleCopy() {
|
||||
}
|
||||
|
||||
// All copies here update the main memory, so mark all rasterizer states as invalid.
|
||||
system.GPU().Maxwell3D().dirty.OnMemoryWrite();
|
||||
system.GPU().Maxwell3D().OnMemoryWrite();
|
||||
|
||||
if (regs.exec.is_dst_linear && regs.exec.is_src_linear) {
|
||||
// When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D
|
||||
|
||||
@@ -82,6 +82,10 @@ union Attribute {
|
||||
Position = 7,
|
||||
Attribute_0 = 8,
|
||||
Attribute_31 = 39,
|
||||
FrontColor = 40,
|
||||
FrontSecondaryColor = 41,
|
||||
BackColor = 42,
|
||||
BackSecondaryColor = 43,
|
||||
ClipDistances0123 = 44,
|
||||
ClipDistances4567 = 45,
|
||||
PointCoord = 46,
|
||||
@@ -89,6 +93,8 @@ union Attribute {
|
||||
// shader, and a tuple of (TessCoord.x, TessCoord.y, TessCoord.z, ~) when inside a Tess Eval
|
||||
// shader.
|
||||
TessCoordInstanceIDVertexID = 47,
|
||||
TexCoord_0 = 48,
|
||||
TexCoord_7 = 55,
|
||||
// This attribute contains a tuple of (Unk, Unk, Unk, gl_FrontFacing) when inside a fragment
|
||||
// shader. It is unknown what the other values contain.
|
||||
FrontFacing = 63,
|
||||
@@ -296,6 +302,23 @@ enum class VmadShr : u64 {
|
||||
Shr15 = 2,
|
||||
};
|
||||
|
||||
enum class VmnmxType : u64 {
|
||||
Bits8,
|
||||
Bits16,
|
||||
Bits32,
|
||||
};
|
||||
|
||||
enum class VmnmxOperation : u64 {
|
||||
Mrg_16H = 0,
|
||||
Mrg_16L = 1,
|
||||
Mrg_8B0 = 2,
|
||||
Mrg_8B2 = 3,
|
||||
Acc = 4,
|
||||
Min = 5,
|
||||
Max = 6,
|
||||
Nop = 7,
|
||||
};
|
||||
|
||||
enum class XmadMode : u64 {
|
||||
None = 0,
|
||||
CLo = 1,
|
||||
@@ -911,14 +934,9 @@ union Instruction {
|
||||
} fadd32i;
|
||||
|
||||
union {
|
||||
BitField<20, 8, u64> shift_position;
|
||||
BitField<28, 8, u64> shift_length;
|
||||
BitField<48, 1, u64> negate_b;
|
||||
BitField<49, 1, u64> negate_a;
|
||||
|
||||
u64 GetLeftShiftValue() const {
|
||||
return 32 - (shift_position + shift_length);
|
||||
}
|
||||
BitField<40, 1, u64> brev;
|
||||
BitField<47, 1, u64> rd_cc;
|
||||
BitField<48, 1, u64> is_signed;
|
||||
} bfe;
|
||||
|
||||
union {
|
||||
@@ -1661,6 +1679,42 @@ union Instruction {
|
||||
BitField<47, 1, u64> cc;
|
||||
} vmad;
|
||||
|
||||
union {
|
||||
BitField<54, 1, u64> is_dest_signed;
|
||||
BitField<48, 1, u64> is_src_a_signed;
|
||||
BitField<49, 1, u64> is_src_b_signed;
|
||||
BitField<37, 2, u64> src_format_a;
|
||||
BitField<29, 2, u64> src_format_b;
|
||||
BitField<56, 1, u64> mx;
|
||||
BitField<55, 1, u64> sat;
|
||||
BitField<36, 2, u64> selector_a;
|
||||
BitField<28, 2, u64> selector_b;
|
||||
BitField<50, 1, u64> is_op_b_register;
|
||||
BitField<51, 3, VmnmxOperation> operation;
|
||||
|
||||
VmnmxType SourceFormatA() const {
|
||||
switch (src_format_a) {
|
||||
case 0b11:
|
||||
return VmnmxType::Bits32;
|
||||
case 0b10:
|
||||
return VmnmxType::Bits16;
|
||||
default:
|
||||
return VmnmxType::Bits8;
|
||||
}
|
||||
}
|
||||
|
||||
VmnmxType SourceFormatB() const {
|
||||
switch (src_format_b) {
|
||||
case 0b11:
|
||||
return VmnmxType::Bits32;
|
||||
case 0b10:
|
||||
return VmnmxType::Bits16;
|
||||
default:
|
||||
return VmnmxType::Bits8;
|
||||
}
|
||||
}
|
||||
} vmnmx;
|
||||
|
||||
union {
|
||||
BitField<20, 16, u64> imm20_16;
|
||||
BitField<35, 1, u64> high_b_rr; // used on RR
|
||||
@@ -1772,6 +1826,7 @@ public:
|
||||
MEMBAR,
|
||||
VMAD,
|
||||
VSETP,
|
||||
VMNMX,
|
||||
FFMA_IMM, // Fused Multiply and Add
|
||||
FFMA_CR,
|
||||
FFMA_RC,
|
||||
@@ -2077,6 +2132,7 @@ private:
|
||||
INST("1110111110011---", Id::MEMBAR, Type::Trivial, "MEMBAR"),
|
||||
INST("01011111--------", Id::VMAD, Type::Video, "VMAD"),
|
||||
INST("0101000011110---", Id::VSETP, Type::Video, "VSETP"),
|
||||
INST("0011101---------", Id::VMNMX, Type::Video, "VMNMX"),
|
||||
INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
|
||||
INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"),
|
||||
INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"),
|
||||
@@ -2177,7 +2233,7 @@ private:
|
||||
INST("0011011-11111---", Id::SHF_LEFT_IMM, Type::Shift, "SHF_LEFT_IMM"),
|
||||
INST("0100110011100---", Id::I2I_C, Type::Conversion, "I2I_C"),
|
||||
INST("0101110011100---", Id::I2I_R, Type::Conversion, "I2I_R"),
|
||||
INST("0011101-11100---", Id::I2I_IMM, Type::Conversion, "I2I_IMM"),
|
||||
INST("0011100-11100---", Id::I2I_IMM, Type::Conversion, "I2I_IMM"),
|
||||
INST("0100110010111---", Id::I2F_C, Type::Conversion, "I2F_C"),
|
||||
INST("0101110010111---", Id::I2F_R, Type::Conversion, "I2F_R"),
|
||||
INST("0011100-10111---", Id::I2F_IMM, Type::Conversion, "I2F_IMM"),
|
||||
|
||||
@@ -140,71 +140,6 @@ void GPU::FlushCommands() {
|
||||
renderer.Rasterizer().FlushCommands();
|
||||
}
|
||||
|
||||
u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
|
||||
ASSERT(format != RenderTargetFormat::NONE);
|
||||
|
||||
switch (format) {
|
||||
case RenderTargetFormat::RGBA32_FLOAT:
|
||||
case RenderTargetFormat::RGBA32_UINT:
|
||||
return 16;
|
||||
case RenderTargetFormat::RGBA16_UINT:
|
||||
case RenderTargetFormat::RGBA16_UNORM:
|
||||
case RenderTargetFormat::RGBA16_FLOAT:
|
||||
case RenderTargetFormat::RGBX16_FLOAT:
|
||||
case RenderTargetFormat::RG32_FLOAT:
|
||||
case RenderTargetFormat::RG32_UINT:
|
||||
return 8;
|
||||
case RenderTargetFormat::RGBA8_UNORM:
|
||||
case RenderTargetFormat::RGBA8_SNORM:
|
||||
case RenderTargetFormat::RGBA8_SRGB:
|
||||
case RenderTargetFormat::RGBA8_UINT:
|
||||
case RenderTargetFormat::RGB10_A2_UNORM:
|
||||
case RenderTargetFormat::BGRA8_UNORM:
|
||||
case RenderTargetFormat::BGRA8_SRGB:
|
||||
case RenderTargetFormat::RG16_UNORM:
|
||||
case RenderTargetFormat::RG16_SNORM:
|
||||
case RenderTargetFormat::RG16_UINT:
|
||||
case RenderTargetFormat::RG16_SINT:
|
||||
case RenderTargetFormat::RG16_FLOAT:
|
||||
case RenderTargetFormat::R32_FLOAT:
|
||||
case RenderTargetFormat::R11G11B10_FLOAT:
|
||||
case RenderTargetFormat::R32_UINT:
|
||||
return 4;
|
||||
case RenderTargetFormat::R16_UNORM:
|
||||
case RenderTargetFormat::R16_SNORM:
|
||||
case RenderTargetFormat::R16_UINT:
|
||||
case RenderTargetFormat::R16_SINT:
|
||||
case RenderTargetFormat::R16_FLOAT:
|
||||
case RenderTargetFormat::RG8_UNORM:
|
||||
case RenderTargetFormat::RG8_SNORM:
|
||||
return 2;
|
||||
case RenderTargetFormat::R8_UNORM:
|
||||
case RenderTargetFormat::R8_UINT:
|
||||
return 1;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented render target format {}", static_cast<u32>(format));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
u32 DepthFormatBytesPerPixel(DepthFormat format) {
|
||||
switch (format) {
|
||||
case DepthFormat::Z32_S8_X24_FLOAT:
|
||||
return 8;
|
||||
case DepthFormat::Z32_FLOAT:
|
||||
case DepthFormat::S8_Z24_UNORM:
|
||||
case DepthFormat::Z24_X8_UNORM:
|
||||
case DepthFormat::Z24_S8_UNORM:
|
||||
case DepthFormat::Z24_C8_UNORM:
|
||||
return 4;
|
||||
case DepthFormat::Z16_UNORM:
|
||||
return 2;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented Depth format {}", static_cast<u32>(format));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Note that, traditionally, methods are treated as 4-byte addressable locations, and hence
|
||||
// their numbers are written down multiplied by 4 in Docs. Here we are not multiply by 4.
|
||||
// So the values you see in docs might be multiplied by 4.
|
||||
|
||||
@@ -39,6 +39,7 @@ enum class RenderTargetFormat : u32 {
|
||||
RGBA32_FLOAT = 0xC0,
|
||||
RGBA32_UINT = 0xC2,
|
||||
RGBA16_UNORM = 0xC6,
|
||||
RGBA16_SNORM = 0xC7,
|
||||
RGBA16_UINT = 0xC9,
|
||||
RGBA16_FLOAT = 0xCA,
|
||||
RG32_FLOAT = 0xCB,
|
||||
@@ -57,6 +58,7 @@ enum class RenderTargetFormat : u32 {
|
||||
RG16_UINT = 0xDD,
|
||||
RG16_FLOAT = 0xDE,
|
||||
R11G11B10_FLOAT = 0xE0,
|
||||
R32_SINT = 0xE3,
|
||||
R32_UINT = 0xE4,
|
||||
R32_FLOAT = 0xE5,
|
||||
B5G6R5_UNORM = 0xE8,
|
||||
@@ -82,12 +84,6 @@ enum class DepthFormat : u32 {
|
||||
Z32_S8_X24_FLOAT = 0x19,
|
||||
};
|
||||
|
||||
/// Returns the number of bytes per pixel of each rendertarget format.
|
||||
u32 RenderTargetBytesPerPixel(RenderTargetFormat format);
|
||||
|
||||
/// Returns the number of bytes per pixel of each depth format.
|
||||
u32 DepthFormatBytesPerPixel(DepthFormat format);
|
||||
|
||||
struct CommandListHeader;
|
||||
class DebugContext;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/scope_acquire_window_context.h"
|
||||
#include "core/frontend/scope_acquire_context.h"
|
||||
#include "video_core/dma_pusher.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/gpu_thread.h"
|
||||
@@ -27,7 +27,7 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
|
||||
return;
|
||||
}
|
||||
|
||||
Core::Frontend::ScopeAcquireWindowContext acquire_context{renderer.GetRenderWindow()};
|
||||
Core::Frontend::ScopeAcquireContext acquire_context{renderer.GetRenderWindow()};
|
||||
|
||||
CommandDataContainer next;
|
||||
while (state.is_running) {
|
||||
|
||||
@@ -4,13 +4,15 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/guest_driver.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
void GuestDriverProfile::DeduceTextureHandlerSize(std::vector<u32>&& bound_offsets) {
|
||||
if (texture_handler_size_deduced) {
|
||||
void GuestDriverProfile::DeduceTextureHandlerSize(std::vector<u32> bound_offsets) {
|
||||
if (texture_handler_size) {
|
||||
return;
|
||||
}
|
||||
const std::size_t size = bound_offsets.size();
|
||||
@@ -29,7 +31,6 @@ void GuestDriverProfile::DeduceTextureHandlerSize(std::vector<u32>&& bound_offse
|
||||
if (min_val > 2) {
|
||||
return;
|
||||
}
|
||||
texture_handler_size_deduced = true;
|
||||
texture_handler_size = min_texture_handler_size * min_val;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
@@ -17,25 +18,29 @@ namespace VideoCore {
|
||||
*/
|
||||
class GuestDriverProfile {
|
||||
public:
|
||||
void DeduceTextureHandlerSize(std::vector<u32>&& bound_offsets);
|
||||
explicit GuestDriverProfile() = default;
|
||||
explicit GuestDriverProfile(std::optional<u32> texture_handler_size)
|
||||
: texture_handler_size{texture_handler_size} {}
|
||||
|
||||
void DeduceTextureHandlerSize(std::vector<u32> bound_offsets);
|
||||
|
||||
u32 GetTextureHandlerSize() const {
|
||||
return texture_handler_size;
|
||||
return texture_handler_size.value_or(default_texture_handler_size);
|
||||
}
|
||||
|
||||
bool TextureHandlerSizeKnown() const {
|
||||
return texture_handler_size_deduced;
|
||||
bool IsTextureHandlerSizeKnown() const {
|
||||
return texture_handler_size.has_value();
|
||||
}
|
||||
|
||||
private:
|
||||
// Minimum size of texture handler any driver can use.
|
||||
static constexpr u32 min_texture_handler_size = 4;
|
||||
// This goes with Vulkan and OpenGL standards but Nvidia GPUs can easily
|
||||
// use 4 bytes instead. Thus, certain drivers may squish the size.
|
||||
|
||||
// This goes with Vulkan and OpenGL standards but Nvidia GPUs can easily use 4 bytes instead.
|
||||
// Thus, certain drivers may squish the size.
|
||||
static constexpr u32 default_texture_handler_size = 8;
|
||||
|
||||
u32 texture_handler_size = default_texture_handler_size;
|
||||
bool texture_handler_size_deduced = false;
|
||||
std::optional<u32> texture_handler_size = default_texture_handler_size;
|
||||
};
|
||||
|
||||
} // namespace VideoCore
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
|
||||
@@ -84,7 +85,9 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) {
|
||||
const auto cpu_addr = GpuToCpuAddress(gpu_addr);
|
||||
ASSERT(cpu_addr);
|
||||
|
||||
rasterizer.FlushAndInvalidateRegion(cache_addr, aligned_size);
|
||||
// Flush and invalidate through the GPU interface, to be asynchronous if possible.
|
||||
system.GPU().FlushAndInvalidateRegion(cache_addr, aligned_size);
|
||||
|
||||
UnmapRange(gpu_addr, aligned_size);
|
||||
ASSERT(system.CurrentProcess()
|
||||
->VMManager()
|
||||
@@ -242,6 +245,8 @@ void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, const std::s
|
||||
switch (page_table.attributes[page_index]) {
|
||||
case Common::PageType::Memory: {
|
||||
const u8* src_ptr{page_table.pointers[page_index] + page_offset};
|
||||
// Flush must happen on the rasterizer interface, such that memory is always synchronous
|
||||
// when it is read (even when in asynchronous GPU mode). Fixes Dead Cells title menu.
|
||||
rasterizer.FlushRegion(ToCacheAddr(src_ptr), copy_amount);
|
||||
std::memcpy(dest_buffer, src_ptr, copy_amount);
|
||||
break;
|
||||
@@ -292,6 +297,8 @@ void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, const
|
||||
switch (page_table.attributes[page_index]) {
|
||||
case Common::PageType::Memory: {
|
||||
u8* dest_ptr{page_table.pointers[page_index] + page_offset};
|
||||
// Invalidate must happen on the rasterizer interface, such that memory is always
|
||||
// synchronous when it is written (even when in asynchronous GPU mode).
|
||||
rasterizer.InvalidateRegion(ToCacheAddr(dest_ptr), copy_amount);
|
||||
std::memcpy(dest_ptr, src_buffer, copy_amount);
|
||||
break;
|
||||
@@ -339,6 +346,8 @@ void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, const std::
|
||||
|
||||
switch (page_table.attributes[page_index]) {
|
||||
case Common::PageType::Memory: {
|
||||
// Flush must happen on the rasterizer interface, such that memory is always synchronous
|
||||
// when it is copied (even when in asynchronous GPU mode).
|
||||
const u8* src_ptr{page_table.pointers[page_index] + page_offset};
|
||||
rasterizer.FlushRegion(ToCacheAddr(src_ptr), copy_amount);
|
||||
WriteBlock(dest_addr, src_ptr, copy_amount);
|
||||
|
||||
@@ -174,7 +174,7 @@ private:
|
||||
/// End of address space, based on address space in bits.
|
||||
static constexpr GPUVAddr address_space_end{1ULL << address_space_width};
|
||||
|
||||
Common::PageTable page_table{page_bits};
|
||||
Common::BackingPageTable page_table{page_bits};
|
||||
VMAMap vma_map;
|
||||
VideoCore::RasterizerInterface& rasterizer;
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ static constexpr ConversionArray morton_to_linear_fns = {
|
||||
MortonCopy<true, PixelFormat::R8UI>,
|
||||
MortonCopy<true, PixelFormat::RGBA16F>,
|
||||
MortonCopy<true, PixelFormat::RGBA16U>,
|
||||
MortonCopy<true, PixelFormat::RGBA16S>,
|
||||
MortonCopy<true, PixelFormat::RGBA16UI>,
|
||||
MortonCopy<true, PixelFormat::R11FG11FB10F>,
|
||||
MortonCopy<true, PixelFormat::RGBA32UI>,
|
||||
@@ -85,6 +86,7 @@ static constexpr ConversionArray morton_to_linear_fns = {
|
||||
MortonCopy<true, PixelFormat::RG32UI>,
|
||||
MortonCopy<true, PixelFormat::RGBX16F>,
|
||||
MortonCopy<true, PixelFormat::R32UI>,
|
||||
MortonCopy<true, PixelFormat::R32I>,
|
||||
MortonCopy<true, PixelFormat::ASTC_2D_8X8>,
|
||||
MortonCopy<true, PixelFormat::ASTC_2D_8X5>,
|
||||
MortonCopy<true, PixelFormat::ASTC_2D_5X4>,
|
||||
@@ -130,6 +132,7 @@ static constexpr ConversionArray linear_to_morton_fns = {
|
||||
MortonCopy<false, PixelFormat::R8U>,
|
||||
MortonCopy<false, PixelFormat::R8UI>,
|
||||
MortonCopy<false, PixelFormat::RGBA16F>,
|
||||
MortonCopy<false, PixelFormat::RGBA16S>,
|
||||
MortonCopy<false, PixelFormat::RGBA16U>,
|
||||
MortonCopy<false, PixelFormat::RGBA16UI>,
|
||||
MortonCopy<false, PixelFormat::R11FG11FB10F>,
|
||||
@@ -166,6 +169,7 @@ static constexpr ConversionArray linear_to_morton_fns = {
|
||||
MortonCopy<false, PixelFormat::RG32UI>,
|
||||
MortonCopy<false, PixelFormat::RGBX16F>,
|
||||
MortonCopy<false, PixelFormat::R32UI>,
|
||||
MortonCopy<false, PixelFormat::R32I>,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
|
||||
359
src/video_core/query_cache.h
Normal file
359
src/video_core/query_cache.h
Normal file
@@ -0,0 +1,359 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
template <class QueryCache, class HostCounter>
|
||||
class CounterStreamBase {
|
||||
public:
|
||||
explicit CounterStreamBase(QueryCache& cache, VideoCore::QueryType type)
|
||||
: cache{cache}, type{type} {}
|
||||
|
||||
/// Updates the state of the stream, enabling or disabling as needed.
|
||||
void Update(bool enabled) {
|
||||
if (enabled) {
|
||||
Enable();
|
||||
} else {
|
||||
Disable();
|
||||
}
|
||||
}
|
||||
|
||||
/// Resets the stream to zero. It doesn't disable the query after resetting.
|
||||
void Reset() {
|
||||
if (current) {
|
||||
current->EndQuery();
|
||||
|
||||
// Immediately start a new query to avoid disabling its state.
|
||||
current = cache.Counter(nullptr, type);
|
||||
}
|
||||
last = nullptr;
|
||||
}
|
||||
|
||||
/// Returns the current counter slicing as needed.
|
||||
std::shared_ptr<HostCounter> Current() {
|
||||
if (!current) {
|
||||
return nullptr;
|
||||
}
|
||||
current->EndQuery();
|
||||
last = std::move(current);
|
||||
current = cache.Counter(last, type);
|
||||
return last;
|
||||
}
|
||||
|
||||
/// Returns true when the counter stream is enabled.
|
||||
bool IsEnabled() const {
|
||||
return current != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Enables the stream.
|
||||
void Enable() {
|
||||
if (current) {
|
||||
return;
|
||||
}
|
||||
current = cache.Counter(last, type);
|
||||
}
|
||||
|
||||
// Disables the stream.
|
||||
void Disable() {
|
||||
if (current) {
|
||||
current->EndQuery();
|
||||
}
|
||||
last = std::exchange(current, nullptr);
|
||||
}
|
||||
|
||||
QueryCache& cache;
|
||||
const VideoCore::QueryType type;
|
||||
|
||||
std::shared_ptr<HostCounter> current;
|
||||
std::shared_ptr<HostCounter> last;
|
||||
};
|
||||
|
||||
template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter,
|
||||
class QueryPool>
|
||||
class QueryCacheBase {
|
||||
public:
|
||||
explicit QueryCacheBase(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
|
||||
: system{system}, rasterizer{rasterizer}, streams{{CounterStream{
|
||||
static_cast<QueryCache&>(*this),
|
||||
VideoCore::QueryType::SamplesPassed}}} {}
|
||||
|
||||
void InvalidateRegion(CacheAddr addr, std::size_t size) {
|
||||
std::unique_lock lock{mutex};
|
||||
FlushAndRemoveRegion(addr, size);
|
||||
}
|
||||
|
||||
void FlushRegion(CacheAddr addr, std::size_t size) {
|
||||
std::unique_lock lock{mutex};
|
||||
FlushAndRemoveRegion(addr, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Records a query in GPU mapped memory, potentially marked with a timestamp.
|
||||
* @param gpu_addr GPU address to flush to when the mapped memory is read.
|
||||
* @param type Query type, e.g. SamplesPassed.
|
||||
* @param timestamp Timestamp, when empty the flushed query is assumed to be short.
|
||||
*/
|
||||
void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) {
|
||||
std::unique_lock lock{mutex};
|
||||
auto& memory_manager = system.GPU().MemoryManager();
|
||||
const auto host_ptr = memory_manager.GetPointer(gpu_addr);
|
||||
|
||||
CachedQuery* query = TryGet(ToCacheAddr(host_ptr));
|
||||
if (!query) {
|
||||
const auto cpu_addr = memory_manager.GpuToCpuAddress(gpu_addr);
|
||||
ASSERT_OR_EXECUTE(cpu_addr, return;);
|
||||
|
||||
query = Register(type, *cpu_addr, host_ptr, timestamp.has_value());
|
||||
}
|
||||
|
||||
query->BindCounter(Stream(type).Current(), timestamp);
|
||||
}
|
||||
|
||||
/// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch.
|
||||
void UpdateCounters() {
|
||||
std::unique_lock lock{mutex};
|
||||
const auto& regs = system.GPU().Maxwell3D().regs;
|
||||
Stream(VideoCore::QueryType::SamplesPassed).Update(regs.samplecnt_enable);
|
||||
}
|
||||
|
||||
/// Resets a counter to zero. It doesn't disable the query after resetting.
|
||||
void ResetCounter(VideoCore::QueryType type) {
|
||||
std::unique_lock lock{mutex};
|
||||
Stream(type).Reset();
|
||||
}
|
||||
|
||||
/// Disable all active streams. Expected to be called at the end of a command buffer.
|
||||
void DisableStreams() {
|
||||
std::unique_lock lock{mutex};
|
||||
for (auto& stream : streams) {
|
||||
stream.Update(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new host counter.
|
||||
std::shared_ptr<HostCounter> Counter(std::shared_ptr<HostCounter> dependency,
|
||||
VideoCore::QueryType type) {
|
||||
return std::make_shared<HostCounter>(static_cast<QueryCache&>(*this), std::move(dependency),
|
||||
type);
|
||||
}
|
||||
|
||||
/// Returns the counter stream of the specified type.
|
||||
CounterStream& Stream(VideoCore::QueryType type) {
|
||||
return streams[static_cast<std::size_t>(type)];
|
||||
}
|
||||
|
||||
/// Returns the counter stream of the specified type.
|
||||
const CounterStream& Stream(VideoCore::QueryType type) const {
|
||||
return streams[static_cast<std::size_t>(type)];
|
||||
}
|
||||
|
||||
protected:
|
||||
std::array<QueryPool, VideoCore::NumQueryTypes> query_pools;
|
||||
|
||||
private:
|
||||
/// Flushes a memory range to guest memory and removes it from the cache.
|
||||
void FlushAndRemoveRegion(CacheAddr addr, std::size_t size) {
|
||||
const u64 addr_begin = static_cast<u64>(addr);
|
||||
const u64 addr_end = addr_begin + static_cast<u64>(size);
|
||||
const auto in_range = [addr_begin, addr_end](CachedQuery& query) {
|
||||
const u64 cache_begin = query.GetCacheAddr();
|
||||
const u64 cache_end = cache_begin + query.SizeInBytes();
|
||||
return cache_begin < addr_end && addr_begin < cache_end;
|
||||
};
|
||||
|
||||
const u64 page_end = addr_end >> PAGE_SHIFT;
|
||||
for (u64 page = addr_begin >> PAGE_SHIFT; page <= page_end; ++page) {
|
||||
const auto& it = cached_queries.find(page);
|
||||
if (it == std::end(cached_queries)) {
|
||||
continue;
|
||||
}
|
||||
auto& contents = it->second;
|
||||
for (auto& query : contents) {
|
||||
if (!in_range(query)) {
|
||||
continue;
|
||||
}
|
||||
rasterizer.UpdatePagesCachedCount(query.CpuAddr(), query.SizeInBytes(), -1);
|
||||
query.Flush();
|
||||
}
|
||||
contents.erase(std::remove_if(std::begin(contents), std::end(contents), in_range),
|
||||
std::end(contents));
|
||||
}
|
||||
}
|
||||
|
||||
/// Registers the passed parameters as cached and returns a pointer to the stored cached query.
|
||||
CachedQuery* Register(VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr, bool timestamp) {
|
||||
rasterizer.UpdatePagesCachedCount(cpu_addr, CachedQuery::SizeInBytes(timestamp), 1);
|
||||
const u64 page = static_cast<u64>(ToCacheAddr(host_ptr)) >> PAGE_SHIFT;
|
||||
return &cached_queries[page].emplace_back(static_cast<QueryCache&>(*this), type, cpu_addr,
|
||||
host_ptr);
|
||||
}
|
||||
|
||||
/// Tries to a get a cached query. Returns nullptr on failure.
|
||||
CachedQuery* TryGet(CacheAddr addr) {
|
||||
const u64 page = static_cast<u64>(addr) >> PAGE_SHIFT;
|
||||
const auto it = cached_queries.find(page);
|
||||
if (it == std::end(cached_queries)) {
|
||||
return nullptr;
|
||||
}
|
||||
auto& contents = it->second;
|
||||
const auto found =
|
||||
std::find_if(std::begin(contents), std::end(contents),
|
||||
[addr](auto& query) { return query.GetCacheAddr() == addr; });
|
||||
return found != std::end(contents) ? &*found : nullptr;
|
||||
}
|
||||
|
||||
static constexpr std::uintptr_t PAGE_SIZE = 4096;
|
||||
static constexpr unsigned PAGE_SHIFT = 12;
|
||||
|
||||
Core::System& system;
|
||||
VideoCore::RasterizerInterface& rasterizer;
|
||||
|
||||
std::recursive_mutex mutex;
|
||||
|
||||
std::unordered_map<u64, std::vector<CachedQuery>> cached_queries;
|
||||
|
||||
std::array<CounterStream, VideoCore::NumQueryTypes> streams;
|
||||
};
|
||||
|
||||
template <class QueryCache, class HostCounter>
|
||||
class HostCounterBase {
|
||||
public:
|
||||
explicit HostCounterBase(std::shared_ptr<HostCounter> dependency_)
|
||||
: dependency{std::move(dependency_)}, depth{dependency ? (dependency->Depth() + 1) : 0} {
|
||||
// Avoid nesting too many dependencies to avoid a stack overflow when these are deleted.
|
||||
constexpr u64 depth_threshold = 96;
|
||||
if (depth > depth_threshold) {
|
||||
depth = 0;
|
||||
base_result = dependency->Query();
|
||||
dependency = nullptr;
|
||||
}
|
||||
}
|
||||
virtual ~HostCounterBase() = default;
|
||||
|
||||
/// Returns the current value of the query.
|
||||
u64 Query() {
|
||||
if (result) {
|
||||
return *result;
|
||||
}
|
||||
|
||||
u64 value = BlockingQuery() + base_result;
|
||||
if (dependency) {
|
||||
value += dependency->Query();
|
||||
dependency = nullptr;
|
||||
}
|
||||
|
||||
result = value;
|
||||
return *result;
|
||||
}
|
||||
|
||||
/// Returns true when flushing this query will potentially wait.
|
||||
bool WaitPending() const noexcept {
|
||||
return result.has_value();
|
||||
}
|
||||
|
||||
u64 Depth() const noexcept {
|
||||
return depth;
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Returns the value of query from the backend API blocking as needed.
|
||||
virtual u64 BlockingQuery() const = 0;
|
||||
|
||||
private:
|
||||
std::shared_ptr<HostCounter> dependency; ///< Counter to add to this value.
|
||||
std::optional<u64> result; ///< Filled with the already returned value.
|
||||
u64 depth; ///< Number of nested dependencies.
|
||||
u64 base_result = 0; ///< Equivalent to nested dependencies value.
|
||||
};
|
||||
|
||||
template <class HostCounter>
|
||||
class CachedQueryBase {
|
||||
public:
|
||||
explicit CachedQueryBase(VAddr cpu_addr, u8* host_ptr)
|
||||
: cpu_addr{cpu_addr}, host_ptr{host_ptr} {}
|
||||
virtual ~CachedQueryBase() = default;
|
||||
|
||||
CachedQueryBase(CachedQueryBase&&) noexcept = default;
|
||||
CachedQueryBase(const CachedQueryBase&) = delete;
|
||||
|
||||
CachedQueryBase& operator=(CachedQueryBase&&) noexcept = default;
|
||||
CachedQueryBase& operator=(const CachedQueryBase&) = delete;
|
||||
|
||||
/// Flushes the query to guest memory.
|
||||
virtual void Flush() {
|
||||
// When counter is nullptr it means that it's just been reseted. We are supposed to write a
|
||||
// zero in these cases.
|
||||
const u64 value = counter ? counter->Query() : 0;
|
||||
std::memcpy(host_ptr, &value, sizeof(u64));
|
||||
|
||||
if (timestamp) {
|
||||
std::memcpy(host_ptr + TIMESTAMP_OFFSET, &*timestamp, sizeof(u64));
|
||||
}
|
||||
}
|
||||
|
||||
/// Binds a counter to this query.
|
||||
void BindCounter(std::shared_ptr<HostCounter> counter_, std::optional<u64> timestamp_) {
|
||||
if (counter) {
|
||||
// If there's an old counter set it means the query is being rewritten by the game.
|
||||
// To avoid losing the data forever, flush here.
|
||||
Flush();
|
||||
}
|
||||
counter = std::move(counter_);
|
||||
timestamp = timestamp_;
|
||||
}
|
||||
|
||||
VAddr CpuAddr() const noexcept {
|
||||
return cpu_addr;
|
||||
}
|
||||
|
||||
CacheAddr GetCacheAddr() const noexcept {
|
||||
return ToCacheAddr(host_ptr);
|
||||
}
|
||||
|
||||
u64 SizeInBytes() const noexcept {
|
||||
return SizeInBytes(timestamp.has_value());
|
||||
}
|
||||
|
||||
static constexpr u64 SizeInBytes(bool with_timestamp) noexcept {
|
||||
return with_timestamp ? LARGE_QUERY_SIZE : SMALL_QUERY_SIZE;
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Returns true when querying the counter may potentially block.
|
||||
bool WaitPending() const noexcept {
|
||||
return counter && counter->WaitPending();
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr std::size_t SMALL_QUERY_SIZE = 8; // Query size without timestamp.
|
||||
static constexpr std::size_t LARGE_QUERY_SIZE = 16; // Query size with timestamp.
|
||||
static constexpr std::intptr_t TIMESTAMP_OFFSET = 8; // Timestamp offset in a large query.
|
||||
|
||||
VAddr cpu_addr; ///< Guest CPU address.
|
||||
u8* host_ptr; ///< Writable host pointer.
|
||||
std::shared_ptr<HostCounter> counter; ///< Host counter to query, owns the dependency tree.
|
||||
std::optional<u64> timestamp; ///< Timestamp to flush to guest memory.
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/gpu.h"
|
||||
@@ -17,9 +18,13 @@ class MemoryManager;
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
enum class QueryType {
|
||||
SamplesPassed,
|
||||
};
|
||||
constexpr std::size_t NumQueryTypes = 1;
|
||||
|
||||
enum class LoadCallbackStage {
|
||||
Prepare,
|
||||
Decompile,
|
||||
Build,
|
||||
Complete,
|
||||
};
|
||||
@@ -29,11 +34,8 @@ class RasterizerInterface {
|
||||
public:
|
||||
virtual ~RasterizerInterface() {}
|
||||
|
||||
/// Draw the current batch of vertex arrays
|
||||
virtual bool DrawBatch(bool is_indexed) = 0;
|
||||
|
||||
/// Draw the current batch of multiple instances of vertex arrays
|
||||
virtual bool DrawMultiBatch(bool is_indexed) = 0;
|
||||
/// Dispatches a draw invocation
|
||||
virtual void Draw(bool is_indexed, bool is_instanced) = 0;
|
||||
|
||||
/// Clear the current framebuffer
|
||||
virtual void Clear() = 0;
|
||||
@@ -41,6 +43,12 @@ public:
|
||||
/// Dispatches a compute shader invocation
|
||||
virtual void DispatchCompute(GPUVAddr code_addr) = 0;
|
||||
|
||||
/// Resets the counter of a query
|
||||
virtual void ResetCounter(QueryType type) = 0;
|
||||
|
||||
/// Records a GPU query and caches it
|
||||
virtual void Query(GPUVAddr gpu_addr, QueryType type, std::optional<u64> timestamp) = 0;
|
||||
|
||||
/// Notify rasterizer that all caches should be flushed to Switch memory
|
||||
virtual void FlushAll() = 0;
|
||||
|
||||
@@ -80,6 +88,9 @@ public:
|
||||
virtual void LoadDiskResources(const std::atomic_bool& stop_loading = false,
|
||||
const DiskResourceLoadCallback& callback = {}) {}
|
||||
|
||||
/// Initializes renderer dirty flags
|
||||
virtual void SetupDirtyFlags() {}
|
||||
|
||||
/// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver.
|
||||
GuestDriverProfile& AccessGuestDriverProfile() {
|
||||
return guest_driver_profile;
|
||||
|
||||
@@ -35,15 +35,19 @@ public:
|
||||
explicit RendererBase(Core::Frontend::EmuWindow& window);
|
||||
virtual ~RendererBase();
|
||||
|
||||
/// Swap buffers (render frame)
|
||||
virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0;
|
||||
|
||||
/// Initialize the renderer
|
||||
virtual bool Init() = 0;
|
||||
|
||||
/// Shutdown the renderer
|
||||
virtual void ShutDown() = 0;
|
||||
|
||||
/// Finalize rendering the guest frame and draw into the presentation texture
|
||||
virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0;
|
||||
|
||||
/// Draws the latest frame to the window waiting timeout_ms for a frame to arrive (Renderer
|
||||
/// specific implementation)
|
||||
virtual void TryPresent(int timeout_ms) = 0;
|
||||
|
||||
// Getter/setter functions:
|
||||
// ------------------------
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/renderer_opengl/gl_framebuffer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
@@ -36,8 +35,7 @@ OGLFramebuffer FramebufferCacheOpenGL::CreateFramebuffer(const FramebufferCacheK
|
||||
framebuffer.Create();
|
||||
|
||||
// TODO(Rodrigo): Use DSA here after Nvidia fixes their framebuffer DSA bugs.
|
||||
local_state.draw.draw_framebuffer = framebuffer.handle;
|
||||
local_state.ApplyFramebufferState();
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle);
|
||||
|
||||
if (key.zeta) {
|
||||
const bool stencil = key.zeta->GetSurfaceParams().type == SurfaceType::DepthStencil;
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_cache.h"
|
||||
|
||||
namespace OpenGL {
|
||||
@@ -63,7 +62,6 @@ public:
|
||||
private:
|
||||
OGLFramebuffer CreateFramebuffer(const FramebufferCacheKey& key);
|
||||
|
||||
OpenGLState local_state;
|
||||
std::unordered_map<FramebufferCacheKey, OGLFramebuffer> cache;
|
||||
};
|
||||
|
||||
|
||||
120
src/video_core/renderer_opengl/gl_query_cache.cpp
Normal file
120
src/video_core/renderer_opengl/gl_query_cache.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_query_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::array<GLenum, VideoCore::NumQueryTypes> QueryTargets = {GL_SAMPLES_PASSED};
|
||||
|
||||
constexpr GLenum GetTarget(VideoCore::QueryType type) {
|
||||
return QueryTargets[static_cast<std::size_t>(type)];
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
QueryCache::QueryCache(Core::System& system, RasterizerOpenGL& gl_rasterizer)
|
||||
: VideoCommon::QueryCacheBase<
|
||||
QueryCache, CachedQuery, CounterStream, HostCounter,
|
||||
std::vector<OGLQuery>>{system,
|
||||
static_cast<VideoCore::RasterizerInterface&>(gl_rasterizer)},
|
||||
gl_rasterizer{gl_rasterizer} {}
|
||||
|
||||
QueryCache::~QueryCache() = default;
|
||||
|
||||
OGLQuery QueryCache::AllocateQuery(VideoCore::QueryType type) {
|
||||
auto& reserve = query_pools[static_cast<std::size_t>(type)];
|
||||
OGLQuery query;
|
||||
if (reserve.empty()) {
|
||||
query.Create(GetTarget(type));
|
||||
return query;
|
||||
}
|
||||
|
||||
query = std::move(reserve.back());
|
||||
reserve.pop_back();
|
||||
return query;
|
||||
}
|
||||
|
||||
void QueryCache::Reserve(VideoCore::QueryType type, OGLQuery&& query) {
|
||||
query_pools[static_cast<std::size_t>(type)].push_back(std::move(query));
|
||||
}
|
||||
|
||||
bool QueryCache::AnyCommandQueued() const noexcept {
|
||||
return gl_rasterizer.AnyCommandQueued();
|
||||
}
|
||||
|
||||
HostCounter::HostCounter(QueryCache& cache, std::shared_ptr<HostCounter> dependency,
|
||||
VideoCore::QueryType type)
|
||||
: VideoCommon::HostCounterBase<QueryCache, HostCounter>{std::move(dependency)}, cache{cache},
|
||||
type{type}, query{cache.AllocateQuery(type)} {
|
||||
glBeginQuery(GetTarget(type), query.handle);
|
||||
}
|
||||
|
||||
HostCounter::~HostCounter() {
|
||||
cache.Reserve(type, std::move(query));
|
||||
}
|
||||
|
||||
void HostCounter::EndQuery() {
|
||||
if (!cache.AnyCommandQueued()) {
|
||||
// There are chances a query waited on without commands (glDraw, glClear, glDispatch). Not
|
||||
// having any of these causes a lock. glFlush is considered a command, so we can safely wait
|
||||
// for this. Insert to the OpenGL command stream a flush.
|
||||
glFlush();
|
||||
}
|
||||
glEndQuery(GetTarget(type));
|
||||
}
|
||||
|
||||
u64 HostCounter::BlockingQuery() const {
|
||||
GLint64 value;
|
||||
glGetQueryObjecti64v(query.handle, GL_QUERY_RESULT, &value);
|
||||
return static_cast<u64>(value);
|
||||
}
|
||||
|
||||
CachedQuery::CachedQuery(QueryCache& cache, VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr)
|
||||
: VideoCommon::CachedQueryBase<HostCounter>{cpu_addr, host_ptr}, cache{&cache}, type{type} {}
|
||||
|
||||
CachedQuery::CachedQuery(CachedQuery&& rhs) noexcept
|
||||
: VideoCommon::CachedQueryBase<HostCounter>(std::move(rhs)), cache{rhs.cache}, type{rhs.type} {}
|
||||
|
||||
CachedQuery& CachedQuery::operator=(CachedQuery&& rhs) noexcept {
|
||||
VideoCommon::CachedQueryBase<HostCounter>::operator=(std::move(rhs));
|
||||
cache = rhs.cache;
|
||||
type = rhs.type;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void CachedQuery::Flush() {
|
||||
// Waiting for a query while another query of the same target is enabled locks Nvidia's driver.
|
||||
// To avoid this disable and re-enable keeping the dependency stream.
|
||||
// But we only have to do this if we have pending waits to be done.
|
||||
auto& stream = cache->Stream(type);
|
||||
const bool slice_counter = WaitPending() && stream.IsEnabled();
|
||||
if (slice_counter) {
|
||||
stream.Update(false);
|
||||
}
|
||||
|
||||
VideoCommon::CachedQueryBase<HostCounter>::Flush();
|
||||
|
||||
if (slice_counter) {
|
||||
stream.Update(true);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
78
src/video_core/renderer_opengl/gl_query_cache.h
Normal file
78
src/video_core/renderer_opengl/gl_query_cache.h
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/query_cache.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class CachedQuery;
|
||||
class HostCounter;
|
||||
class QueryCache;
|
||||
class RasterizerOpenGL;
|
||||
|
||||
using CounterStream = VideoCommon::CounterStreamBase<QueryCache, HostCounter>;
|
||||
|
||||
class QueryCache final : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream,
|
||||
HostCounter, std::vector<OGLQuery>> {
|
||||
public:
|
||||
explicit QueryCache(Core::System& system, RasterizerOpenGL& rasterizer);
|
||||
~QueryCache();
|
||||
|
||||
OGLQuery AllocateQuery(VideoCore::QueryType type);
|
||||
|
||||
void Reserve(VideoCore::QueryType type, OGLQuery&& query);
|
||||
|
||||
bool AnyCommandQueued() const noexcept;
|
||||
|
||||
private:
|
||||
RasterizerOpenGL& gl_rasterizer;
|
||||
};
|
||||
|
||||
class HostCounter final : public VideoCommon::HostCounterBase<QueryCache, HostCounter> {
|
||||
public:
|
||||
explicit HostCounter(QueryCache& cache, std::shared_ptr<HostCounter> dependency,
|
||||
VideoCore::QueryType type);
|
||||
~HostCounter();
|
||||
|
||||
void EndQuery();
|
||||
|
||||
private:
|
||||
u64 BlockingQuery() const override;
|
||||
|
||||
QueryCache& cache;
|
||||
const VideoCore::QueryType type;
|
||||
OGLQuery query;
|
||||
};
|
||||
|
||||
class CachedQuery final : public VideoCommon::CachedQueryBase<HostCounter> {
|
||||
public:
|
||||
explicit CachedQuery(QueryCache& cache, VideoCore::QueryType type, VAddr cpu_addr,
|
||||
u8* host_ptr);
|
||||
CachedQuery(CachedQuery&& rhs) noexcept;
|
||||
CachedQuery(const CachedQuery&) = delete;
|
||||
|
||||
CachedQuery& operator=(CachedQuery&& rhs) noexcept;
|
||||
CachedQuery& operator=(const CachedQuery&) = delete;
|
||||
|
||||
void Flush() override;
|
||||
|
||||
private:
|
||||
QueryCache* cache;
|
||||
VideoCore::QueryType type;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,12 +24,13 @@
|
||||
#include "video_core/renderer_opengl/gl_buffer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_device.h"
|
||||
#include "video_core/renderer_opengl/gl_framebuffer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_query_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_sampler_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_state_tracker.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_cache.h"
|
||||
#include "video_core/renderer_opengl/utils.h"
|
||||
#include "video_core/textures/texture.h"
|
||||
@@ -54,13 +55,15 @@ struct DrawParameters;
|
||||
class RasterizerOpenGL : public VideoCore::RasterizerAccelerated {
|
||||
public:
|
||||
explicit RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
|
||||
ScreenInfo& info);
|
||||
ScreenInfo& info, GLShader::ProgramManager& program_manager,
|
||||
StateTracker& state_tracker);
|
||||
~RasterizerOpenGL() override;
|
||||
|
||||
bool DrawBatch(bool is_indexed) override;
|
||||
bool DrawMultiBatch(bool is_indexed) override;
|
||||
void Draw(bool is_indexed, bool is_instanced) override;
|
||||
void Clear() override;
|
||||
void DispatchCompute(GPUVAddr code_addr) override;
|
||||
void ResetCounter(VideoCore::QueryType type) override;
|
||||
void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override;
|
||||
void FlushAll() override;
|
||||
void FlushRegion(CacheAddr addr, u64 size) override;
|
||||
void InvalidateRegion(CacheAddr addr, u64 size) override;
|
||||
@@ -74,13 +77,18 @@ public:
|
||||
u32 pixel_stride) override;
|
||||
void LoadDiskResources(const std::atomic_bool& stop_loading,
|
||||
const VideoCore::DiskResourceLoadCallback& callback) override;
|
||||
void SetupDirtyFlags() override;
|
||||
|
||||
/// Returns true when there are commands queued to the OpenGL server.
|
||||
bool AnyCommandQueued() const {
|
||||
return num_queued_commands > 0;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Configures the color and depth framebuffer states.
|
||||
void ConfigureFramebuffers();
|
||||
|
||||
void ConfigureClearFramebuffer(OpenGLState& current_state, bool using_color_fb,
|
||||
bool using_depth_fb, bool using_stencil_fb);
|
||||
void ConfigureClearFramebuffer(bool using_color_fb, bool using_depth_fb, bool using_stencil_fb);
|
||||
|
||||
/// Configures the current constbuffers to use for the draw command.
|
||||
void SetupDrawConstBuffers(std::size_t stage_index, const Shader& shader);
|
||||
@@ -90,7 +98,7 @@ private:
|
||||
|
||||
/// Configures a constant buffer.
|
||||
void SetupConstBuffer(u32 binding, const Tegra::Engines::ConstBufferInfo& buffer,
|
||||
const GLShader::ConstBufferEntry& entry);
|
||||
const ConstBufferEntry& entry);
|
||||
|
||||
/// Configures the current global memory entries to use for the draw command.
|
||||
void SetupDrawGlobalMemory(std::size_t stage_index, const Shader& shader);
|
||||
@@ -99,12 +107,9 @@ private:
|
||||
void SetupComputeGlobalMemory(const Shader& kernel);
|
||||
|
||||
/// Configures a constant buffer.
|
||||
void SetupGlobalMemory(u32 binding, const GLShader::GlobalMemoryEntry& entry, GPUVAddr gpu_addr,
|
||||
void SetupGlobalMemory(u32 binding, const GlobalMemoryEntry& entry, GPUVAddr gpu_addr,
|
||||
std::size_t size);
|
||||
|
||||
/// Syncs all the state, shaders, render targets and textures setting before a draw call.
|
||||
void Draw(bool is_indexed, bool is_instanced);
|
||||
|
||||
/// Configures the current textures to use for the draw command.
|
||||
void SetupDrawTextures(std::size_t stage_index, const Shader& shader);
|
||||
|
||||
@@ -113,7 +118,7 @@ private:
|
||||
|
||||
/// Configures a texture.
|
||||
void SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
|
||||
const GLShader::SamplerEntry& entry);
|
||||
const SamplerEntry& entry);
|
||||
|
||||
/// Configures images in a graphics shader.
|
||||
void SetupDrawImages(std::size_t stage_index, const Shader& shader);
|
||||
@@ -122,15 +127,16 @@ private:
|
||||
void SetupComputeImages(const Shader& shader);
|
||||
|
||||
/// Configures an image.
|
||||
void SetupImage(u32 binding, const Tegra::Texture::TICEntry& tic,
|
||||
const GLShader::ImageEntry& entry);
|
||||
void SetupImage(u32 binding, const Tegra::Texture::TICEntry& tic, const ImageEntry& entry);
|
||||
|
||||
/// Syncs the viewport and depth range to match the guest state
|
||||
void SyncViewport(OpenGLState& current_state);
|
||||
void SyncViewport();
|
||||
|
||||
/// Syncs the depth clamp state
|
||||
void SyncDepthClamp();
|
||||
|
||||
/// Syncs the clip enabled status to match the guest state
|
||||
void SyncClipEnabled(
|
||||
const std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances>& clip_mask);
|
||||
void SyncClipEnabled(u32 clip_mask);
|
||||
|
||||
/// Syncs the clip coefficients to match the guest state
|
||||
void SyncClipCoef();
|
||||
@@ -160,16 +166,16 @@ private:
|
||||
void SyncMultiSampleState();
|
||||
|
||||
/// Syncs the scissor test state to match the guest state
|
||||
void SyncScissorTest(OpenGLState& current_state);
|
||||
|
||||
/// Syncs the transform feedback state to match the guest state
|
||||
void SyncTransformFeedback();
|
||||
void SyncScissorTest();
|
||||
|
||||
/// Syncs the point state to match the guest state
|
||||
void SyncPointState();
|
||||
|
||||
/// Syncs the rasterizer enable state to match the guest state
|
||||
void SyncRasterizeEnable(OpenGLState& current_state);
|
||||
void SyncRasterizeEnable();
|
||||
|
||||
/// Syncs polygon modes to match the guest state
|
||||
void SyncPolygonModes();
|
||||
|
||||
/// Syncs Color Mask
|
||||
void SyncColorMask();
|
||||
@@ -180,47 +186,61 @@ private:
|
||||
/// Syncs the alpha test state to match the guest state
|
||||
void SyncAlphaTest();
|
||||
|
||||
/// Check for extension that are not strictly required
|
||||
/// but are needed for correct emulation
|
||||
/// Syncs the framebuffer sRGB state to match the guest state
|
||||
void SyncFramebufferSRGB();
|
||||
|
||||
/// Begin a transform feedback
|
||||
void BeginTransformFeedback(GLenum primitive_mode);
|
||||
|
||||
/// End a transform feedback
|
||||
void EndTransformFeedback();
|
||||
|
||||
/// Check for extension that are not strictly required but are needed for correct emulation
|
||||
void CheckExtensions();
|
||||
|
||||
const Device device;
|
||||
OpenGLState state;
|
||||
|
||||
TextureCacheOpenGL texture_cache;
|
||||
ShaderCacheOpenGL shader_cache;
|
||||
SamplerCacheOpenGL sampler_cache;
|
||||
FramebufferCacheOpenGL framebuffer_cache;
|
||||
|
||||
Core::System& system;
|
||||
ScreenInfo& screen_info;
|
||||
|
||||
std::unique_ptr<GLShader::ProgramManager> shader_program_manager;
|
||||
std::map<std::array<Tegra::Engines::Maxwell3D::Regs::VertexAttribute,
|
||||
Tegra::Engines::Maxwell3D::Regs::NumVertexAttributes>,
|
||||
OGLVertexArray>
|
||||
vertex_array_cache;
|
||||
|
||||
static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
|
||||
OGLBufferCache buffer_cache;
|
||||
|
||||
VertexArrayPushBuffer vertex_array_pushbuffer;
|
||||
BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER};
|
||||
BindBuffersRangePushBuffer bind_ssbo_pushbuffer{GL_SHADER_STORAGE_BUFFER};
|
||||
|
||||
std::size_t CalculateVertexArraysSize() const;
|
||||
|
||||
std::size_t CalculateIndexBufferSize() const;
|
||||
|
||||
/// Updates and returns a vertex array object representing current vertex format
|
||||
GLuint SetupVertexFormat();
|
||||
/// Updates the current vertex format
|
||||
void SetupVertexFormat();
|
||||
|
||||
void SetupVertexBuffer(GLuint vao);
|
||||
void SetupVertexInstances(GLuint vao);
|
||||
void SetupVertexBuffer();
|
||||
void SetupVertexInstances();
|
||||
|
||||
GLintptr SetupIndexBuffer();
|
||||
|
||||
void SetupShaders(GLenum primitive_mode);
|
||||
|
||||
const Device device;
|
||||
|
||||
TextureCacheOpenGL texture_cache;
|
||||
ShaderCacheOpenGL shader_cache;
|
||||
SamplerCacheOpenGL sampler_cache;
|
||||
FramebufferCacheOpenGL framebuffer_cache;
|
||||
QueryCache query_cache;
|
||||
|
||||
Core::System& system;
|
||||
ScreenInfo& screen_info;
|
||||
GLShader::ProgramManager& program_manager;
|
||||
StateTracker& state_tracker;
|
||||
|
||||
static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
|
||||
OGLBufferCache buffer_cache;
|
||||
|
||||
VertexArrayPushBuffer vertex_array_pushbuffer{state_tracker};
|
||||
BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER};
|
||||
BindBuffersRangePushBuffer bind_ssbo_pushbuffer{GL_SHADER_STORAGE_BUFFER};
|
||||
|
||||
std::array<OGLBuffer, Tegra::Engines::Maxwell3D::Regs::NumTransformFeedbackBuffers>
|
||||
transform_feedback_buffers;
|
||||
std::bitset<Tegra::Engines::Maxwell3D::Regs::NumTransformFeedbackBuffers>
|
||||
enabled_transform_feedback_buffers;
|
||||
|
||||
/// Number of commands queued to the OpenGL driver. Reseted on flush.
|
||||
std::size_t num_queued_commands = 0;
|
||||
|
||||
u32 last_clip_distance_mask = 0;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user