Compare commits
411 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f8d6bc504 | |||
| 2f45e999d8 | |||
| d44464829b | |||
| efe50d88ec | |||
| 21f5912ec9 | |||
| 8d46c3cc66 | |||
| 7712e46d64 | |||
| 92b2e92620 | |||
| 2984695265 | |||
| c9260a75f6 | |||
| 1a16d055df | |||
| 83a84f1c2d | |||
| 90a4591563 | |||
| 768fdb269e | |||
| c48b9668f0 | |||
| 027ff7847c | |||
| 4514325b9c | |||
| b5fd9c58cd | |||
| 910a0fa58e | |||
| 1e21f5f872 | |||
| e1201abc1e | |||
| c3242abe95 | |||
| d81cdd9114 | |||
| 45bcd7f9b8 | |||
| 4390370a19 | |||
| 54c7af9902 | |||
| b57d61010f | |||
| eaca010ee9 | |||
| c5d22952bf | |||
| 81806603eb | |||
| ca9da569ce | |||
| 6705439cf3 | |||
| 3799c820ca | |||
| 1105614b86 | |||
| 6a1ad03153 | |||
| fc01074f89 | |||
| bcd666b86b | |||
| d0c7c3f64f | |||
| 862dddf8c9 | |||
| d134ca68c6 | |||
| e35c2fd5d0 | |||
| 29f9a454eb | |||
| 41b65d38fa | |||
| fc9abd3c62 | |||
| c001a2af25 | |||
| 3d24eb54ec | |||
| ab93b4c66d | |||
| 49eb78497b | |||
| 6a4ab3e0d2 | |||
| b720009dc0 | |||
| 36910e9020 | |||
| 0456ed6b4e | |||
| ec4d7f71fe | |||
| 25db62ce15 | |||
| dfe11d72e3 | |||
| d94dcaefa0 | |||
| f0340b8d22 | |||
| ee0547e4c4 | |||
| cb30fe50cd | |||
| 3271099fea | |||
| 5cb1c2ad84 | |||
| 928380ebf9 | |||
| 0ec5b9bff2 | |||
| 7ad17ae397 | |||
| edbfbf2f2f | |||
| fbefcf7280 | |||
| 16bf50e610 | |||
| fc8aef7323 | |||
| 8ec32e9755 | |||
| 03186af6a1 | |||
| d68eb751c5 | |||
| 694c078655 | |||
| 9484519ec3 | |||
| 064aa3de11 | |||
| ade44ac2ea | |||
| f785f73e92 | |||
| 09400e4f4e | |||
| 76d83ffbec | |||
| 72add82ee9 | |||
| 995e27e9b7 | |||
| 79e8f4abe8 | |||
| 404a94a199 | |||
| 40d31b8b8a | |||
| b9f06bd599 | |||
| 330b31ae2e | |||
| 50e9ba34b4 | |||
| a28a10bc54 | |||
| cd9345e10c | |||
| 5c4ed30c21 | |||
| e2a86e2c8a | |||
| 2dba59d1ff | |||
| 9ba6bab920 | |||
| f56226e17f | |||
| 2ce0410f2c | |||
| cd4aa50207 | |||
| e5c83b5a3e | |||
| 7432343214 | |||
| 8f32bab4c9 | |||
| 26a9dc3f65 | |||
| 360a97ab54 | |||
| 540ed72e99 | |||
| c81793029d | |||
| 1e66eaf929 | |||
| 7ddccb55d2 | |||
| 34dc1c8bc2 | |||
| 1ed22b4613 | |||
| 8a6e6465a7 | |||
| 35ed051742 | |||
| 5bd0c1517f | |||
| 6267110b69 | |||
| 4e766280c4 | |||
| 3d2d77eb3f | |||
| e394e1ecc4 | |||
| 90a0506d56 | |||
| ad58d7eae7 | |||
| d0a5a48948 | |||
| 4258d515e6 | |||
| f54280dafd | |||
| 11099dda2e | |||
| 64a68ccbb4 | |||
| 4790ba7839 | |||
| 1900abde13 | |||
| 60b5670577 | |||
| b00406c8e4 | |||
| 8dea7fa129 | |||
| 2241d8c971 | |||
| beaf7654bb | |||
| 0dec42431f | |||
| fea05a17ea | |||
| e4c63d432d | |||
| f300a1d54b | |||
| 3a1a3dd0db | |||
| 74e6e3623f | |||
| 8a244dd3d3 | |||
| fd1cef5616 | |||
| adcac857f8 | |||
| f6a049337e | |||
| 40050c1188 | |||
| 9bf7ad97f5 | |||
| 5723145165 | |||
| 84cc22b21b | |||
| c93dd45997 | |||
| a1c4bca908 | |||
| 432f4441b9 | |||
| 306b3491c4 | |||
| 8dbad556ec | |||
| a8a4f37628 | |||
| 81d1a1133d | |||
| cfd9f7d25b | |||
| a05d9405b9 | |||
| 74f80299b0 | |||
| f11eefed56 | |||
| 6b873b72ae | |||
| a3c81745b1 | |||
| d8486a9968 | |||
| 9b38c8ef08 | |||
| e7af84670d | |||
| b46ec4efea | |||
| 4f9f55ec21 | |||
| 3442365127 | |||
| 651358d0b6 | |||
| 187c9d7e33 | |||
| 0849be094e | |||
| 8bb39750a1 | |||
| 12e7d3b254 | |||
| 51dd3da11c | |||
| 87eb3cb083 | |||
| b084a9bf0a | |||
| 2f12caccf9 | |||
| ebf19616f4 | |||
| b998aa5504 | |||
| fdde08bd01 | |||
| 2136ebccd6 | |||
| 59add00d4a | |||
| e791da9791 | |||
| 07add23251 | |||
| ee25e0a40b | |||
| 0cee5e1af8 | |||
| ffcaf5af90 | |||
| 5b57ee66ce | |||
| f68be36159 | |||
| 2935c9d8de | |||
| 264bb5abf7 | |||
| 0137f2e6e1 | |||
| 6d8e498f76 | |||
| b8b1b58f36 | |||
| 8433edacb3 | |||
| 68c8a1b170 | |||
| 7d133fd37e | |||
| cef7649bed | |||
| 615fb40416 | |||
| d990f043bd | |||
| 12bf8ca929 | |||
| 8dd6dc9a29 | |||
| 43039d95ac | |||
| 7ba6f68a8d | |||
| 19c11e2fd2 | |||
| 888bc10c17 | |||
| 54150f5afa | |||
| 3da43ad2a9 | |||
| 0f7337c522 | |||
| 29e3636e0a | |||
| a271cf89cf | |||
| 88d9b55b14 | |||
| 03cf308c16 | |||
| d63d8bf7f9 | |||
| a396473201 | |||
| ef7c50b276 | |||
| f6815086a1 | |||
| 91ff6d4cb3 | |||
| 46a620f9d7 | |||
| 0b37e7cb39 | |||
| 384e24d3e9 | |||
| ad53dc22fd | |||
| 5ffec69dc7 | |||
| 11a380c3da | |||
| f6cbb14dce | |||
| e781f6e767 | |||
| 55ef89a9c7 | |||
| d24a4b79d4 | |||
| dc471700ca | |||
| 490df43f34 | |||
| 8fa0416fee | |||
| d15814d39f | |||
| b5e83bcc7b | |||
| fc3eb72359 | |||
| eceee8e5f4 | |||
| 101d86897b | |||
| 1dbb9e353e | |||
| 5eb0b65590 | |||
| 36144a5690 | |||
| a943600019 | |||
| 45ac3f414e | |||
| b92198b7f8 | |||
| cf4893410b | |||
| 345bcd4cdf | |||
| 84786dde00 | |||
| ca2d904770 | |||
| 3431e0acf5 | |||
| a99f84f4b2 | |||
| 8eddafd9d0 | |||
| 78e233c460 | |||
| 789af19c60 | |||
| c624edceba | |||
| 9eab07f863 | |||
| a5bff8e9b3 | |||
| 850896a52b | |||
| c8b3a12856 | |||
| 92d676d788 | |||
| 419f427a01 | |||
| 480b03b645 | |||
| 5eda606952 | |||
| b54cbc985e | |||
| 7978ea4e8c | |||
| 4064e03568 | |||
| f499c8177e | |||
| d8b3f665db | |||
| 03884b7ea6 | |||
| c905044e1b | |||
| bf32fcc817 | |||
| f6de57c1a5 | |||
| cc112f971e | |||
| 5ce5432830 | |||
| b2d45a4072 | |||
| 07e477f891 | |||
| 2147240e47 | |||
| f1aa7ff893 | |||
| b3bcf0fa88 | |||
| 8185509683 | |||
| d348070226 | |||
| eb7197eb47 | |||
| 49a0e4330e | |||
| 6ac44f3bdc | |||
| 6123b6ea45 | |||
| 081669c334 | |||
| 599c0763e5 | |||
| c8b358dba2 | |||
| d92b5fc435 | |||
| c65c651b6f | |||
| 36da4d1121 | |||
| b3308830b2 | |||
| 18adea343e | |||
| 09f4f3f23b | |||
| a1054a093c | |||
| 099dd0c0d2 | |||
| acbfb0083a | |||
| af4696657c | |||
| ea089c012e | |||
| b9a313057e | |||
| 42f653ab6f | |||
| 26f4e92c1f | |||
| 873d26b335 | |||
| d05675242a | |||
| 72c8a94a6c | |||
| b94e947793 | |||
| 50a7152941 | |||
| 4b4af29f86 | |||
| a95c49e7d0 | |||
| 2897ddb8f5 | |||
| b590e5572d | |||
| f46e1b8ca2 | |||
| 7116a7d28b | |||
| 37a3b89e7a | |||
| 7c88f4de2d | |||
| 62c13e8318 | |||
| 41bbb31af4 | |||
| 524674aafc | |||
| 6af82fd070 | |||
| afb06b1495 | |||
| 118d5fa3b0 | |||
| c68a0d17fc | |||
| c17938f96b | |||
| f58ee3f15f | |||
| 7652543231 | |||
| e773354477 | |||
| abbbdc2bc0 | |||
| 05b7b177f6 | |||
| b89d096652 | |||
| d866916f42 | |||
| da8e0f6571 | |||
| 3fa9702952 | |||
| ae7da0b12d | |||
| 7a13a515d9 | |||
| 133f497f84 | |||
| a9ad983c13 | |||
| 214b9fc9a7 | |||
| 951c61aeaa | |||
| 285b6dbc39 | |||
| eb7d361657 | |||
| 2428214c4b | |||
| af89f7683d | |||
| 667a8ae163 | |||
| 3a89723d97 | |||
| 9ee5c4ec56 | |||
| 5e58271903 | |||
| 8c907c620d | |||
| b84d429c2e | |||
| 9f34be5a61 | |||
| 14ac0c2923 | |||
| 279c7bcc1a | |||
| c7235e67ef | |||
| 840d5520d2 | |||
| 1eec9590c3 | |||
| e18bf4b062 | |||
| 88e1e7f14b | |||
| 091463a429 | |||
| f67605e6aa | |||
| 9a0648ff0a | |||
| c9e4acc4e2 | |||
| 292dfac25e | |||
| 37addf7a94 | |||
| 640fc1418b | |||
| 8a48c4ed1c | |||
| f9e0681d59 | |||
| cbc0f0a66e | |||
| 481b210c0d | |||
| 4e7a6639d2 | |||
| a0c7d93b84 | |||
| 516325eba8 | |||
| e0193e2be5 | |||
| b85f5b1332 | |||
| 648c7b4ed6 | |||
| 14fc1bec17 | |||
| fa7abafa5f | |||
| 36df305b13 | |||
| f1eff447bb | |||
| 49e3c073a5 | |||
| 6991d447d4 | |||
| b30a1d49ff | |||
| 78759d98dc | |||
| 5e24f7ed31 | |||
| cf221ca92d | |||
| 59c6f45e7a | |||
| caf38725ae | |||
| ee6d40d414 | |||
| eb4ea7e5c7 | |||
| 3074b2eb93 | |||
| 39bb6851e4 | |||
| 55650c5b75 | |||
| e57b13ad94 | |||
| 1a9576fdff | |||
| 8e33cf1c2b | |||
| 6fb212784e | |||
| 2030522d86 | |||
| 1490b49fa9 | |||
| 212b497d5c | |||
| 7feac8ba46 | |||
| 6c00151d17 | |||
| 04b4f3b051 | |||
| 14d2c77f91 | |||
| 6430fc29a9 | |||
| c73841500a | |||
| 11f4bf8a9a | |||
| 9b3611eb8d | |||
| e610485cd2 | |||
| c82e6dc810 | |||
| 476637d143 | |||
| 3ae9258efe | |||
| 3f765ea9a4 | |||
| 316f80af87 | |||
| 228a381aed | |||
| c22c4f5d59 | |||
| 218d790bd6 | |||
| b8f3e5157b | |||
| 35d94dcb2b | |||
| 4a13f9eecd | |||
| ad99bbf5fe | |||
| b387a26f30 | |||
| 75c4aec8ab | |||
| 660c6bec22 | |||
| 04fa990b0c |
@@ -41,12 +41,11 @@ for i in package/*.exe; do
|
||||
done
|
||||
|
||||
pip3 install pefile
|
||||
python3 .ci/scripts/windows/scan_dll.py package/*.exe "package/"
|
||||
python3 .ci/scripts/windows/scan_dll.py package/imageformats/*.dll "package/"
|
||||
python3 .ci/scripts/windows/scan_dll.py package/*.exe package/imageformats/*.dll "package/"
|
||||
|
||||
# copy FFmpeg libraries
|
||||
EXTERNALS_PATH="$(pwd)/build/externals"
|
||||
FFMPEG_DLL_PATH="$(find ${EXTERNALS_PATH} -maxdepth 1 -type d | grep ffmpeg)/bin"
|
||||
FFMPEG_DLL_PATH="$(find "${EXTERNALS_PATH}" -maxdepth 1 -type d | grep 'ffmpeg-')/bin"
|
||||
find ${FFMPEG_DLL_PATH} -type f -regex ".*\.dll" -exec cp -v {} package/ ';'
|
||||
|
||||
# copy libraries from yuzu.exe path
|
||||
|
||||
+3
-3
@@ -34,12 +34,12 @@
|
||||
[submodule "opus"]
|
||||
path = externals/opus/opus
|
||||
url = https://github.com/xiph/opus.git
|
||||
[submodule "ffmpeg"]
|
||||
path = externals/ffmpeg
|
||||
url = https://git.ffmpeg.org/ffmpeg.git
|
||||
[submodule "SDL"]
|
||||
path = externals/SDL
|
||||
url = https://github.com/libsdl-org/SDL.git
|
||||
[submodule "externals/cpp-httplib"]
|
||||
path = externals/cpp-httplib
|
||||
url = https://github.com/yhirose/cpp-httplib.git
|
||||
[submodule "externals/ffmpeg/ffmpeg"]
|
||||
path = externals/ffmpeg/ffmpeg
|
||||
url = https://git.ffmpeg.org/ffmpeg.git
|
||||
|
||||
+15
-221
@@ -33,6 +33,10 @@ option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
|
||||
|
||||
option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
|
||||
|
||||
option(YUZU_USE_BUNDLED_OPUS "Compile bundled opus" ON)
|
||||
|
||||
option(YUZU_TESTS "Compile tests" ON)
|
||||
|
||||
# Default to a Release build
|
||||
get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE)
|
||||
@@ -166,8 +170,7 @@ macro(yuzu_find_packages)
|
||||
# Capitalization matters here. We need the naming to match the generated paths from Conan
|
||||
set(REQUIRED_LIBS
|
||||
# Cmake Pkg Prefix Version Conan Pkg
|
||||
"Catch2 2.13.7 catch2/2.13.7"
|
||||
"fmt 8.0.1 fmt/8.0.1"
|
||||
"fmt 8.0.1 fmt/8.1.1"
|
||||
"lz4 1.8 lz4/1.9.2"
|
||||
"nlohmann_json 3.8 nlohmann_json/3.8.0"
|
||||
"ZLIB 1.2 zlib/1.2.11"
|
||||
@@ -175,6 +178,11 @@ macro(yuzu_find_packages)
|
||||
# can't use opus until AVX check is fixed: https://github.com/yuzu-emu/yuzu/pull/4068
|
||||
#"opus 1.3 opus/1.3.1"
|
||||
)
|
||||
if (YUZU_TESTS)
|
||||
list(APPEND REQUIRED_LIBS
|
||||
"Catch2 2.13.7 catch2/2.13.7"
|
||||
)
|
||||
endif()
|
||||
|
||||
foreach(PACKAGE ${REQUIRED_LIBS})
|
||||
string(REGEX REPLACE "[ \t\r\n]+" ";" PACKAGE_SPLIT ${PACKAGE})
|
||||
@@ -229,7 +237,7 @@ elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR YUZU_USE_BUNDLED_BOOST)
|
||||
include_directories(SYSTEM "${Boost_INCLUDE_DIRS}")
|
||||
else()
|
||||
message(STATUS "Boost 1.73.0 or newer not found, falling back to Conan")
|
||||
list(APPEND CONAN_REQUIRED_LIBS "boost/1.73.0")
|
||||
list(APPEND CONAN_REQUIRED_LIBS "boost/1.78.0")
|
||||
endif()
|
||||
|
||||
# Attempt to locate any packages that are required and report the missing ones in CONAN_REQUIRED_LIBS
|
||||
@@ -249,7 +257,7 @@ if(ENABLE_QT)
|
||||
# Check for system Qt on Linux, fallback to bundled Qt
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
if (NOT YUZU_USE_BUNDLED_QT)
|
||||
find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets)
|
||||
find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus)
|
||||
endif()
|
||||
if (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT)
|
||||
# Check for dependencies, then enable bundled Qt download
|
||||
@@ -508,13 +516,13 @@ set(FFmpeg_COMPONENTS
|
||||
avutil
|
||||
swscale)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
if (UNIX AND NOT APPLE)
|
||||
Include(FindPkgConfig REQUIRED)
|
||||
pkg_check_modules(LIBVA libva)
|
||||
endif()
|
||||
if (NOT YUZU_USE_BUNDLED_FFMPEG)
|
||||
# Use system installed FFmpeg
|
||||
find_package(FFmpeg QUIET COMPONENTS ${FFmpeg_COMPONENTS})
|
||||
find_package(FFmpeg 4.3 QUIET COMPONENTS ${FFmpeg_COMPONENTS})
|
||||
|
||||
if (FFmpeg_FOUND)
|
||||
# Overwrite aggregate defines from FFmpeg module to avoid over-linking libraries.
|
||||
@@ -527,225 +535,11 @@ if (NOT YUZU_USE_BUNDLED_FFMPEG)
|
||||
set(FFmpeg_INCLUDE_DIR ${FFmpeg_INCLUDE_DIR} ${FFmpeg_INCLUDE_${COMPONENT}} CACHE PATH "Path to FFmpeg headers" FORCE)
|
||||
endforeach()
|
||||
else()
|
||||
message(WARNING "FFmpeg not found, falling back to externals")
|
||||
message(WARNING "FFmpeg not found or too old, falling back to externals")
|
||||
set(YUZU_USE_BUNDLED_FFMPEG ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (YUZU_USE_BUNDLED_FFMPEG)
|
||||
if (NOT WIN32)
|
||||
# TODO(lat9nq): Move this to externals/ffmpeg/CMakeLists.txt (and move externals/ffmpeg to
|
||||
# externals/ffmpeg/ffmpeg)
|
||||
|
||||
# Build FFmpeg from externals
|
||||
message(STATUS "Using FFmpeg from externals")
|
||||
|
||||
# FFmpeg has source that requires one of nasm or yasm to assemble it.
|
||||
# REQUIRED throws an error if not found here during configuration rather than during compilation.
|
||||
find_program(ASSEMBLER NAMES nasm yasm)
|
||||
if ("${ASSEMBLER}" STREQUAL "ASSEMBLER-NOTFOUND")
|
||||
message(FATAL_ERROR "One of either `nasm` or `yasm` not found but is required.")
|
||||
endif()
|
||||
|
||||
find_program(AUTOCONF autoconf)
|
||||
if ("${AUTOCONF}" STREQUAL "AUTOCONF-NOTFOUND")
|
||||
message(FATAL_ERROR "Required program `autoconf` not found.")
|
||||
endif()
|
||||
|
||||
set(FFmpeg_PREFIX ${PROJECT_SOURCE_DIR}/externals/ffmpeg)
|
||||
set(FFmpeg_BUILD_DIR ${PROJECT_BINARY_DIR}/externals/ffmpeg)
|
||||
set(FFmpeg_MAKEFILE ${FFmpeg_BUILD_DIR}/Makefile)
|
||||
make_directory(${FFmpeg_BUILD_DIR})
|
||||
|
||||
# Read version string from external
|
||||
file(READ ${FFmpeg_PREFIX}/RELEASE FFmpeg_VERSION)
|
||||
set(FFmpeg_FOUND NO)
|
||||
if (NOT FFmpeg_VERSION STREQUAL "")
|
||||
set(FFmpeg_FOUND YES)
|
||||
endif()
|
||||
|
||||
unset(FFmpeg_LIBRARIES CACHE)
|
||||
foreach(COMPONENT ${FFmpeg_COMPONENTS})
|
||||
set(FFmpeg_${COMPONENT}_PREFIX "${FFmpeg_BUILD_DIR}/lib${COMPONENT}")
|
||||
set(FFmpeg_${COMPONENT}_LIB_NAME "lib${COMPONENT}.a")
|
||||
set(FFmpeg_${COMPONENT}_LIBRARY "${FFmpeg_${COMPONENT}_PREFIX}/${FFmpeg_${COMPONENT}_LIB_NAME}")
|
||||
|
||||
set(FFmpeg_LIBRARIES
|
||||
${FFmpeg_LIBRARIES}
|
||||
${FFmpeg_${COMPONENT}_LIBRARY}
|
||||
CACHE PATH "Paths to FFmpeg libraries" FORCE)
|
||||
endforeach()
|
||||
|
||||
Include(FindPkgConfig REQUIRED)
|
||||
pkg_check_modules(LIBVA libva)
|
||||
pkg_check_modules(CUDA cuda)
|
||||
pkg_check_modules(FFNVCODEC ffnvcodec)
|
||||
pkg_check_modules(VDPAU vdpau)
|
||||
|
||||
set(FFmpeg_HWACCEL_LIBRARIES)
|
||||
set(FFmpeg_HWACCEL_FLAGS)
|
||||
set(FFmpeg_HWACCEL_INCLUDE_DIRS)
|
||||
set(FFmpeg_HWACCEL_LDFLAGS)
|
||||
|
||||
if(LIBVA_FOUND)
|
||||
pkg_check_modules(LIBDRM libdrm REQUIRED)
|
||||
find_package(X11 REQUIRED)
|
||||
pkg_check_modules(LIBVA-DRM libva-drm REQUIRED)
|
||||
pkg_check_modules(LIBVA-X11 libva-x11 REQUIRED)
|
||||
list(APPEND FFmpeg_HWACCEL_LIBRARIES
|
||||
${LIBDRM_LIBRARIES}
|
||||
${X11_LIBRARIES}
|
||||
${LIBVA-DRM_LIBRARIES}
|
||||
${LIBVA-X11_LIBRARIES}
|
||||
${LIBVA_LIBRARIES})
|
||||
set(FFmpeg_HWACCEL_FLAGS
|
||||
--enable-hwaccel=h264_vaapi
|
||||
--enable-hwaccel=vp8_vaapi
|
||||
--enable-hwaccel=vp9_vaapi
|
||||
--enable-libdrm)
|
||||
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
|
||||
${LIBDRM_INCLUDE_DIRS}
|
||||
${X11_INCLUDE_DIRS}
|
||||
${LIBVA-DRM_INCLUDE_DIRS}
|
||||
${LIBVA-X11_INCLUDE_DIRS}
|
||||
${LIBVA_INCLUDE_DIRS}
|
||||
)
|
||||
message(STATUS "VA-API found")
|
||||
else()
|
||||
set(FFmpeg_HWACCEL_FLAGS --disable-vaapi)
|
||||
endif()
|
||||
|
||||
if (FFNVCODEC_FOUND AND CUDA_FOUND)
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS
|
||||
--enable-cuvid
|
||||
--enable-ffnvcodec
|
||||
--enable-nvdec
|
||||
--enable-hwaccel=h264_nvdec
|
||||
--enable-hwaccel=vp8_nvdec
|
||||
--enable-hwaccel=vp9_nvdec
|
||||
--extra-cflags=-I${CUDA_INCLUDE_DIRS}
|
||||
)
|
||||
list(APPEND FFmpeg_HWACCEL_LIBRARIES
|
||||
${FFNVCODEC_LIBRARIES}
|
||||
${CUDA_LIBRARIES}
|
||||
)
|
||||
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
|
||||
${FFNVCODEC_INCLUDE_DIRS}
|
||||
${CUDA_INCLUDE_DIRS}
|
||||
)
|
||||
list(APPEND FFmpeg_HWACCEL_LDFLAGS
|
||||
${FFNVCODEC_LDFLAGS}
|
||||
${CUDA_LDFLAGS}
|
||||
)
|
||||
message(STATUS "ffnvcodec libraries version ${FFNVCODEC_VERSION} found")
|
||||
endif()
|
||||
|
||||
if (VDPAU_FOUND)
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS
|
||||
--enable-vdpau
|
||||
--enable-hwaccel=h264_vdpau
|
||||
--enable-hwaccel=vp9_vdpau
|
||||
)
|
||||
list(APPEND FFmpeg_HWACCEL_LIBRARIES ${VDPAU_LIBRARIES})
|
||||
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${VDPAU_INCLUDE_DIRS})
|
||||
list(APPEND FFmpeg_HWACCEL_LDFLAGS ${VDPAU_LDFLAGS})
|
||||
message(STATUS "vdpau libraries version ${VDPAU_VERSION} found")
|
||||
else()
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vdpau)
|
||||
endif()
|
||||
|
||||
# `configure` parameters builds only exactly what yuzu needs from FFmpeg
|
||||
# `--disable-vdpau` is needed to avoid linking issues
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${FFmpeg_MAKEFILE}
|
||||
COMMAND
|
||||
/bin/bash ${FFmpeg_PREFIX}/configure
|
||||
--disable-avdevice
|
||||
--disable-avfilter
|
||||
--disable-avformat
|
||||
--disable-doc
|
||||
--disable-everything
|
||||
--disable-ffmpeg
|
||||
--disable-ffprobe
|
||||
--disable-network
|
||||
--disable-postproc
|
||||
--disable-swresample
|
||||
--enable-decoder=h264
|
||||
--enable-decoder=vp8
|
||||
--enable-decoder=vp9
|
||||
--cc="${CMAKE_C_COMPILER}"
|
||||
--cxx="${CMAKE_CXX_COMPILER}"
|
||||
${FFmpeg_HWACCEL_FLAGS}
|
||||
WORKING_DIRECTORY
|
||||
${FFmpeg_BUILD_DIR}
|
||||
)
|
||||
unset(FFmpeg_HWACCEL_FLAGS)
|
||||
|
||||
# Workaround for Ubuntu 18.04's older version of make not being able to call make as a child
|
||||
# with context of the jobserver. Also helps ninja users.
|
||||
execute_process(
|
||||
COMMAND
|
||||
nproc
|
||||
OUTPUT_VARIABLE
|
||||
SYSTEM_THREADS)
|
||||
|
||||
set(FFmpeg_BUILD_LIBRARIES ${FFmpeg_LIBRARIES})
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${FFmpeg_BUILD_LIBRARIES}
|
||||
COMMAND
|
||||
make -j${SYSTEM_THREADS}
|
||||
WORKING_DIRECTORY
|
||||
${FFmpeg_BUILD_DIR}
|
||||
)
|
||||
|
||||
set(FFmpeg_INCLUDE_DIR
|
||||
"${FFmpeg_PREFIX};${FFmpeg_BUILD_DIR};${FFmpeg_HWACCEL_INCLUDE_DIRS}"
|
||||
CACHE PATH "Path to FFmpeg headers" FORCE)
|
||||
|
||||
set(FFmpeg_LDFLAGS
|
||||
"${FFmpeg_HWACCEL_LDFLAGS}"
|
||||
CACHE STRING "FFmpeg linker flags" FORCE)
|
||||
|
||||
# ALL makes this custom target build every time
|
||||
# but it won't actually build if the DEPENDS parameter is up to date
|
||||
add_custom_target(ffmpeg-configure ALL DEPENDS ${FFmpeg_MAKEFILE})
|
||||
add_custom_target(ffmpeg-build ALL DEPENDS ${FFmpeg_BUILD_LIBRARIES} ffmpeg-configure)
|
||||
link_libraries(${FFmpeg_LIBVA_LIBRARIES})
|
||||
set(FFmpeg_LIBRARIES ${FFmpeg_BUILD_LIBRARIES} ${FFmpeg_HWACCEL_LIBRARIES}
|
||||
CACHE PATH "Paths to FFmpeg libraries" FORCE)
|
||||
unset(FFmpeg_BUILD_LIBRARIES)
|
||||
unset(FFmpeg_HWACCEL_FLAGS)
|
||||
unset(FFmpeg_HWACCEL_INCLUDE_DIRS)
|
||||
unset(FFmpeg_HWACCEL_LDFLAGS)
|
||||
unset(FFmpeg_HWACCEL_LIBRARIES)
|
||||
|
||||
if (FFmpeg_FOUND)
|
||||
message(STATUS "Found FFmpeg version ${FFmpeg_VERSION}")
|
||||
else()
|
||||
message(FATAL_ERROR "FFmpeg not found")
|
||||
endif()
|
||||
else() # WIN32
|
||||
# Use yuzu FFmpeg binaries
|
||||
set(FFmpeg_EXT_NAME "ffmpeg-4.4")
|
||||
set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}")
|
||||
download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "")
|
||||
set(FFmpeg_FOUND YES)
|
||||
set(FFmpeg_INCLUDE_DIR "${FFmpeg_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE)
|
||||
set(FFmpeg_LIBRARY_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg library directory" FORCE)
|
||||
set(FFmpeg_LDFLAGS "" CACHE STRING "FFmpeg linker flags" FORCE)
|
||||
set(FFmpeg_DLL_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg dll's" FORCE)
|
||||
set(FFmpeg_LIBRARIES
|
||||
${FFmpeg_LIBRARY_DIR}/swscale.lib
|
||||
${FFmpeg_LIBRARY_DIR}/avcodec.lib
|
||||
${FFmpeg_LIBRARY_DIR}/avutil.lib
|
||||
CACHE PATH "Paths to FFmpeg libraries" FORCE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
unset(FFmpeg_COMPONENTS)
|
||||
|
||||
# Prefer the -pthread flag on Linux.
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
@@ -11,9 +11,15 @@ find_package(Git QUIET PATHS "${GIT_EXECUTABLE}")
|
||||
|
||||
# generate git/build information
|
||||
include(GetGitRevisionDescription)
|
||||
get_git_head_revision(GIT_REF_SPEC GIT_REV)
|
||||
git_describe(GIT_DESC --always --long --dirty)
|
||||
git_branch_name(GIT_BRANCH)
|
||||
if(NOT GIT_REF_SPEC)
|
||||
get_git_head_revision(GIT_REF_SPEC GIT_REV)
|
||||
endif()
|
||||
if(NOT GIT_DESC)
|
||||
git_describe(GIT_DESC --always --long --dirty)
|
||||
endif()
|
||||
if (NOT GIT_BRANCH)
|
||||
git_branch_name(GIT_BRANCH)
|
||||
endif()
|
||||
get_timestamp(BUILD_DATE)
|
||||
|
||||
# Generate cpp with Git revision from template
|
||||
|
||||
Vendored
+1230
-1010
File diff suppressed because it is too large
Load Diff
Vendored
+603
-416
File diff suppressed because it is too large
Load Diff
Vendored
+611
-424
File diff suppressed because it is too large
Load Diff
Vendored
+600
-413
File diff suppressed because it is too large
Load Diff
Vendored
+601
-414
File diff suppressed because it is too large
Load Diff
Vendored
+6221
File diff suppressed because it is too large
Load Diff
Vendored
+644
-457
File diff suppressed because it is too large
Load Diff
Vendored
+6218
File diff suppressed because it is too large
Load Diff
Vendored
+663
-468
File diff suppressed because it is too large
Load Diff
Vendored
+592
-405
File diff suppressed because it is too large
Load Diff
Vendored
+676
-489
File diff suppressed because it is too large
Load Diff
Vendored
+667
-480
File diff suppressed because it is too large
Load Diff
Vendored
+657
-469
File diff suppressed because it is too large
Load Diff
Vendored
+770
-573
File diff suppressed because it is too large
Load Diff
Vendored
+721
-528
File diff suppressed because it is too large
Load Diff
Vendored
+639
-452
File diff suppressed because it is too large
Load Diff
Vendored
+563
-376
File diff suppressed because it is too large
Load Diff
Vendored
+666
-479
File diff suppressed because it is too large
Load Diff
Vendored
+630
-443
File diff suppressed because it is too large
Load Diff
Vendored
+668
-481
File diff suppressed because it is too large
Load Diff
Vendored
+563
-376
File diff suppressed because it is too large
Load Diff
Vendored
+616
-429
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
@@ -8,5 +8,5 @@ Icon=yuzu
|
||||
TryExec=yuzu
|
||||
Exec=yuzu %f
|
||||
Categories=Game;Emulator;Qt;
|
||||
MimeType=application/x-nx-nro;application/x-nx-nso;
|
||||
MimeType=application/x-nx-nro;application/x-nx-nso;application/x-nx-nsp;application/x-nx-xci;
|
||||
Keywords=Switch;Nintendo;
|
||||
Vendored
+15
@@ -15,4 +15,19 @@
|
||||
<glob pattern="*.nso"/>
|
||||
<magic><match value="NSO" type="string" offset="0"/></magic>
|
||||
</mime-type>
|
||||
|
||||
<mime-type type="application/x-nx-nsp">
|
||||
<comment>Nintendo Switch Package</comment>
|
||||
<acronym>NSP</acronym>
|
||||
<icon name="yuzu"/>
|
||||
<glob pattern="*.nsp"/>
|
||||
<magic><match value="PFS" type="string" offset="0"/></magic>
|
||||
</mime-type>
|
||||
|
||||
<mime-type type="application/x-nx-xci">
|
||||
<comment>Nintendo Switch Card Image</comment>
|
||||
<acronym>XCI</acronym>
|
||||
<icon name="yuzu"/>
|
||||
<glob pattern="*.xci"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
||||
Vendored
+16
-10
@@ -13,10 +13,6 @@ if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
|
||||
target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
|
||||
endif()
|
||||
|
||||
# Catch
|
||||
add_library(catch-single-include INTERFACE)
|
||||
target_include_directories(catch-single-include INTERFACE catch/single_include)
|
||||
|
||||
# Dynarmic
|
||||
if (ARCHITECTURE_x86_64)
|
||||
set(DYNARMIC_TESTS OFF)
|
||||
@@ -52,11 +48,12 @@ endif()
|
||||
# SDL2
|
||||
if (YUZU_USE_EXTERNAL_SDL2)
|
||||
if (NOT WIN32)
|
||||
# Yuzu itself needs: Events Joystick Haptic Sensor Timers Audio
|
||||
# Yuzu itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers
|
||||
# Since 2.0.18 Atomic+Threads required for HIDAPI/libusb (see https://github.com/libsdl-org/SDL/issues/5095)
|
||||
# Yuzu-cmd also needs: Video (depends on Loadso/Dlopen)
|
||||
set(SDL_UNUSED_SUBSYSTEMS
|
||||
Atomic Render Power Threads
|
||||
File CPUinfo Filesystem Locale)
|
||||
CPUinfo File Filesystem
|
||||
Locale Power Render)
|
||||
foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS})
|
||||
string(TOUPPER ${_SUB} _OPT)
|
||||
option(SDL_${_OPT} "" OFF)
|
||||
@@ -116,8 +113,17 @@ if (ENABLE_WEB_SERVICE)
|
||||
endif()
|
||||
|
||||
# Opus
|
||||
find_package(opus 1.3)
|
||||
if (NOT opus_FOUND)
|
||||
message(STATUS "opus 1.3 or newer not found, falling back to externals")
|
||||
if (YUZU_USE_BUNDLED_OPUS)
|
||||
add_subdirectory(opus EXCLUDE_FROM_ALL)
|
||||
else()
|
||||
find_package(opus 1.3 REQUIRED)
|
||||
endif()
|
||||
|
||||
# FFMpeg
|
||||
if (YUZU_USE_BUNDLED_FFMPEG)
|
||||
add_subdirectory(ffmpeg)
|
||||
set(FFmpeg_PATH "${FFmpeg_PATH}" PARENT_SCOPE)
|
||||
set(FFmpeg_LDFLAGS "${FFmpeg_LDFLAGS}" PARENT_SCOPE)
|
||||
set(FFmpeg_LIBRARIES "${FFmpeg_LIBRARIES}" PARENT_SCOPE)
|
||||
set(FFmpeg_INCLUDE_DIR "${FFmpeg_INCLUDE_DIR}" PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
+18
-18
@@ -747,12 +747,12 @@ AF1 sharpness){
|
||||
// Immediate constants for peak range.
|
||||
AF2 peakC=AF2(1.0,-1.0*4.0);
|
||||
// Limiters, these need to be high precision RCPs.
|
||||
AF1 hitMinR=mn4R*ARcpF1(AF1_(4.0)*mx4R);
|
||||
AF1 hitMinG=mn4G*ARcpF1(AF1_(4.0)*mx4G);
|
||||
AF1 hitMinB=mn4B*ARcpF1(AF1_(4.0)*mx4B);
|
||||
AF1 hitMaxR=(peakC.x-mx4R)*ARcpF1(AF1_(4.0)*mn4R+peakC.y);
|
||||
AF1 hitMaxG=(peakC.x-mx4G)*ARcpF1(AF1_(4.0)*mn4G+peakC.y);
|
||||
AF1 hitMaxB=(peakC.x-mx4B)*ARcpF1(AF1_(4.0)*mn4B+peakC.y);
|
||||
AF1 hitMinR=min(mn4R,eR)*ARcpF1(AF1_(4.0)*mx4R);
|
||||
AF1 hitMinG=min(mn4G,eG)*ARcpF1(AF1_(4.0)*mx4G);
|
||||
AF1 hitMinB=min(mn4B,eB)*ARcpF1(AF1_(4.0)*mx4B);
|
||||
AF1 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpF1(AF1_(4.0)*mn4R+peakC.y);
|
||||
AF1 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpF1(AF1_(4.0)*mn4G+peakC.y);
|
||||
AF1 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpF1(AF1_(4.0)*mn4B+peakC.y);
|
||||
AF1 lobeR=max(-hitMinR,hitMaxR);
|
||||
AF1 lobeG=max(-hitMinG,hitMaxG);
|
||||
AF1 lobeB=max(-hitMinB,hitMaxB);
|
||||
@@ -845,12 +845,12 @@ AF1 sharpness){
|
||||
// Immediate constants for peak range.
|
||||
AH2 peakC=AH2(1.0,-1.0*4.0);
|
||||
// Limiters, these need to be high precision RCPs.
|
||||
AH1 hitMinR=mn4R*ARcpH1(AH1_(4.0)*mx4R);
|
||||
AH1 hitMinG=mn4G*ARcpH1(AH1_(4.0)*mx4G);
|
||||
AH1 hitMinB=mn4B*ARcpH1(AH1_(4.0)*mx4B);
|
||||
AH1 hitMaxR=(peakC.x-mx4R)*ARcpH1(AH1_(4.0)*mn4R+peakC.y);
|
||||
AH1 hitMaxG=(peakC.x-mx4G)*ARcpH1(AH1_(4.0)*mn4G+peakC.y);
|
||||
AH1 hitMaxB=(peakC.x-mx4B)*ARcpH1(AH1_(4.0)*mn4B+peakC.y);
|
||||
AH1 hitMinR=min(mn4R,eR)*ARcpH1(AH1_(4.0)*mx4R);
|
||||
AH1 hitMinG=min(mn4G,eG)*ARcpH1(AH1_(4.0)*mx4G);
|
||||
AH1 hitMinB=min(mn4B,eB)*ARcpH1(AH1_(4.0)*mx4B);
|
||||
AH1 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpH1(AH1_(4.0)*mn4R+peakC.y);
|
||||
AH1 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpH1(AH1_(4.0)*mn4G+peakC.y);
|
||||
AH1 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpH1(AH1_(4.0)*mn4B+peakC.y);
|
||||
AH1 lobeR=max(-hitMinR,hitMaxR);
|
||||
AH1 lobeG=max(-hitMinG,hitMaxG);
|
||||
AH1 lobeB=max(-hitMinB,hitMaxB);
|
||||
@@ -963,12 +963,12 @@ AF1 sharpness){
|
||||
// Immediate constants for peak range.
|
||||
AH2 peakC=AH2(1.0,-1.0*4.0);
|
||||
// Limiters, these need to be high precision RCPs.
|
||||
AH2 hitMinR=mn4R*ARcpH2(AH2_(4.0)*mx4R);
|
||||
AH2 hitMinG=mn4G*ARcpH2(AH2_(4.0)*mx4G);
|
||||
AH2 hitMinB=mn4B*ARcpH2(AH2_(4.0)*mx4B);
|
||||
AH2 hitMaxR=(peakC.x-mx4R)*ARcpH2(AH2_(4.0)*mn4R+peakC.y);
|
||||
AH2 hitMaxG=(peakC.x-mx4G)*ARcpH2(AH2_(4.0)*mn4G+peakC.y);
|
||||
AH2 hitMaxB=(peakC.x-mx4B)*ARcpH2(AH2_(4.0)*mn4B+peakC.y);
|
||||
AH2 hitMinR=min(mn4R,eR)*ARcpH2(AH2_(4.0)*mx4R);
|
||||
AH2 hitMinG=min(mn4G,eG)*ARcpH2(AH2_(4.0)*mx4G);
|
||||
AH2 hitMinB=min(mn4B,eB)*ARcpH2(AH2_(4.0)*mx4B);
|
||||
AH2 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpH2(AH2_(4.0)*mn4R+peakC.y);
|
||||
AH2 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpH2(AH2_(4.0)*mn4G+peakC.y);
|
||||
AH2 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpH2(AH2_(4.0)*mn4B+peakC.y);
|
||||
AH2 lobeR=max(-hitMinR,hitMaxR);
|
||||
AH2 lobeG=max(-hitMinG,hitMaxG);
|
||||
AH2 lobeB=max(-hitMinB,hitMaxB);
|
||||
|
||||
Vendored
+1
-1
Submodule externals/SDL updated: 2e9821423a...e2ade2bfc4
Vendored
+1
-1
Submodule externals/Vulkan-Headers updated: 07c4a37bcf...e005e1f817
Vendored
+1
-1
Submodule externals/dynarmic updated: cce7e4ee5d...a8cbfd9af4
Vendored
-1
Submodule externals/ffmpeg deleted from 79e8d17024
Vendored
+219
@@ -0,0 +1,219 @@
|
||||
if (NOT WIN32)
|
||||
# Build FFmpeg from externals
|
||||
message(STATUS "Using FFmpeg from externals")
|
||||
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86_64|amd64)")
|
||||
# FFmpeg has source that requires one of nasm or yasm to assemble it.
|
||||
# REQUIRED throws an error if not found here during configuration rather than during compilation.
|
||||
find_program(ASSEMBLER NAMES nasm yasm)
|
||||
if ("${ASSEMBLER}" STREQUAL "ASSEMBLER-NOTFOUND")
|
||||
message(FATAL_ERROR "One of either `nasm` or `yasm` not found but is required.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_program(AUTOCONF autoconf)
|
||||
if ("${AUTOCONF}" STREQUAL "AUTOCONF-NOTFOUND")
|
||||
message(FATAL_ERROR "Required program `autoconf` not found.")
|
||||
endif()
|
||||
|
||||
set(FFmpeg_PREFIX ${PROJECT_SOURCE_DIR}/externals/ffmpeg/ffmpeg)
|
||||
set(FFmpeg_BUILD_DIR ${PROJECT_BINARY_DIR}/externals/ffmpeg-build)
|
||||
set(FFmpeg_MAKEFILE ${FFmpeg_BUILD_DIR}/Makefile)
|
||||
make_directory(${FFmpeg_BUILD_DIR})
|
||||
|
||||
# Read version string from external
|
||||
file(READ ${FFmpeg_PREFIX}/RELEASE FFmpeg_VERSION)
|
||||
set(FFmpeg_FOUND NO)
|
||||
if (NOT FFmpeg_VERSION STREQUAL "")
|
||||
set(FFmpeg_FOUND YES)
|
||||
endif()
|
||||
|
||||
unset(FFmpeg_LIBRARIES CACHE)
|
||||
foreach(COMPONENT ${FFmpeg_COMPONENTS})
|
||||
set(FFmpeg_${COMPONENT}_PREFIX "${FFmpeg_BUILD_DIR}/lib${COMPONENT}")
|
||||
set(FFmpeg_${COMPONENT}_LIB_NAME "lib${COMPONENT}.a")
|
||||
set(FFmpeg_${COMPONENT}_LIBRARY "${FFmpeg_${COMPONENT}_PREFIX}/${FFmpeg_${COMPONENT}_LIB_NAME}")
|
||||
|
||||
set(FFmpeg_LIBRARIES
|
||||
${FFmpeg_LIBRARIES}
|
||||
${FFmpeg_${COMPONENT}_LIBRARY}
|
||||
CACHE PATH "Paths to FFmpeg libraries" FORCE)
|
||||
endforeach()
|
||||
|
||||
Include(FindPkgConfig REQUIRED)
|
||||
pkg_check_modules(LIBVA libva)
|
||||
pkg_check_modules(CUDA cuda)
|
||||
pkg_check_modules(FFNVCODEC ffnvcodec)
|
||||
pkg_check_modules(VDPAU vdpau)
|
||||
|
||||
set(FFmpeg_HWACCEL_LIBRARIES)
|
||||
set(FFmpeg_HWACCEL_FLAGS)
|
||||
set(FFmpeg_HWACCEL_INCLUDE_DIRS)
|
||||
set(FFmpeg_HWACCEL_LDFLAGS)
|
||||
|
||||
if(LIBVA_FOUND)
|
||||
pkg_check_modules(LIBDRM libdrm REQUIRED)
|
||||
find_package(X11 REQUIRED)
|
||||
pkg_check_modules(LIBVA-DRM libva-drm REQUIRED)
|
||||
pkg_check_modules(LIBVA-X11 libva-x11 REQUIRED)
|
||||
list(APPEND FFmpeg_HWACCEL_LIBRARIES
|
||||
${LIBDRM_LIBRARIES}
|
||||
${X11_LIBRARIES}
|
||||
${LIBVA-DRM_LIBRARIES}
|
||||
${LIBVA-X11_LIBRARIES}
|
||||
${LIBVA_LIBRARIES})
|
||||
set(FFmpeg_HWACCEL_FLAGS
|
||||
--enable-hwaccel=h264_vaapi
|
||||
--enable-hwaccel=vp8_vaapi
|
||||
--enable-hwaccel=vp9_vaapi
|
||||
--enable-libdrm)
|
||||
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
|
||||
${LIBDRM_INCLUDE_DIRS}
|
||||
${X11_INCLUDE_DIRS}
|
||||
${LIBVA-DRM_INCLUDE_DIRS}
|
||||
${LIBVA-X11_INCLUDE_DIRS}
|
||||
${LIBVA_INCLUDE_DIRS}
|
||||
)
|
||||
message(STATUS "VA-API found")
|
||||
else()
|
||||
set(FFmpeg_HWACCEL_FLAGS --disable-vaapi)
|
||||
endif()
|
||||
|
||||
if (FFNVCODEC_FOUND)
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS
|
||||
--enable-cuvid
|
||||
--enable-ffnvcodec
|
||||
--enable-nvdec
|
||||
--enable-hwaccel=h264_nvdec
|
||||
--enable-hwaccel=vp8_nvdec
|
||||
--enable-hwaccel=vp9_nvdec
|
||||
)
|
||||
list(APPEND FFmpeg_HWACCEL_LIBRARIES ${FFNVCODEC_LIBRARIES})
|
||||
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${FFNVCODEC_INCLUDE_DIRS})
|
||||
list(APPEND FFmpeg_HWACCEL_LDFLAGS ${FFNVCODEC_LDFLAGS})
|
||||
message(STATUS "ffnvcodec libraries version ${FFNVCODEC_VERSION} found")
|
||||
# ffnvenc could load CUDA libraries at the runtime using dlopen/dlsym or LoadLibrary/GetProcAddress
|
||||
# here we handle the hard-linking senario where CUDA is linked during compilation
|
||||
if (CUDA_FOUND)
|
||||
# This line causes build error if CUDA_INCLUDE_DIRS is anything but a single non-empty value
|
||||
#list(APPEND FFmpeg_HWACCEL_FLAGS --extra-cflags=-I${CUDA_INCLUDE_DIRS})
|
||||
list(APPEND FFmpeg_HWACCEL_LIBRARIES ${CUDA_LIBRARIES})
|
||||
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${CUDA_INCLUDE_DIRS})
|
||||
list(APPEND FFmpeg_HWACCEL_LDFLAGS ${CUDA_LDFLAGS})
|
||||
message(STATUS "CUDA libraries found, hard-linking will be performed")
|
||||
endif(CUDA_FOUND)
|
||||
endif()
|
||||
|
||||
if (VDPAU_FOUND)
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS
|
||||
--enable-vdpau
|
||||
--enable-hwaccel=h264_vdpau
|
||||
--enable-hwaccel=vp9_vdpau
|
||||
)
|
||||
list(APPEND FFmpeg_HWACCEL_LIBRARIES ${VDPAU_LIBRARIES})
|
||||
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${VDPAU_INCLUDE_DIRS})
|
||||
list(APPEND FFmpeg_HWACCEL_LDFLAGS ${VDPAU_LDFLAGS})
|
||||
message(STATUS "vdpau libraries version ${VDPAU_VERSION} found")
|
||||
else()
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vdpau)
|
||||
endif()
|
||||
|
||||
# `configure` parameters builds only exactly what yuzu needs from FFmpeg
|
||||
# `--disable-vdpau` is needed to avoid linking issues
|
||||
set(FFmpeg_CC ${CMAKE_C_COMPILER_LAUNCHER} ${CMAKE_C_COMPILER})
|
||||
set(FFmpeg_CXX ${CMAKE_CXX_COMPILER_LAUNCHER} ${CMAKE_CXX_COMPILER})
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${FFmpeg_MAKEFILE}
|
||||
COMMAND
|
||||
/bin/bash ${FFmpeg_PREFIX}/configure
|
||||
--disable-avdevice
|
||||
--disable-avfilter
|
||||
--disable-avformat
|
||||
--disable-doc
|
||||
--disable-everything
|
||||
--disable-ffmpeg
|
||||
--disable-ffprobe
|
||||
--disable-network
|
||||
--disable-postproc
|
||||
--disable-swresample
|
||||
--enable-decoder=h264
|
||||
--enable-decoder=vp8
|
||||
--enable-decoder=vp9
|
||||
--cc="${FFmpeg_CC}"
|
||||
--cxx="${FFmpeg_CXX}"
|
||||
${FFmpeg_HWACCEL_FLAGS}
|
||||
WORKING_DIRECTORY
|
||||
${FFmpeg_BUILD_DIR}
|
||||
)
|
||||
unset(FFmpeg_CC)
|
||||
unset(FFmpeg_CXX)
|
||||
unset(FFmpeg_HWACCEL_FLAGS)
|
||||
|
||||
# Workaround for Ubuntu 18.04's older version of make not being able to call make as a child
|
||||
# with context of the jobserver. Also helps ninja users.
|
||||
execute_process(
|
||||
COMMAND
|
||||
nproc
|
||||
OUTPUT_VARIABLE
|
||||
SYSTEM_THREADS)
|
||||
|
||||
set(FFmpeg_BUILD_LIBRARIES ${FFmpeg_LIBRARIES})
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${FFmpeg_BUILD_LIBRARIES}
|
||||
COMMAND
|
||||
make -j${SYSTEM_THREADS}
|
||||
WORKING_DIRECTORY
|
||||
${FFmpeg_BUILD_DIR}
|
||||
)
|
||||
|
||||
set(FFmpeg_INCLUDE_DIR
|
||||
"${FFmpeg_PREFIX};${FFmpeg_BUILD_DIR};${FFmpeg_HWACCEL_INCLUDE_DIRS}"
|
||||
CACHE PATH "Path to FFmpeg headers" FORCE)
|
||||
|
||||
set(FFmpeg_LDFLAGS
|
||||
"${FFmpeg_HWACCEL_LDFLAGS}"
|
||||
CACHE STRING "FFmpeg linker flags" FORCE)
|
||||
|
||||
# ALL makes this custom target build every time
|
||||
# but it won't actually build if the DEPENDS parameter is up to date
|
||||
add_custom_target(ffmpeg-configure ALL DEPENDS ${FFmpeg_MAKEFILE})
|
||||
add_custom_target(ffmpeg-build ALL DEPENDS ${FFmpeg_BUILD_LIBRARIES} ffmpeg-configure)
|
||||
link_libraries(${FFmpeg_LIBVA_LIBRARIES})
|
||||
set(FFmpeg_LIBRARIES ${FFmpeg_BUILD_LIBRARIES} ${FFmpeg_HWACCEL_LIBRARIES}
|
||||
CACHE PATH "Paths to FFmpeg libraries" FORCE)
|
||||
unset(FFmpeg_BUILD_LIBRARIES)
|
||||
unset(FFmpeg_HWACCEL_FLAGS)
|
||||
unset(FFmpeg_HWACCEL_INCLUDE_DIRS)
|
||||
unset(FFmpeg_HWACCEL_LDFLAGS)
|
||||
unset(FFmpeg_HWACCEL_LIBRARIES)
|
||||
|
||||
if (FFmpeg_FOUND)
|
||||
message(STATUS "Found FFmpeg version ${FFmpeg_VERSION}")
|
||||
else()
|
||||
message(FATAL_ERROR "FFmpeg not found")
|
||||
endif()
|
||||
else(WIN32)
|
||||
# Use yuzu FFmpeg binaries
|
||||
set(FFmpeg_EXT_NAME "ffmpeg-4.4")
|
||||
set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}")
|
||||
download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "")
|
||||
set(FFmpeg_FOUND YES)
|
||||
set(FFmpeg_INCLUDE_DIR "${FFmpeg_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE)
|
||||
set(FFmpeg_LIBRARY_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg library directory" FORCE)
|
||||
set(FFmpeg_LDFLAGS "" CACHE STRING "FFmpeg linker flags" FORCE)
|
||||
set(FFmpeg_DLL_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg dll's" FORCE)
|
||||
set(FFmpeg_LIBRARIES
|
||||
${FFmpeg_LIBRARY_DIR}/swscale.lib
|
||||
${FFmpeg_LIBRARY_DIR}/avcodec.lib
|
||||
${FFmpeg_LIBRARY_DIR}/avutil.lib
|
||||
CACHE PATH "Paths to FFmpeg libraries" FORCE)
|
||||
# exported variables
|
||||
set(FFmpeg_PATH "${FFmpeg_PATH}" PARENT_SCOPE)
|
||||
set(FFmpeg_LDFLAGS "${FFmpeg_LDFLAGS}" PARENT_SCOPE)
|
||||
set(FFmpeg_LIBRARIES "${FFmpeg_LIBRARIES}" PARENT_SCOPE)
|
||||
set(FFmpeg_INCLUDE_DIR "${FFmpeg_INCLUDE_DIR}" PARENT_SCOPE)
|
||||
endif(WIN32)
|
||||
|
||||
unset(FFmpeg_COMPONENTS)
|
||||
+1
Submodule externals/ffmpeg/ffmpeg added at dc91b913b6
+4
-1
@@ -151,7 +151,10 @@ add_subdirectory(audio_core)
|
||||
add_subdirectory(video_core)
|
||||
add_subdirectory(input_common)
|
||||
add_subdirectory(shader_recompiler)
|
||||
add_subdirectory(tests)
|
||||
|
||||
if (YUZU_TESTS)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
if (ENABLE_SDL2)
|
||||
add_subdirectory(yuzu_cmd)
|
||||
|
||||
@@ -15,7 +15,9 @@ constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41};
|
||||
constexpr ResultCode ERR_SPLITTER_SORT_FAILED{ErrorModule::Audio, 43};
|
||||
} // namespace Audren
|
||||
|
||||
constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '9');
|
||||
constexpr u8 BASE_REVISION = '0';
|
||||
constexpr u32_le CURRENT_PROCESS_REVISION =
|
||||
Common::MakeMagic('R', 'E', 'V', static_cast<u8>(BASE_REVISION + 0xA));
|
||||
constexpr std::size_t MAX_MIX_BUFFERS = 24;
|
||||
constexpr std::size_t MAX_BIQUAD_FILTERS = 2;
|
||||
constexpr std::size_t MAX_CHANNEL_COUNT = 6;
|
||||
|
||||
@@ -79,8 +79,8 @@ static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Implementation of a volume slider with a dynamic range of 60 dB
|
||||
const float volume_scale_factor = volume == 0 ? 0 : std::exp(6.90775f * volume) * 0.001f;
|
||||
// Perceived volume is not the same as the volume level
|
||||
const float volume_scale_factor = (0.85f * ((volume * volume) - volume)) + volume;
|
||||
for (auto& sample : samples) {
|
||||
sample = static_cast<s16>(sample * volume_scale_factor);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,11 @@ add_custom_command(OUTPUT scm_rev.cpp
|
||||
-DTITLE_BAR_FORMAT_RUNNING=${TITLE_BAR_FORMAT_RUNNING}
|
||||
-DBUILD_TAG=${BUILD_TAG}
|
||||
-DBUILD_ID=${DISPLAY_VERSION}
|
||||
-DGIT_REF_SPEC=${GIT_REF_SPEC}
|
||||
-DGIT_REV=${GIT_REV}
|
||||
-DGIT_DESC=${GIT_DESC}
|
||||
-DGIT_BRANCH=${GIT_BRANCH}
|
||||
-DBUILD_FULLNAME=${BUILD_FULLNAME}
|
||||
-DGIT_EXECUTABLE=${GIT_EXECUTABLE}
|
||||
-P ${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake
|
||||
DEPENDS
|
||||
@@ -80,6 +85,7 @@ add_library(common STATIC
|
||||
logging/backend.h
|
||||
logging/filter.cpp
|
||||
logging/filter.h
|
||||
logging/formatter.h
|
||||
logging/log.h
|
||||
logging/log_entry.h
|
||||
logging/text_formatter.cpp
|
||||
|
||||
@@ -45,6 +45,12 @@ template <typename T>
|
||||
return static_cast<u32>(log2_f + static_cast<u64>((value ^ (1ULL << log2_f)) != 0ULL));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_unsigned_v<T>
|
||||
[[nodiscard]] constexpr bool IsPow2(T value) {
|
||||
return std::has_single_bit(value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
[[nodiscard]] T NextPow2(T value) {
|
||||
|
||||
@@ -46,13 +46,3 @@ using GPUVAddr = u64; ///< Represents a pointer in the GPU virtual address space
|
||||
|
||||
using u128 = std::array<std::uint64_t, 2>;
|
||||
static_assert(sizeof(u128) == 16, "u128 must be 128 bits wide");
|
||||
|
||||
// An inheritable class to disallow the copy constructor and operator= functions
|
||||
class NonCopyable {
|
||||
protected:
|
||||
constexpr NonCopyable() = default;
|
||||
~NonCopyable() = default;
|
||||
|
||||
NonCopyable(const NonCopyable&) = delete;
|
||||
NonCopyable& operator=(const NonCopyable&) = delete;
|
||||
};
|
||||
|
||||
@@ -124,7 +124,10 @@ void Fiber::YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to) {
|
||||
|
||||
// "from" might no longer be valid if the thread was killed
|
||||
if (auto from = weak_from.lock()) {
|
||||
ASSERT(from->impl->previous_fiber != nullptr);
|
||||
if (from->impl->previous_fiber == nullptr) {
|
||||
ASSERT_MSG(false, "previous_fiber is nullptr!");
|
||||
return;
|
||||
}
|
||||
from->impl->previous_fiber->impl->context = transfer.fctx;
|
||||
from->impl->previous_fiber->impl->guard.unlock();
|
||||
from->impl->previous_fiber.reset();
|
||||
|
||||
@@ -188,9 +188,8 @@ public:
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
[[nodiscard]] void Open(const Path& path, FileAccessMode mode,
|
||||
FileType type = FileType::BinaryFile,
|
||||
FileShareFlag flag = FileShareFlag::ShareReadOnly) {
|
||||
void Open(const Path& path, FileAccessMode mode, FileType type = FileType::BinaryFile,
|
||||
FileShareFlag flag = FileShareFlag::ShareReadOnly) {
|
||||
using ValueType = typename Path::value_type;
|
||||
if constexpr (IsChar<ValueType>) {
|
||||
Open(ToU8String(path), mode, type, flag);
|
||||
|
||||
@@ -16,6 +16,10 @@ std::u8string BufferToU8String(std::span<const u8> buffer) {
|
||||
return std::u8string{buffer.begin(), std::ranges::find(buffer, u8{0})};
|
||||
}
|
||||
|
||||
std::u8string_view BufferToU8StringView(std::span<const u8> buffer) {
|
||||
return std::u8string_view{reinterpret_cast<const char8_t*>(buffer.data())};
|
||||
}
|
||||
|
||||
std::string ToUTF8String(std::u8string_view u8_string) {
|
||||
return std::string{u8_string.begin(), u8_string.end()};
|
||||
}
|
||||
@@ -24,6 +28,10 @@ std::string BufferToUTF8String(std::span<const u8> buffer) {
|
||||
return std::string{buffer.begin(), std::ranges::find(buffer, u8{0})};
|
||||
}
|
||||
|
||||
std::string_view BufferToUTF8StringView(std::span<const u8> buffer) {
|
||||
return std::string_view{reinterpret_cast<const char*>(buffer.data())};
|
||||
}
|
||||
|
||||
std::string PathToUTF8String(const std::filesystem::path& path) {
|
||||
return ToUTF8String(path.u8string());
|
||||
}
|
||||
|
||||
@@ -37,6 +37,15 @@ concept IsChar = std::same_as<T, char>;
|
||||
*/
|
||||
[[nodiscard]] std::u8string BufferToU8String(std::span<const u8> buffer);
|
||||
|
||||
/**
|
||||
* Same as BufferToU8String, but returns a string view of the buffer.
|
||||
*
|
||||
* @param buffer Buffer of bytes
|
||||
*
|
||||
* @returns UTF-8 encoded std::u8string_view.
|
||||
*/
|
||||
[[nodiscard]] std::u8string_view BufferToU8StringView(std::span<const u8> buffer);
|
||||
|
||||
/**
|
||||
* Converts a std::u8string or std::u8string_view to a UTF-8 encoded std::string.
|
||||
*
|
||||
@@ -57,6 +66,15 @@ concept IsChar = std::same_as<T, char>;
|
||||
*/
|
||||
[[nodiscard]] std::string BufferToUTF8String(std::span<const u8> buffer);
|
||||
|
||||
/**
|
||||
* Same as BufferToUTF8String, but returns a string view of the buffer.
|
||||
*
|
||||
* @param buffer Buffer of bytes
|
||||
*
|
||||
* @returns UTF-8 encoded std::string_view.
|
||||
*/
|
||||
[[nodiscard]] std::string_view BufferToUTF8StringView(std::span<const u8> buffer);
|
||||
|
||||
/**
|
||||
* Converts a filesystem path to a UTF-8 encoded std::string.
|
||||
*
|
||||
|
||||
@@ -209,6 +209,15 @@ enum class ButtonNames {
|
||||
Triangle,
|
||||
Share,
|
||||
Options,
|
||||
Home,
|
||||
Touch,
|
||||
|
||||
// Mouse buttons
|
||||
ButtonMouseWheel,
|
||||
ButtonBackward,
|
||||
ButtonForward,
|
||||
ButtonTask,
|
||||
ButtonExtra,
|
||||
};
|
||||
|
||||
// Callback data consisting of an input type and the equivalent data status
|
||||
|
||||
@@ -108,6 +108,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
||||
SUB(Service, Migration) \
|
||||
SUB(Service, Mii) \
|
||||
SUB(Service, MM) \
|
||||
SUB(Service, MNPP) \
|
||||
SUB(Service, NCM) \
|
||||
SUB(Service, NFC) \
|
||||
SUB(Service, NFP) \
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2022 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
// adapted from https://github.com/fmtlib/fmt/issues/2704
|
||||
// a generic formatter for enum classes
|
||||
#if FMT_VERSION >= 80100
|
||||
template <typename T>
|
||||
struct fmt::formatter<T, std::enable_if_t<std::is_enum_v<T>, char>>
|
||||
: formatter<std::underlying_type_t<T>> {
|
||||
template <typename FormatContext>
|
||||
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
return fmt::formatter<std::underlying_type_t<T>>::format(
|
||||
static_cast<std::underlying_type_t<T>>(value), ctx);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
@@ -7,8 +7,9 @@
|
||||
#include <algorithm>
|
||||
#include <string_view>
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/logging/formatter.h"
|
||||
#include "common/logging/types.h"
|
||||
|
||||
namespace Common::Log {
|
||||
|
||||
@@ -76,6 +76,7 @@ enum class Class : u8 {
|
||||
Service_Migration, ///< The migration service
|
||||
Service_Mii, ///< The Mii service
|
||||
Service_MM, ///< The MM (Multimedia) service
|
||||
Service_MNPP, ///< The MNPP service
|
||||
Service_NCM, ///< The NCM service
|
||||
Service_NFC, ///< The NFC (Near-field communication) service
|
||||
Service_NFP, ///< The NFP service
|
||||
|
||||
@@ -10,11 +10,65 @@ PageTable::PageTable() = default;
|
||||
|
||||
PageTable::~PageTable() noexcept = default;
|
||||
|
||||
void PageTable::Resize(size_t address_space_width_in_bits, size_t page_size_in_bits) {
|
||||
const size_t num_page_table_entries{1ULL << (address_space_width_in_bits - page_size_in_bits)};
|
||||
bool PageTable::BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context,
|
||||
u64 address) const {
|
||||
// Setup invalid defaults.
|
||||
out_entry.phys_addr = 0;
|
||||
out_entry.block_size = page_size;
|
||||
out_context.next_page = 0;
|
||||
|
||||
// Validate that we can read the actual entry.
|
||||
const auto page = address / page_size;
|
||||
if (page >= backing_addr.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate that the entry is mapped.
|
||||
const auto phys_addr = backing_addr[page];
|
||||
if (phys_addr == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Populate the results.
|
||||
out_entry.phys_addr = phys_addr + address;
|
||||
out_context.next_page = page + 1;
|
||||
out_context.next_offset = address + page_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const {
|
||||
// Setup invalid defaults.
|
||||
out_entry.phys_addr = 0;
|
||||
out_entry.block_size = page_size;
|
||||
|
||||
// Validate that we can read the actual entry.
|
||||
const auto page = context.next_page;
|
||||
if (page >= backing_addr.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate that the entry is mapped.
|
||||
const auto phys_addr = backing_addr[page];
|
||||
if (phys_addr == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Populate the results.
|
||||
out_entry.phys_addr = phys_addr + context.next_offset;
|
||||
context.next_page = page + 1;
|
||||
context.next_offset += page_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits) {
|
||||
const std::size_t num_page_table_entries{1ULL
|
||||
<< (address_space_width_in_bits - page_size_in_bits)};
|
||||
pointers.resize(num_page_table_entries);
|
||||
backing_addr.resize(num_page_table_entries);
|
||||
current_address_space_width_in_bits = address_space_width_in_bits;
|
||||
page_size = 1ULL << page_size_in_bits;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
+20
-4
@@ -27,6 +27,16 @@ enum class PageType : u8 {
|
||||
* mimics the way a real CPU page table works.
|
||||
*/
|
||||
struct PageTable {
|
||||
struct TraversalEntry {
|
||||
u64 phys_addr{};
|
||||
std::size_t block_size{};
|
||||
};
|
||||
|
||||
struct TraversalContext {
|
||||
u64 next_page{};
|
||||
u64 next_offset{};
|
||||
};
|
||||
|
||||
/// Number of bits reserved for attribute tagging.
|
||||
/// This can be at most the guaranteed alignment of the pointers in the page table.
|
||||
static constexpr int ATTRIBUTE_BITS = 2;
|
||||
@@ -89,6 +99,10 @@ struct PageTable {
|
||||
PageTable(PageTable&&) noexcept = default;
|
||||
PageTable& operator=(PageTable&&) noexcept = default;
|
||||
|
||||
bool BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context,
|
||||
u64 address) const;
|
||||
bool ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const;
|
||||
|
||||
/**
|
||||
* Resizes the page table to be able to accommodate enough pages within
|
||||
* a given address space.
|
||||
@@ -96,9 +110,9 @@ struct PageTable {
|
||||
* @param address_space_width_in_bits The address size width in bits.
|
||||
* @param page_size_in_bits The page size in bits.
|
||||
*/
|
||||
void Resize(size_t address_space_width_in_bits, size_t page_size_in_bits);
|
||||
void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits);
|
||||
|
||||
size_t GetAddressSpaceBits() const {
|
||||
std::size_t GetAddressSpaceBits() const {
|
||||
return current_address_space_width_in_bits;
|
||||
}
|
||||
|
||||
@@ -110,9 +124,11 @@ struct PageTable {
|
||||
|
||||
VirtualBuffer<u64> backing_addr;
|
||||
|
||||
size_t current_address_space_width_in_bits;
|
||||
std::size_t current_address_space_width_in_bits{};
|
||||
|
||||
u8* fastmem_arena;
|
||||
u8* fastmem_arena{};
|
||||
|
||||
std::size_t page_size{};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -554,6 +554,7 @@ struct Values {
|
||||
Setting<bool> use_docked_mode{true, "use_docked_mode"};
|
||||
|
||||
BasicSetting<bool> enable_raw_input{false, "enable_raw_input"};
|
||||
BasicSetting<bool> controller_navigation{true, "controller_navigation"};
|
||||
|
||||
Setting<bool> vibration_enabled{true, "vibration_enabled"};
|
||||
Setting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"};
|
||||
@@ -597,6 +598,7 @@ struct Values {
|
||||
BasicSetting<std::string> program_args{std::string(), "program_args"};
|
||||
BasicSetting<bool> dump_exefs{false, "dump_exefs"};
|
||||
BasicSetting<bool> dump_nso{false, "dump_nso"};
|
||||
BasicSetting<bool> dump_shaders{false, "dump_shaders"};
|
||||
BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"};
|
||||
BasicSetting<bool> reporting_services{false, "reporting_services"};
|
||||
BasicSetting<bool> quest_flag{false, "quest_flag"};
|
||||
|
||||
+20
-9
@@ -8,6 +8,7 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common::Telemetry {
|
||||
@@ -28,7 +29,7 @@ struct VisitorInterface;
|
||||
/**
|
||||
* Interface class for telemetry data fields.
|
||||
*/
|
||||
class FieldInterface : NonCopyable {
|
||||
class FieldInterface {
|
||||
public:
|
||||
virtual ~FieldInterface() = default;
|
||||
|
||||
@@ -52,14 +53,15 @@ public:
|
||||
template <typename T>
|
||||
class Field : public FieldInterface {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(Field);
|
||||
|
||||
Field(FieldType type_, std::string name_, T value_)
|
||||
: name(std::move(name_)), type(type_), value(std::move(value_)) {}
|
||||
|
||||
Field(const Field&) = default;
|
||||
Field& operator=(const Field&) = default;
|
||||
~Field() override = default;
|
||||
|
||||
Field(Field&&) = default;
|
||||
Field& operator=(Field&& other) = default;
|
||||
Field(Field&&) noexcept = default;
|
||||
Field& operator=(Field&& other) noexcept = default;
|
||||
|
||||
void Accept(VisitorInterface& visitor) const override;
|
||||
|
||||
@@ -98,9 +100,15 @@ private:
|
||||
/**
|
||||
* Collection of data fields that have been logged.
|
||||
*/
|
||||
class FieldCollection final : NonCopyable {
|
||||
class FieldCollection final {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(FieldCollection);
|
||||
|
||||
FieldCollection() = default;
|
||||
~FieldCollection() = default;
|
||||
|
||||
FieldCollection(FieldCollection&&) noexcept = default;
|
||||
FieldCollection& operator=(FieldCollection&&) noexcept = default;
|
||||
|
||||
/**
|
||||
* Accept method for the visitor pattern, visits each field in the collection.
|
||||
@@ -133,7 +141,7 @@ private:
|
||||
* Telemetry fields visitor interface class. A backend to log to a web service should implement
|
||||
* this interface.
|
||||
*/
|
||||
struct VisitorInterface : NonCopyable {
|
||||
struct VisitorInterface {
|
||||
virtual ~VisitorInterface() = default;
|
||||
|
||||
virtual void Visit(const Field<bool>& field) = 0;
|
||||
@@ -160,8 +168,11 @@ struct VisitorInterface : NonCopyable {
|
||||
* Empty implementation of VisitorInterface that drops all fields. Used when a functional
|
||||
* backend implementation is not available.
|
||||
*/
|
||||
struct NullVisitor : public VisitorInterface {
|
||||
~NullVisitor() = default;
|
||||
struct NullVisitor final : public VisitorInterface {
|
||||
YUZU_NON_COPYABLE(NullVisitor);
|
||||
|
||||
NullVisitor() = default;
|
||||
~NullVisitor() override = default;
|
||||
|
||||
void Visit(const Field<bool>& /*field*/) override {}
|
||||
void Visit(const Field<double>& /*field*/) override {}
|
||||
|
||||
+173
-47
@@ -1,23 +1,25 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Copyright 2022 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <bit>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/tiny_mt.h"
|
||||
#include "common/uuid.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsHexDigit(char c) {
|
||||
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
|
||||
}
|
||||
constexpr size_t RawStringSize = sizeof(UUID) * 2;
|
||||
constexpr size_t FormattedStringSize = RawStringSize + 4;
|
||||
|
||||
u8 HexCharToByte(char c) {
|
||||
std::optional<u8> HexCharToByte(char c) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
return static_cast<u8>(c - '0');
|
||||
}
|
||||
@@ -28,60 +30,184 @@ u8 HexCharToByte(char c) {
|
||||
return static_cast<u8>(c - 'A' + 10);
|
||||
}
|
||||
ASSERT_MSG(false, "{} is not a hexadecimal digit!", c);
|
||||
return u8{0};
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::array<u8, 0x10> ConstructFromRawString(std::string_view raw_string) {
|
||||
std::array<u8, 0x10> uuid;
|
||||
|
||||
for (size_t i = 0; i < RawStringSize; i += 2) {
|
||||
const auto upper = HexCharToByte(raw_string[i]);
|
||||
const auto lower = HexCharToByte(raw_string[i + 1]);
|
||||
if (!upper || !lower) {
|
||||
return {};
|
||||
}
|
||||
uuid[i / 2] = static_cast<u8>((*upper << 4) | *lower);
|
||||
}
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
std::array<u8, 0x10> ConstructFromFormattedString(std::string_view formatted_string) {
|
||||
std::array<u8, 0x10> uuid;
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
// Process the first 8 characters.
|
||||
const auto* str = formatted_string.data();
|
||||
|
||||
for (; i < 4; ++i) {
|
||||
const auto upper = HexCharToByte(*(str++));
|
||||
const auto lower = HexCharToByte(*(str++));
|
||||
if (!upper || !lower) {
|
||||
return {};
|
||||
}
|
||||
uuid[i] = static_cast<u8>((*upper << 4) | *lower);
|
||||
}
|
||||
|
||||
// Process the next 4 characters.
|
||||
++str;
|
||||
|
||||
for (; i < 6; ++i) {
|
||||
const auto upper = HexCharToByte(*(str++));
|
||||
const auto lower = HexCharToByte(*(str++));
|
||||
if (!upper || !lower) {
|
||||
return {};
|
||||
}
|
||||
uuid[i] = static_cast<u8>((*upper << 4) | *lower);
|
||||
}
|
||||
|
||||
// Process the next 4 characters.
|
||||
++str;
|
||||
|
||||
for (; i < 8; ++i) {
|
||||
const auto upper = HexCharToByte(*(str++));
|
||||
const auto lower = HexCharToByte(*(str++));
|
||||
if (!upper || !lower) {
|
||||
return {};
|
||||
}
|
||||
uuid[i] = static_cast<u8>((*upper << 4) | *lower);
|
||||
}
|
||||
|
||||
// Process the next 4 characters.
|
||||
++str;
|
||||
|
||||
for (; i < 10; ++i) {
|
||||
const auto upper = HexCharToByte(*(str++));
|
||||
const auto lower = HexCharToByte(*(str++));
|
||||
if (!upper || !lower) {
|
||||
return {};
|
||||
}
|
||||
uuid[i] = static_cast<u8>((*upper << 4) | *lower);
|
||||
}
|
||||
|
||||
// Process the last 12 characters.
|
||||
++str;
|
||||
|
||||
for (; i < 16; ++i) {
|
||||
const auto upper = HexCharToByte(*(str++));
|
||||
const auto lower = HexCharToByte(*(str++));
|
||||
if (!upper || !lower) {
|
||||
return {};
|
||||
}
|
||||
uuid[i] = static_cast<u8>((*upper << 4) | *lower);
|
||||
}
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
std::array<u8, 0x10> ConstructUUID(std::string_view uuid_string) {
|
||||
const auto length = uuid_string.length();
|
||||
|
||||
if (length == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Check if the input string contains 32 hexadecimal characters.
|
||||
if (length == RawStringSize) {
|
||||
return ConstructFromRawString(uuid_string);
|
||||
}
|
||||
|
||||
// Check if the input string has the length of a RFC 4122 formatted UUID string.
|
||||
if (length == FormattedStringSize) {
|
||||
return ConstructFromFormattedString(uuid_string);
|
||||
}
|
||||
|
||||
ASSERT_MSG(false, "UUID string has an invalid length of {} characters!", length);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
u128 HexStringToU128(std::string_view hex_string) {
|
||||
const size_t length = hex_string.length();
|
||||
UUID::UUID(std::string_view uuid_string) : uuid{ConstructUUID(uuid_string)} {}
|
||||
|
||||
// Detect "0x" prefix.
|
||||
const bool has_0x_prefix = length > 2 && hex_string[0] == '0' && hex_string[1] == 'x';
|
||||
const size_t offset = has_0x_prefix ? 2 : 0;
|
||||
|
||||
// Check length.
|
||||
if (length > 32 + offset) {
|
||||
ASSERT_MSG(false, "hex_string has more than 32 hexadecimal characters!");
|
||||
return INVALID_UUID;
|
||||
}
|
||||
|
||||
u64 lo = 0;
|
||||
u64 hi = 0;
|
||||
for (size_t i = 0; i < length - offset; ++i) {
|
||||
const char c = hex_string[length - 1 - i];
|
||||
if (!IsHexDigit(c)) {
|
||||
ASSERT_MSG(false, "{} is not a hexadecimal digit!", c);
|
||||
return INVALID_UUID;
|
||||
}
|
||||
if (i < 16) {
|
||||
lo |= u64{HexCharToByte(c)} << (i * 4);
|
||||
}
|
||||
if (i >= 16) {
|
||||
hi |= u64{HexCharToByte(c)} << ((i - 16) * 4);
|
||||
}
|
||||
}
|
||||
return u128{lo, hi};
|
||||
std::string UUID::RawString() const {
|
||||
return fmt::format("{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}"
|
||||
"{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
|
||||
uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
|
||||
uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14],
|
||||
uuid[15]);
|
||||
}
|
||||
|
||||
UUID UUID::Generate() {
|
||||
std::string UUID::FormattedString() const {
|
||||
return fmt::format("{:02x}{:02x}{:02x}{:02x}"
|
||||
"-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-"
|
||||
"{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
|
||||
uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
|
||||
uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14],
|
||||
uuid[15]);
|
||||
}
|
||||
|
||||
size_t UUID::Hash() const noexcept {
|
||||
u64 upper_hash;
|
||||
u64 lower_hash;
|
||||
|
||||
std::memcpy(&upper_hash, uuid.data(), sizeof(u64));
|
||||
std::memcpy(&lower_hash, uuid.data() + sizeof(u64), sizeof(u64));
|
||||
|
||||
return upper_hash ^ std::rotl(lower_hash, 1);
|
||||
}
|
||||
|
||||
u128 UUID::AsU128() const {
|
||||
u128 uuid_old;
|
||||
std::memcpy(&uuid_old, uuid.data(), sizeof(UUID));
|
||||
return uuid_old;
|
||||
}
|
||||
|
||||
UUID UUID::MakeRandom() {
|
||||
std::random_device device;
|
||||
std::mt19937 gen(device());
|
||||
std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
|
||||
return UUID{distribution(gen), distribution(gen)};
|
||||
|
||||
return MakeRandomWithSeed(device());
|
||||
}
|
||||
|
||||
std::string UUID::Format() const {
|
||||
return fmt::format("{:016x}{:016x}", uuid[1], uuid[0]);
|
||||
UUID UUID::MakeRandomWithSeed(u32 seed) {
|
||||
// Create and initialize our RNG.
|
||||
TinyMT rng;
|
||||
rng.Initialize(seed);
|
||||
|
||||
UUID uuid;
|
||||
|
||||
// Populate the UUID with random bytes.
|
||||
rng.GenerateRandomBytes(uuid.uuid.data(), sizeof(UUID));
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
std::string UUID::FormatSwitch() const {
|
||||
std::array<u8, 16> s{};
|
||||
std::memcpy(s.data(), uuid.data(), sizeof(u128));
|
||||
return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
|
||||
":02x}{:02x}{:02x}{:02x}{:02x}",
|
||||
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
|
||||
s[12], s[13], s[14], s[15]);
|
||||
UUID UUID::MakeRandomRFC4122V4() {
|
||||
auto uuid = MakeRandom();
|
||||
|
||||
// According to Proposed Standard RFC 4122 Section 4.4, we must:
|
||||
|
||||
// 1. Set the two most significant bits (bits 6 and 7) of the
|
||||
// clock_seq_hi_and_reserved to zero and one, respectively.
|
||||
uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F);
|
||||
|
||||
// 2. Set the four most significant bits (bits 12 through 15) of the
|
||||
// time_hi_and_version field to the 4-bit version number from Section 4.1.3.
|
||||
uuid.uuid[6] = 0x40 | (uuid.uuid[6] & 0xF);
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
+106
-54
@@ -1,9 +1,11 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Copyright 2022 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
@@ -11,69 +13,119 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
constexpr u128 INVALID_UUID{{0, 0}};
|
||||
|
||||
/**
|
||||
* Converts a hex string to a 128-bit unsigned integer.
|
||||
*
|
||||
* The hex string can be formatted in lowercase or uppercase, with or without the "0x" prefix.
|
||||
*
|
||||
* This function will assert and return INVALID_UUID under the following conditions:
|
||||
* - If the hex string is more than 32 characters long
|
||||
* - If the hex string contains non-hexadecimal characters
|
||||
*
|
||||
* @param hex_string Hexadecimal string
|
||||
*
|
||||
* @returns A 128-bit unsigned integer if successfully converted, INVALID_UUID otherwise.
|
||||
*/
|
||||
[[nodiscard]] u128 HexStringToU128(std::string_view hex_string);
|
||||
|
||||
struct UUID {
|
||||
// UUIDs which are 0 are considered invalid!
|
||||
u128 uuid;
|
||||
UUID() = default;
|
||||
constexpr explicit UUID(const u128& id) : uuid{id} {}
|
||||
constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
|
||||
explicit UUID(std::string_view hex_string) {
|
||||
uuid = HexStringToU128(hex_string);
|
||||
std::array<u8, 0x10> uuid{};
|
||||
|
||||
/// Constructs an invalid UUID.
|
||||
constexpr UUID() = default;
|
||||
|
||||
/// Constructs a UUID from a reference to a 128 bit array.
|
||||
constexpr explicit UUID(const std::array<u8, 16>& uuid_) : uuid{uuid_} {}
|
||||
|
||||
/**
|
||||
* Constructs a UUID from either:
|
||||
* 1. A 32 hexadecimal character string representing the bytes of the UUID
|
||||
* 2. A RFC 4122 formatted UUID string, in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
*
|
||||
* The input string may contain uppercase or lowercase characters, but they must:
|
||||
* 1. Contain valid hexadecimal characters (0-9, a-f, A-F)
|
||||
* 2. Not contain the "0x" hexadecimal prefix
|
||||
*
|
||||
* Should the input string not meet the above requirements,
|
||||
* an assert will be triggered and an invalid UUID is set instead.
|
||||
*/
|
||||
explicit UUID(std::string_view uuid_string);
|
||||
|
||||
~UUID() = default;
|
||||
|
||||
constexpr UUID(const UUID&) noexcept = default;
|
||||
constexpr UUID(UUID&&) noexcept = default;
|
||||
|
||||
constexpr UUID& operator=(const UUID&) noexcept = default;
|
||||
constexpr UUID& operator=(UUID&&) noexcept = default;
|
||||
|
||||
/**
|
||||
* Returns whether the stored UUID is valid or not.
|
||||
*
|
||||
* @returns True if the stored UUID is valid, false otherwise.
|
||||
*/
|
||||
constexpr bool IsValid() const {
|
||||
return uuid != std::array<u8, 0x10>{};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr explicit operator bool() const {
|
||||
return uuid != INVALID_UUID;
|
||||
/**
|
||||
* Returns whether the stored UUID is invalid or not.
|
||||
*
|
||||
* @returns True if the stored UUID is invalid, false otherwise.
|
||||
*/
|
||||
constexpr bool IsInvalid() const {
|
||||
return !IsValid();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool operator==(const UUID& rhs) const {
|
||||
return uuid == rhs.uuid;
|
||||
/**
|
||||
* Returns a 32 hexadecimal character string representing the bytes of the UUID.
|
||||
*
|
||||
* @returns A 32 hexadecimal character string of the UUID.
|
||||
*/
|
||||
std::string RawString() const;
|
||||
|
||||
/**
|
||||
* Returns a RFC 4122 formatted UUID string in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
|
||||
*
|
||||
* @returns A RFC 4122 formatted UUID string.
|
||||
*/
|
||||
std::string FormattedString() const;
|
||||
|
||||
/**
|
||||
* Returns a 64-bit hash of the UUID for use in hash table data structures.
|
||||
*
|
||||
* @returns A 64-bit hash of the UUID.
|
||||
*/
|
||||
size_t Hash() const noexcept;
|
||||
|
||||
/// DO NOT USE. Copies the contents of the UUID into a u128.
|
||||
u128 AsU128() const;
|
||||
|
||||
/**
|
||||
* Creates a default UUID "yuzu Default UID".
|
||||
*
|
||||
* @returns A UUID with its bytes set to the ASCII values of "yuzu Default UID".
|
||||
*/
|
||||
static constexpr UUID MakeDefault() {
|
||||
return UUID{
|
||||
{'y', 'u', 'z', 'u', ' ', 'D', 'e', 'f', 'a', 'u', 'l', 't', ' ', 'U', 'I', 'D'},
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool operator!=(const UUID& rhs) const {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
/**
|
||||
* Creates a random UUID.
|
||||
*
|
||||
* @returns A random UUID.
|
||||
*/
|
||||
static UUID MakeRandom();
|
||||
|
||||
// TODO(ogniK): Properly generate uuids based on RFC-4122
|
||||
[[nodiscard]] static UUID Generate();
|
||||
/**
|
||||
* Creates a random UUID with a seed.
|
||||
*
|
||||
* @param seed A seed to initialize the Mersenne-Twister RNG
|
||||
*
|
||||
* @returns A random UUID.
|
||||
*/
|
||||
static UUID MakeRandomWithSeed(u32 seed);
|
||||
|
||||
// Set the UUID to {0,0} to be considered an invalid user
|
||||
constexpr void Invalidate() {
|
||||
uuid = INVALID_UUID;
|
||||
}
|
||||
/**
|
||||
* Creates a random UUID. The generated UUID is RFC 4122 Version 4 compliant.
|
||||
*
|
||||
* @returns A random UUID that is RFC 4122 Version 4 compliant.
|
||||
*/
|
||||
static UUID MakeRandomRFC4122V4();
|
||||
|
||||
[[nodiscard]] constexpr bool IsInvalid() const {
|
||||
return uuid == INVALID_UUID;
|
||||
}
|
||||
[[nodiscard]] constexpr bool IsValid() const {
|
||||
return !IsInvalid();
|
||||
}
|
||||
|
||||
// TODO(ogniK): Properly generate a Nintendo ID
|
||||
[[nodiscard]] constexpr u64 GetNintendoID() const {
|
||||
return uuid[0];
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string Format() const;
|
||||
[[nodiscard]] std::string FormatSwitch() const;
|
||||
friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default;
|
||||
};
|
||||
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
|
||||
static_assert(sizeof(UUID) == 0x10, "UUID has incorrect size.");
|
||||
|
||||
/// An invalid UUID. This UUID has all its bytes set to 0.
|
||||
constexpr UUID InvalidUUID = {};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -82,7 +134,7 @@ namespace std {
|
||||
template <>
|
||||
struct hash<Common::UUID> {
|
||||
size_t operator()(const Common::UUID& uuid) const noexcept {
|
||||
return uuid.uuid[1] ^ uuid.uuid[0];
|
||||
return uuid.Hash();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -65,14 +65,20 @@ private:
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
|
||||
std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
|
||||
u32 emulated_clock_frequency) {
|
||||
std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
|
||||
u64 emulated_clock_frequency) {
|
||||
const auto& caps = GetCPUCaps();
|
||||
u64 rtsc_frequency = 0;
|
||||
if (caps.invariant_tsc) {
|
||||
rtsc_frequency = EstimateRDTSCFrequency();
|
||||
}
|
||||
if (rtsc_frequency == 0) {
|
||||
|
||||
// Fallback to StandardWallClock if the hardware TSC does not have the precision greater than:
|
||||
// - A nanosecond
|
||||
// - The emulated CPU frequency
|
||||
// - The emulated clock counter frequency (CNTFRQ)
|
||||
if (rtsc_frequency <= WallClock::NS_RATIO || rtsc_frequency <= emulated_cpu_frequency ||
|
||||
rtsc_frequency <= emulated_clock_frequency) {
|
||||
return std::make_unique<StandardWallClock>(emulated_cpu_frequency,
|
||||
emulated_clock_frequency);
|
||||
} else {
|
||||
@@ -83,8 +89,8 @@ std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
|
||||
|
||||
#else
|
||||
|
||||
std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
|
||||
u32 emulated_clock_frequency) {
|
||||
std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
|
||||
u64 emulated_clock_frequency) {
|
||||
return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,10 @@ namespace Common {
|
||||
|
||||
class WallClock {
|
||||
public:
|
||||
static constexpr u64 NS_RATIO = 1'000'000'000;
|
||||
static constexpr u64 US_RATIO = 1'000'000;
|
||||
static constexpr u64 MS_RATIO = 1'000;
|
||||
|
||||
virtual ~WallClock() = default;
|
||||
|
||||
/// Returns current wall time in nanoseconds
|
||||
@@ -49,7 +53,7 @@ private:
|
||||
bool is_native;
|
||||
};
|
||||
|
||||
[[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
|
||||
u32 emulated_clock_frequency);
|
||||
[[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
|
||||
u64 emulated_clock_frequency);
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -47,9 +47,9 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
|
||||
_mm_mfence();
|
||||
time_point.inner.last_measure = __rdtsc();
|
||||
time_point.inner.accumulated_ticks = 0U;
|
||||
ns_rtsc_factor = GetFixedPoint64Factor(1000000000, rtsc_frequency);
|
||||
us_rtsc_factor = GetFixedPoint64Factor(1000000, rtsc_frequency);
|
||||
ms_rtsc_factor = GetFixedPoint64Factor(1000, rtsc_frequency);
|
||||
ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency);
|
||||
us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency);
|
||||
ms_rtsc_factor = GetFixedPoint64Factor(MS_RATIO, rtsc_frequency);
|
||||
clock_rtsc_factor = GetFixedPoint64Factor(emulated_clock_frequency, rtsc_frequency);
|
||||
cpu_rtsc_factor = GetFixedPoint64Factor(emulated_cpu_frequency, rtsc_frequency);
|
||||
}
|
||||
|
||||
@@ -37,12 +37,12 @@ constexpr Xbyak::Reg IndexToReg(size_t reg_index) {
|
||||
}
|
||||
}
|
||||
|
||||
inline std::bitset<32> BuildRegSet(std::initializer_list<Xbyak::Reg> regs) {
|
||||
std::bitset<32> bits;
|
||||
constexpr std::bitset<32> BuildRegSet(std::initializer_list<Xbyak::Reg> regs) {
|
||||
size_t bits = 0;
|
||||
for (const Xbyak::Reg& reg : regs) {
|
||||
bits[RegToIndex(reg)] = true;
|
||||
bits |= size_t{1} << RegToIndex(reg);
|
||||
}
|
||||
return bits;
|
||||
return {bits};
|
||||
}
|
||||
|
||||
constexpr inline std::bitset<32> ABI_ALL_GPRS(0x0000FFFF);
|
||||
@@ -57,7 +57,7 @@ constexpr inline Xbyak::Reg ABI_PARAM2 = Xbyak::util::rdx;
|
||||
constexpr inline Xbyak::Reg ABI_PARAM3 = Xbyak::util::r8;
|
||||
constexpr inline Xbyak::Reg ABI_PARAM4 = Xbyak::util::r9;
|
||||
|
||||
const std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
|
||||
constexpr inline std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
|
||||
// GPRs
|
||||
Xbyak::util::rcx,
|
||||
Xbyak::util::rdx,
|
||||
@@ -74,7 +74,7 @@ const std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
|
||||
Xbyak::util::xmm5,
|
||||
});
|
||||
|
||||
const std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({
|
||||
constexpr inline std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({
|
||||
// GPRs
|
||||
Xbyak::util::rbx,
|
||||
Xbyak::util::rsi,
|
||||
@@ -108,7 +108,7 @@ constexpr inline Xbyak::Reg ABI_PARAM2 = Xbyak::util::rsi;
|
||||
constexpr inline Xbyak::Reg ABI_PARAM3 = Xbyak::util::rdx;
|
||||
constexpr inline Xbyak::Reg ABI_PARAM4 = Xbyak::util::rcx;
|
||||
|
||||
const std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
|
||||
constexpr inline std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
|
||||
// GPRs
|
||||
Xbyak::util::rcx,
|
||||
Xbyak::util::rdx,
|
||||
@@ -137,7 +137,7 @@ const std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
|
||||
Xbyak::util::xmm15,
|
||||
});
|
||||
|
||||
const std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({
|
||||
constexpr inline std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({
|
||||
// GPRs
|
||||
Xbyak::util::rbx,
|
||||
Xbyak::util::rbp,
|
||||
|
||||
@@ -187,6 +187,8 @@ add_library(core STATIC
|
||||
hle/kernel/k_event.h
|
||||
hle/kernel/k_handle_table.cpp
|
||||
hle/kernel/k_handle_table.h
|
||||
hle/kernel/k_interrupt_manager.cpp
|
||||
hle/kernel/k_interrupt_manager.h
|
||||
hle/kernel/k_light_condition_variable.cpp
|
||||
hle/kernel/k_light_condition_variable.h
|
||||
hle/kernel/k_light_lock.cpp
|
||||
@@ -245,6 +247,9 @@ add_library(core STATIC
|
||||
hle/kernel/k_trace.h
|
||||
hle/kernel/k_transfer_memory.cpp
|
||||
hle/kernel/k_transfer_memory.h
|
||||
hle/kernel/k_worker_task.h
|
||||
hle/kernel/k_worker_task_manager.cpp
|
||||
hle/kernel/k_worker_task_manager.h
|
||||
hle/kernel/k_writable_event.cpp
|
||||
hle/kernel/k_writable_event.h
|
||||
hle/kernel/kernel.cpp
|
||||
@@ -265,8 +270,6 @@ add_library(core STATIC
|
||||
hle/kernel/svc_wrap.h
|
||||
hle/kernel/time_manager.cpp
|
||||
hle/kernel/time_manager.h
|
||||
hle/lock.cpp
|
||||
hle/lock.h
|
||||
hle/result.h
|
||||
hle/service/acc/acc.cpp
|
||||
hle/service/acc/acc.h
|
||||
@@ -464,6 +467,8 @@ add_library(core STATIC
|
||||
hle/service/mii/types.h
|
||||
hle/service/mm/mm_u.cpp
|
||||
hle/service/mm/mm_u.h
|
||||
hle/service/mnpp/mnpp_app.cpp
|
||||
hle/service/mnpp/mnpp_app.h
|
||||
hle/service/ncm/ncm.cpp
|
||||
hle/service/ncm/ncm.h
|
||||
hle/service/nfc/nfc.cpp
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hardware_properties.h"
|
||||
|
||||
@@ -24,8 +25,11 @@ class CPUInterruptHandler;
|
||||
using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>;
|
||||
|
||||
/// Generic ARMv8 CPU interface
|
||||
class ARM_Interface : NonCopyable {
|
||||
class ARM_Interface {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(ARM_Interface);
|
||||
YUZU_NON_MOVEABLE(ARM_Interface);
|
||||
|
||||
explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers_,
|
||||
bool uses_wall_clock_)
|
||||
: system{system_}, interrupt_handlers{interrupt_handlers_}, uses_wall_clock{
|
||||
|
||||
@@ -317,6 +317,8 @@ struct System::Impl {
|
||||
is_powered_on = false;
|
||||
exit_lock = false;
|
||||
|
||||
gpu_core->NotifyShutdown();
|
||||
|
||||
services.reset();
|
||||
service_manager.reset();
|
||||
cheat_engine.reset();
|
||||
|
||||
@@ -128,15 +128,6 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
if (exefs == nullptr)
|
||||
return exefs;
|
||||
|
||||
if (Settings::values.dump_exefs) {
|
||||
LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
|
||||
const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id);
|
||||
if (dump_dir != nullptr) {
|
||||
const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
|
||||
VfsRawCopyD(exefs, exefs_dir);
|
||||
}
|
||||
}
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
const auto update_disabled =
|
||||
std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
|
||||
@@ -179,6 +170,15 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings::values.dump_exefs) {
|
||||
LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
|
||||
const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id);
|
||||
if (dump_dir != nullptr) {
|
||||
const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
|
||||
VfsRawCopyD(exefs, exefs_dir);
|
||||
}
|
||||
}
|
||||
|
||||
return exefs;
|
||||
}
|
||||
|
||||
|
||||
+15
-3
@@ -12,6 +12,7 @@
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
|
||||
@@ -29,8 +30,11 @@ enum class VfsEntryType {
|
||||
// A class representing an abstract filesystem. A default implementation given the root VirtualDir
|
||||
// is provided for convenience, but if the Vfs implementation has any additional state or
|
||||
// functionality, they will need to override.
|
||||
class VfsFilesystem : NonCopyable {
|
||||
class VfsFilesystem {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(VfsFilesystem);
|
||||
YUZU_NON_MOVEABLE(VfsFilesystem);
|
||||
|
||||
explicit VfsFilesystem(VirtualDir root);
|
||||
virtual ~VfsFilesystem();
|
||||
|
||||
@@ -77,8 +81,12 @@ protected:
|
||||
};
|
||||
|
||||
// A class representing a file in an abstract filesystem.
|
||||
class VfsFile : NonCopyable {
|
||||
class VfsFile {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(VfsFile);
|
||||
YUZU_NON_MOVEABLE(VfsFile);
|
||||
|
||||
VfsFile() = default;
|
||||
virtual ~VfsFile();
|
||||
|
||||
// Retrieves the file name.
|
||||
@@ -176,8 +184,12 @@ public:
|
||||
};
|
||||
|
||||
// A class representing a directory in an abstract filesystem.
|
||||
class VfsDirectory : NonCopyable {
|
||||
class VfsDirectory {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(VfsDirectory);
|
||||
YUZU_NON_MOVEABLE(VfsDirectory);
|
||||
|
||||
VfsDirectory() = default;
|
||||
virtual ~VfsDirectory();
|
||||
|
||||
// Retrives the file located at path as if the current directory was root. Returns nullptr if
|
||||
|
||||
@@ -45,26 +45,26 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
|
||||
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
|
||||
if (parameters.allow_pro_controller) {
|
||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
|
||||
controller->Connect();
|
||||
controller->Connect(true);
|
||||
} else if (parameters.allow_dual_joycons) {
|
||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual);
|
||||
controller->Connect();
|
||||
controller->Connect(true);
|
||||
} else if (parameters.allow_left_joycon && parameters.allow_right_joycon) {
|
||||
// Assign left joycons to even player indices and right joycons to odd player indices.
|
||||
// We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and
|
||||
// a right Joycon for Player 2 in 2 Player Assist mode.
|
||||
if (index % 2 == 0) {
|
||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconLeft);
|
||||
controller->Connect();
|
||||
controller->Connect(true);
|
||||
} else {
|
||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconRight);
|
||||
controller->Connect();
|
||||
controller->Connect(true);
|
||||
}
|
||||
} else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
|
||||
!Settings::values.use_docked_mode.GetValue()) {
|
||||
// We should *never* reach here under any normal circumstances.
|
||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
|
||||
controller->Connect();
|
||||
controller->Connect(true);
|
||||
} else {
|
||||
UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!");
|
||||
}
|
||||
|
||||
@@ -13,8 +13,7 @@ ProfileSelectApplet::~ProfileSelectApplet() = default;
|
||||
void DefaultProfileSelectApplet::SelectProfile(
|
||||
std::function<void(std::optional<Common::UUID>)> callback) const {
|
||||
Service::Account::ProfileManager manager;
|
||||
callback(manager.GetUser(Settings::values.current_user.GetValue())
|
||||
.value_or(Common::UUID{Common::INVALID_UUID}));
|
||||
callback(manager.GetUser(Settings::values.current_user.GetValue()).value_or(Common::UUID{}));
|
||||
LOG_INFO(Service_ACC, "called, selecting current user instead of prompting...");
|
||||
}
|
||||
|
||||
|
||||
@@ -158,10 +158,13 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
|
||||
auto& motion = console.motion_state;
|
||||
motion.accel = emulated.GetAcceleration();
|
||||
motion.gyro = emulated.GetGyroscope();
|
||||
motion.rotation = emulated.GetGyroscope();
|
||||
motion.rotation = emulated.GetRotations();
|
||||
motion.orientation = emulated.GetOrientation();
|
||||
motion.quaternion = emulated.GetQuaternion();
|
||||
motion.gyro_bias = emulated.GetGyroBias();
|
||||
motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
|
||||
// Find what is this value
|
||||
motion.verticalization_error = 0.0f;
|
||||
|
||||
TriggerOnChange(ConsoleTriggerType::Motion);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/input.h"
|
||||
#include "common/param_package.h"
|
||||
@@ -50,6 +51,8 @@ struct ConsoleMotion {
|
||||
Common::Vec3f rotation{};
|
||||
std::array<Common::Vec3f, 3> orientation{};
|
||||
Common::Quaternion<f32> quaternion{};
|
||||
Common::Vec3f gyro_bias{};
|
||||
f32 verticalization_error{};
|
||||
bool is_at_rest{};
|
||||
};
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ void EmulatedController::LoadDevices() {
|
||||
motion_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>);
|
||||
std::transform(trigger_params.begin(), trigger_params.end(), trigger_devices.begin(),
|
||||
Common::Input::CreateDevice<Common::Input::InputDevice>);
|
||||
std::transform(battery_params.begin(), battery_params.begin(), battery_devices.end(),
|
||||
std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(),
|
||||
Common::Input::CreateDevice<Common::Input::InputDevice>);
|
||||
std::transform(output_params.begin(), output_params.end(), output_devices.begin(),
|
||||
Common::Input::CreateDevice<Common::Input::OutputDevice>);
|
||||
@@ -269,7 +269,8 @@ void EmulatedController::ReloadInput() {
|
||||
}
|
||||
|
||||
// Use a common UUID for TAS
|
||||
const auto tas_uuid = Common::UUID{0x0, 0x7A5};
|
||||
static constexpr Common::UUID TAS_UUID = Common::UUID{
|
||||
{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
|
||||
|
||||
// Register TAS devices. No need to force update
|
||||
for (std::size_t index = 0; index < tas_button_devices.size(); ++index) {
|
||||
@@ -278,8 +279,8 @@ void EmulatedController::ReloadInput() {
|
||||
}
|
||||
tas_button_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index, tas_uuid](const Common::Input::CallbackStatus& callback) {
|
||||
SetButton(callback, index, tas_uuid);
|
||||
[this, index](const Common::Input::CallbackStatus& callback) {
|
||||
SetButton(callback, index, TAS_UUID);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -290,8 +291,8 @@ void EmulatedController::ReloadInput() {
|
||||
}
|
||||
tas_stick_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index, tas_uuid](const Common::Input::CallbackStatus& callback) {
|
||||
SetStick(callback, index, tas_uuid);
|
||||
[this, index](const Common::Input::CallbackStatus& callback) {
|
||||
SetStick(callback, index, TAS_UUID);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -351,6 +352,19 @@ void EmulatedController::DisableConfiguration() {
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatedController::EnableSystemButtons() {
|
||||
system_buttons_enabled = true;
|
||||
}
|
||||
|
||||
void EmulatedController::DisableSystemButtons() {
|
||||
system_buttons_enabled = false;
|
||||
}
|
||||
|
||||
void EmulatedController::ResetSystemButtons() {
|
||||
controller.home_button_state.home.Assign(false);
|
||||
controller.capture_button_state.capture.Assign(false);
|
||||
}
|
||||
|
||||
bool EmulatedController::IsConfiguring() const {
|
||||
return is_configuring;
|
||||
}
|
||||
@@ -389,7 +403,8 @@ std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices(
|
||||
devices.begin(), devices.end(), [param](const Common::ParamPackage param_) {
|
||||
return param.Get("engine", "") == param_.Get("engine", "") &&
|
||||
param.Get("guid", "") == param_.Get("guid", "") &&
|
||||
param.Get("port", 0) == param_.Get("port", 0);
|
||||
param.Get("port", 0) == param_.Get("port", 0) &&
|
||||
param.Get("pad", 0) == param_.Get("pad", 0);
|
||||
});
|
||||
if (devices_it != devices.end()) {
|
||||
continue;
|
||||
@@ -398,6 +413,7 @@ std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices(
|
||||
device.Set("engine", param.Get("engine", ""));
|
||||
device.Set("guid", param.Get("guid", ""));
|
||||
device.Set("port", param.Get("port", 0));
|
||||
device.Set("pad", param.Get("pad", 0));
|
||||
devices.push_back(device);
|
||||
}
|
||||
|
||||
@@ -412,7 +428,8 @@ std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices(
|
||||
devices.begin(), devices.end(), [param](const Common::ParamPackage param_) {
|
||||
return param.Get("engine", "") == param_.Get("engine", "") &&
|
||||
param.Get("guid", "") == param_.Get("guid", "") &&
|
||||
param.Get("port", 0) == param_.Get("port", 0);
|
||||
param.Get("port", 0) == param_.Get("port", 0) &&
|
||||
param.Get("pad", 0) == param_.Get("pad", 0);
|
||||
});
|
||||
if (devices_it != devices.end()) {
|
||||
continue;
|
||||
@@ -421,6 +438,7 @@ std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices(
|
||||
device.Set("engine", param.Get("engine", ""));
|
||||
device.Set("guid", param.Get("guid", ""));
|
||||
device.Set("port", param.Get("port", 0));
|
||||
device.Set("pad", param.Get("pad", 0));
|
||||
devices.push_back(device);
|
||||
}
|
||||
return devices;
|
||||
@@ -596,7 +614,16 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
|
||||
controller.npad_button_state.right_sr.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::Home:
|
||||
if (!system_buttons_enabled) {
|
||||
break;
|
||||
}
|
||||
controller.home_button_state.home.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::Screenshot:
|
||||
if (!system_buttons_enabled) {
|
||||
break;
|
||||
}
|
||||
controller.capture_button_state.capture.Assign(current_status.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -723,6 +750,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
|
||||
raw_status.gyro.y.value,
|
||||
raw_status.gyro.z.value,
|
||||
});
|
||||
emulated.SetGyroThreshold(raw_status.gyro.x.properties.threshold);
|
||||
emulated.UpdateRotation(raw_status.delta_timestamp);
|
||||
emulated.UpdateOrientation(raw_status.delta_timestamp);
|
||||
force_update_motion = raw_status.force_update;
|
||||
@@ -843,23 +871,24 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
|
||||
}
|
||||
|
||||
bool EmulatedController::TestVibration(std::size_t device_index) {
|
||||
if (device_index >= output_devices.size()) {
|
||||
return false;
|
||||
}
|
||||
if (!output_devices[device_index]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send a slight vibration to test for rumble support
|
||||
constexpr Common::Input::VibrationStatus status = {
|
||||
static constexpr VibrationValue test_vibration = {
|
||||
.low_amplitude = 0.001f,
|
||||
.low_frequency = 160.0f,
|
||||
.high_amplitude = 0.001f,
|
||||
.high_frequency = 320.0f,
|
||||
.type = Common::Input::VibrationAmplificationType::Linear,
|
||||
};
|
||||
return output_devices[device_index]->SetVibration(status) ==
|
||||
Common::Input::VibrationError::None;
|
||||
|
||||
// Send a slight vibration to test for rumble support
|
||||
SetVibration(device_index, test_vibration);
|
||||
|
||||
// Stop any vibration and return the result
|
||||
return SetVibration(device_index, DEFAULT_VIBRATION_VALUE);
|
||||
}
|
||||
|
||||
bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) {
|
||||
LOG_INFO(Service_HID, "Set polling mode {}", polling_mode);
|
||||
auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
|
||||
return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None;
|
||||
}
|
||||
|
||||
void EmulatedController::SetLedPattern() {
|
||||
@@ -884,15 +913,42 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
|
||||
if (!is_connected) {
|
||||
return;
|
||||
}
|
||||
if (!IsControllerSupported()) {
|
||||
LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller",
|
||||
npad_type);
|
||||
Disconnect();
|
||||
if (IsControllerSupported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Disconnect();
|
||||
|
||||
// Fallback fullkey controllers to Pro controllers
|
||||
if (IsControllerFullkey() && supported_style_tag.fullkey) {
|
||||
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
|
||||
SetNpadStyleIndex(NpadStyleIndex::ProController);
|
||||
Connect();
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller",
|
||||
npad_type);
|
||||
}
|
||||
|
||||
bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
|
||||
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
|
||||
switch (type) {
|
||||
case NpadStyleIndex::ProController:
|
||||
case NpadStyleIndex::GameCube:
|
||||
case NpadStyleIndex::NES:
|
||||
case NpadStyleIndex::SNES:
|
||||
case NpadStyleIndex::N64:
|
||||
case NpadStyleIndex::SegaGenesis:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool EmulatedController::IsControllerSupported() const {
|
||||
switch (npad_type) {
|
||||
bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
|
||||
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
|
||||
switch (type) {
|
||||
case NpadStyleIndex::ProController:
|
||||
return supported_style_tag.fullkey;
|
||||
case NpadStyleIndex::Handheld:
|
||||
@@ -920,9 +976,10 @@ bool EmulatedController::IsControllerSupported() const {
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatedController::Connect() {
|
||||
if (!IsControllerSupported()) {
|
||||
LOG_ERROR(Service_HID, "Controller type {} is not supported", npad_type);
|
||||
void EmulatedController::Connect(bool use_temporary_value) {
|
||||
if (!IsControllerSupported(use_temporary_value)) {
|
||||
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
|
||||
LOG_ERROR(Service_HID, "Controller type {} is not supported", type);
|
||||
return;
|
||||
}
|
||||
{
|
||||
@@ -1054,6 +1111,20 @@ BatteryValues EmulatedController::GetBatteryValues() const {
|
||||
return controller.battery_values;
|
||||
}
|
||||
|
||||
HomeButtonState EmulatedController::GetHomeButtons() const {
|
||||
if (is_configuring) {
|
||||
return {};
|
||||
}
|
||||
return controller.home_button_state;
|
||||
}
|
||||
|
||||
CaptureButtonState EmulatedController::GetCaptureButtons() const {
|
||||
if (is_configuring) {
|
||||
return {};
|
||||
}
|
||||
return controller.capture_button_state;
|
||||
}
|
||||
|
||||
NpadButtonState EmulatedController::GetNpadButtons() const {
|
||||
if (is_configuring) {
|
||||
return {};
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/input.h"
|
||||
#include "common/param_package.h"
|
||||
#include "common/point.h"
|
||||
#include "common/quaternion.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/vector_math.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
@@ -101,6 +99,8 @@ struct ControllerStatus {
|
||||
VibrationValues vibration_values{};
|
||||
|
||||
// Data for HID serices
|
||||
HomeButtonState home_button_state{};
|
||||
CaptureButtonState capture_button_state{};
|
||||
NpadButtonState npad_button_state{};
|
||||
DebugPadButton debug_pad_button_state{};
|
||||
AnalogSticks analog_stick_state{};
|
||||
@@ -167,8 +167,11 @@ public:
|
||||
*/
|
||||
void SetSupportedNpadStyleTag(NpadStyleTag supported_styles);
|
||||
|
||||
/// Sets the connected status to true
|
||||
void Connect();
|
||||
/**
|
||||
* Sets the connected status to true
|
||||
* @param use_temporary_value If true tmp_npad_type will be used
|
||||
*/
|
||||
void Connect(bool use_temporary_value = false);
|
||||
|
||||
/// Sets the connected status to false
|
||||
void Disconnect();
|
||||
@@ -195,6 +198,15 @@ public:
|
||||
/// Returns the emulated controller into normal mode, allowing the modification of the HID state
|
||||
void DisableConfiguration();
|
||||
|
||||
/// Enables Home and Screenshot buttons
|
||||
void EnableSystemButtons();
|
||||
|
||||
/// Disables Home and Screenshot buttons
|
||||
void DisableSystemButtons();
|
||||
|
||||
/// Sets Home and Screenshot buttons to false
|
||||
void ResetSystemButtons();
|
||||
|
||||
/// Returns true if the emulated controller is in configuring mode
|
||||
bool IsConfiguring() const;
|
||||
|
||||
@@ -258,7 +270,13 @@ public:
|
||||
/// Returns the latest battery status from the controller with parameters
|
||||
BatteryValues GetBatteryValues() const;
|
||||
|
||||
/// Returns the latest status of button input for the npad service
|
||||
/// Returns the latest status of button input for the hid::HomeButton service
|
||||
HomeButtonState GetHomeButtons() const;
|
||||
|
||||
/// Returns the latest status of button input for the hid::CaptureButton service
|
||||
CaptureButtonState GetCaptureButtons() const;
|
||||
|
||||
/// Returns the latest status of button input for the hid::Npad service
|
||||
NpadButtonState GetNpadButtons() const;
|
||||
|
||||
/// Returns the latest status of button input for the debug pad service
|
||||
@@ -281,16 +299,23 @@ public:
|
||||
|
||||
/**
|
||||
* Sends a specific vibration to the output device
|
||||
* @return returns true if vibration had no errors
|
||||
* @return true if vibration had no errors
|
||||
*/
|
||||
bool SetVibration(std::size_t device_index, VibrationValue vibration);
|
||||
|
||||
/**
|
||||
* Sends a small vibration to the output device
|
||||
* @return returns true if SetVibration was successfull
|
||||
* @return true if SetVibration was successfull
|
||||
*/
|
||||
bool TestVibration(std::size_t device_index);
|
||||
|
||||
/**
|
||||
* Sets the desired data to be polled from a controller
|
||||
* @param polling_mode type of input desired buttons, gyro, nfc, ir, etc.
|
||||
* @return true if SetPollingMode was successfull
|
||||
*/
|
||||
bool SetPollingMode(Common::Input::PollingMode polling_mode);
|
||||
|
||||
/// Returns the led pattern corresponding to this emulated controller
|
||||
LedPattern GetLedPattern() const;
|
||||
|
||||
@@ -317,11 +342,18 @@ private:
|
||||
/// Set the params for TAS devices
|
||||
void LoadTASParams();
|
||||
|
||||
/**
|
||||
* @param use_temporary_value If true tmp_npad_type will be used
|
||||
* @return true if the controller style is fullkey
|
||||
*/
|
||||
bool IsControllerFullkey(bool use_temporary_value = false) const;
|
||||
|
||||
/**
|
||||
* Checks the current controller type against the supported_style_tag
|
||||
* @param use_temporary_value If true tmp_npad_type will be used
|
||||
* @return true if the controller is supported
|
||||
*/
|
||||
bool IsControllerSupported() const;
|
||||
bool IsControllerSupported(bool use_temporary_value = false) const;
|
||||
|
||||
/**
|
||||
* Updates the button status of the controller
|
||||
@@ -373,6 +405,7 @@ private:
|
||||
NpadStyleTag supported_style_tag{NpadStyleSet::All};
|
||||
bool is_connected{false};
|
||||
bool is_configuring{false};
|
||||
bool system_buttons_enabled{true};
|
||||
f32 motion_sensitivity{0.01f};
|
||||
bool force_update_motion{false};
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
|
||||
namespace Core::HID {
|
||||
|
||||
@@ -378,6 +378,26 @@ struct LedPattern {
|
||||
};
|
||||
};
|
||||
|
||||
struct HomeButtonState {
|
||||
union {
|
||||
u64 raw{};
|
||||
|
||||
// Buttons
|
||||
BitField<0, 1, u64> home;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(HomeButtonState) == 0x8, "HomeButtonState has incorrect size.");
|
||||
|
||||
struct CaptureButtonState {
|
||||
union {
|
||||
u64 raw{};
|
||||
|
||||
// Buttons
|
||||
BitField<0, 1, u64> capture;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(CaptureButtonState) == 0x8, "CaptureButtonState has incorrect size.");
|
||||
|
||||
struct NpadButtonState {
|
||||
union {
|
||||
NpadButton raw{};
|
||||
@@ -496,6 +516,13 @@ struct VibrationValue {
|
||||
};
|
||||
static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size.");
|
||||
|
||||
constexpr VibrationValue DEFAULT_VIBRATION_VALUE{
|
||||
.low_amplitude = 0.0f,
|
||||
.low_frequency = 160.0f,
|
||||
.high_amplitude = 0.0f,
|
||||
.high_frequency = 320.0f,
|
||||
};
|
||||
|
||||
// This is nn::hid::VibrationDeviceInfo
|
||||
struct VibrationDeviceInfo {
|
||||
VibrationDeviceType type{};
|
||||
|
||||
@@ -28,7 +28,7 @@ Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackSta
|
||||
if (value > 0.8f) {
|
||||
battery = Common::Input::BatteryLevel::Full;
|
||||
}
|
||||
if (value >= 1.0f) {
|
||||
if (value >= 0.95f) {
|
||||
battery = Common::Input::BatteryLevel::Charging;
|
||||
}
|
||||
break;
|
||||
@@ -114,7 +114,7 @@ Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatu
|
||||
if (TransformToButton(callback).value) {
|
||||
std::random_device device;
|
||||
std::mt19937 gen(device());
|
||||
std::uniform_int_distribution<s16> distribution(-1000, 1000);
|
||||
std::uniform_int_distribution<s16> distribution(-5000, 5000);
|
||||
status.accel.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
|
||||
status.accel.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
|
||||
status.accel.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace Core::HID {
|
||||
MotionInput::MotionInput() {
|
||||
// Initialize PID constants with default values
|
||||
SetPID(0.3f, 0.005f, 0.0f);
|
||||
SetGyroThreshold(0.007f);
|
||||
}
|
||||
|
||||
void MotionInput::SetPID(f32 new_kp, f32 new_ki, f32 new_kd) {
|
||||
@@ -23,14 +24,14 @@ void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) {
|
||||
}
|
||||
|
||||
void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) {
|
||||
gyro = gyroscope - gyro_drift;
|
||||
gyro = gyroscope - gyro_bias;
|
||||
|
||||
// Auto adjust drift to minimize drift
|
||||
if (!IsMoving(0.1f)) {
|
||||
gyro_drift = (gyro_drift * 0.9999f) + (gyroscope * 0.0001f);
|
||||
gyro_bias = (gyro_bias * 0.9999f) + (gyroscope * 0.0001f);
|
||||
}
|
||||
|
||||
if (gyro.Length2() < gyro_threshold) {
|
||||
if (gyro.Length() < gyro_threshold) {
|
||||
gyro = {};
|
||||
} else {
|
||||
only_accelerometer = false;
|
||||
@@ -41,8 +42,8 @@ void MotionInput::SetQuaternion(const Common::Quaternion<f32>& quaternion) {
|
||||
quat = quaternion;
|
||||
}
|
||||
|
||||
void MotionInput::SetGyroDrift(const Common::Vec3f& drift) {
|
||||
gyro_drift = drift;
|
||||
void MotionInput::SetGyroBias(const Common::Vec3f& bias) {
|
||||
gyro_bias = bias;
|
||||
}
|
||||
|
||||
void MotionInput::SetGyroThreshold(f32 threshold) {
|
||||
@@ -192,6 +193,10 @@ Common::Vec3f MotionInput::GetGyroscope() const {
|
||||
return gyro;
|
||||
}
|
||||
|
||||
Common::Vec3f MotionInput::GetGyroBias() const {
|
||||
return gyro_bias;
|
||||
}
|
||||
|
||||
Common::Quaternion<f32> MotionInput::GetQuaternion() const {
|
||||
return quat;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
void SetAcceleration(const Common::Vec3f& acceleration);
|
||||
void SetGyroscope(const Common::Vec3f& gyroscope);
|
||||
void SetQuaternion(const Common::Quaternion<f32>& quaternion);
|
||||
void SetGyroDrift(const Common::Vec3f& drift);
|
||||
void SetGyroBias(const Common::Vec3f& bias);
|
||||
void SetGyroThreshold(f32 threshold);
|
||||
|
||||
void EnableReset(bool reset);
|
||||
@@ -36,6 +36,7 @@ public:
|
||||
[[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const;
|
||||
[[nodiscard]] Common::Vec3f GetAcceleration() const;
|
||||
[[nodiscard]] Common::Vec3f GetGyroscope() const;
|
||||
[[nodiscard]] Common::Vec3f GetGyroBias() const;
|
||||
[[nodiscard]] Common::Vec3f GetRotations() const;
|
||||
[[nodiscard]] Common::Quaternion<f32> GetQuaternion() const;
|
||||
|
||||
@@ -69,7 +70,7 @@ private:
|
||||
Common::Vec3f gyro;
|
||||
|
||||
// Vector to be substracted from gyro measurements
|
||||
Common::Vec3f gyro_drift;
|
||||
Common::Vec3f gyro_bias;
|
||||
|
||||
// Minimum gyro amplitude to detect if the device is moving
|
||||
f32 gyro_threshold = 0.0f;
|
||||
|
||||
@@ -404,6 +404,11 @@ inline s32 RequestParser::Pop() {
|
||||
return static_cast<s32>(Pop<u32>());
|
||||
}
|
||||
|
||||
// Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects.
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wclass-memaccess"
|
||||
#endif
|
||||
template <typename T>
|
||||
void RequestParser::PopRaw(T& value) {
|
||||
static_assert(std::is_trivially_copyable_v<T>,
|
||||
@@ -411,6 +416,9 @@ void RequestParser::PopRaw(T& value) {
|
||||
std::memcpy(&value, cmdbuf + index, sizeof(T));
|
||||
index += (sizeof(T) + 3) / 4; // round up to word length
|
||||
}
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
T RequestParser::PopRaw() {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "core/hle/kernel/global_scheduler_context.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/physical_core.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -42,6 +43,11 @@ void GlobalSchedulerContext::PreemptThreads() {
|
||||
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
|
||||
const u32 priority = preemption_priorities[core_id];
|
||||
kernel.Scheduler(core_id).RotateScheduledQueue(core_id, priority);
|
||||
|
||||
// Signal an interrupt occurred. For core 3, this is a certainty, as preemption will result
|
||||
// in the rotator thread being scheduled. For cores 0-2, this is to simulate or system
|
||||
// interrupts that may have occurred.
|
||||
kernel.PhysicalCore(core_id).Interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -341,10 +341,6 @@ public:
|
||||
return *thread;
|
||||
}
|
||||
|
||||
bool IsThreadWaiting() const {
|
||||
return is_thread_waiting;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class IPC::ResponseBuilder;
|
||||
|
||||
@@ -379,7 +375,6 @@ private:
|
||||
u32 domain_offset{};
|
||||
|
||||
std::shared_ptr<SessionRequestManager> manager;
|
||||
bool is_thread_waiting{};
|
||||
|
||||
KernelCore& kernel;
|
||||
Core::Memory::Memory& memory;
|
||||
|
||||
@@ -31,8 +31,6 @@ public:
|
||||
}
|
||||
|
||||
constexpr void SetAffinity(s32 core, bool set) {
|
||||
ASSERT(0 <= core && core < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
|
||||
|
||||
if (set) {
|
||||
this->mask |= GetCoreBit(core);
|
||||
} else {
|
||||
|
||||
@@ -20,8 +20,6 @@ class KernelCore;
|
||||
class KProcess;
|
||||
|
||||
#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \
|
||||
YUZU_NON_COPYABLE(CLASS); \
|
||||
YUZU_NON_MOVEABLE(CLASS); \
|
||||
\
|
||||
private: \
|
||||
friend class ::Kernel::KClassTokenGenerator; \
|
||||
@@ -32,6 +30,9 @@ private:
|
||||
} \
|
||||
\
|
||||
public: \
|
||||
YUZU_NON_COPYABLE(CLASS); \
|
||||
YUZU_NON_MOVEABLE(CLASS); \
|
||||
\
|
||||
using BaseClass = BASE_CLASS; \
|
||||
static constexpr TypeObj GetStaticTypeObj() { \
|
||||
constexpr ClassTokenType Token = ClassToken(); \
|
||||
@@ -224,9 +225,9 @@ private:
|
||||
|
||||
template <typename T>
|
||||
class KScopedAutoObject {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KScopedAutoObject);
|
||||
|
||||
public:
|
||||
constexpr KScopedAutoObject() = default;
|
||||
|
||||
constexpr KScopedAutoObject(T* o) : m_obj(o) {
|
||||
|
||||
@@ -16,13 +16,12 @@ class KernelCore;
|
||||
class KProcess;
|
||||
|
||||
class KAutoObjectWithListContainer {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KAutoObjectWithListContainer);
|
||||
YUZU_NON_MOVEABLE(KAutoObjectWithListContainer);
|
||||
|
||||
public:
|
||||
using ListType = boost::intrusive::rbtree<KAutoObjectWithList>;
|
||||
|
||||
public:
|
||||
class ListAccessor : public KScopedLightLock {
|
||||
public:
|
||||
explicit ListAccessor(KAutoObjectWithListContainer* container)
|
||||
@@ -48,7 +47,6 @@ public:
|
||||
|
||||
friend class ListAccessor;
|
||||
|
||||
public:
|
||||
KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(kernel), m_object_list() {}
|
||||
|
||||
void Initialize() {}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/device_memory.h"
|
||||
#include "core/hle/kernel/k_auto_object.h"
|
||||
@@ -28,8 +29,7 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr
|
||||
auto& page_table = m_owner->PageTable();
|
||||
|
||||
// Construct the page group.
|
||||
KMemoryInfo kBlockInfo = page_table.QueryInfo(addr);
|
||||
m_page_group = KPageLinkedList(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages());
|
||||
m_page_group = KPageLinkedList(addr, Common::DivideUp(size, PageSize));
|
||||
|
||||
// Lock the memory.
|
||||
R_TRY(page_table.LockForCodeMemory(addr, size))
|
||||
@@ -143,4 +143,4 @@ ResultCode KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -22,13 +22,12 @@ namespace Kernel {
|
||||
class KernelCore;
|
||||
|
||||
class KHandleTable {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KHandleTable);
|
||||
YUZU_NON_MOVEABLE(KHandleTable);
|
||||
|
||||
public:
|
||||
static constexpr size_t MaxTableSize = 1024;
|
||||
|
||||
public:
|
||||
explicit KHandleTable(KernelCore& kernel_);
|
||||
~KHandleTable();
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/kernel/k_interrupt_manager.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
|
||||
namespace Kernel::KInterruptManager {
|
||||
|
||||
void HandleInterrupt(KernelCore& kernel, s32 core_id) {
|
||||
auto* process = kernel.CurrentProcess();
|
||||
if (!process) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& scheduler = kernel.Scheduler(core_id);
|
||||
auto& current_thread = *scheduler.GetCurrentThread();
|
||||
|
||||
// If the user disable count is set, we may need to pin the current thread.
|
||||
if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// Pin the current thread.
|
||||
process->PinCurrentThread(core_id);
|
||||
|
||||
// Set the interrupt flag for the thread.
|
||||
scheduler.GetCurrentThread()->SetInterruptFlag();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Kernel::KInterruptManager
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
|
||||
namespace KInterruptManager {
|
||||
void HandleInterrupt(KernelCore& kernel, s32 core_id);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -70,12 +70,12 @@ enum class KMemoryState : u32 {
|
||||
ThreadLocal =
|
||||
static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted,
|
||||
|
||||
Transferred = static_cast<u32>(Svc::MemoryState::Transferred) | FlagsMisc |
|
||||
FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
|
||||
FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||
Transfered = static_cast<u32>(Svc::MemoryState::Transferred) | FlagsMisc |
|
||||
FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
|
||||
FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||
|
||||
SharedTransferred = static_cast<u32>(Svc::MemoryState::SharedTransferred) | FlagsMisc |
|
||||
FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||
SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransferred) | FlagsMisc |
|
||||
FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||
|
||||
SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped |
|
||||
FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||
@@ -93,6 +93,8 @@ enum class KMemoryState : u32 {
|
||||
GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped |
|
||||
FlagReferenceCounted | FlagCanDebug,
|
||||
CodeOut = static_cast<u32>(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted,
|
||||
|
||||
Coverage = static_cast<u32>(Svc::MemoryState::Coverage) | FlagMapped,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(KMemoryState);
|
||||
|
||||
@@ -108,8 +110,8 @@ static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x03FFBD09);
|
||||
static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x005C3C0A);
|
||||
static_assert(static_cast<u32>(KMemoryState::Stack) == 0x005C3C0B);
|
||||
static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0040200C);
|
||||
static_assert(static_cast<u32>(KMemoryState::Transferred) == 0x015C3C0D);
|
||||
static_assert(static_cast<u32>(KMemoryState::SharedTransferred) == 0x005C380E);
|
||||
static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x015C3C0D);
|
||||
static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x005C380E);
|
||||
static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0040380F);
|
||||
static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010);
|
||||
static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x005C3811);
|
||||
@@ -117,10 +119,11 @@ static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x004C2812);
|
||||
static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00002013);
|
||||
static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x00402214);
|
||||
static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x00402015);
|
||||
static_assert(static_cast<u32>(KMemoryState::Coverage) == 0x00002016);
|
||||
|
||||
enum class KMemoryPermission : u8 {
|
||||
None = 0,
|
||||
Mask = static_cast<u8>(~None),
|
||||
All = static_cast<u8>(~None),
|
||||
|
||||
Read = 1 << 0,
|
||||
Write = 1 << 1,
|
||||
@@ -155,7 +158,13 @@ enum class KMemoryPermission : u8 {
|
||||
DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission);
|
||||
|
||||
constexpr KMemoryPermission ConvertToKMemoryPermission(Svc::MemoryPermission perm) {
|
||||
return static_cast<KMemoryPermission>(perm);
|
||||
return static_cast<KMemoryPermission>(
|
||||
(static_cast<KMemoryPermission>(perm) & KMemoryPermission::UserMask) |
|
||||
KMemoryPermission::KernelRead |
|
||||
((static_cast<KMemoryPermission>(perm) & KMemoryPermission::UserWrite)
|
||||
<< KMemoryPermission::KernelShift) |
|
||||
(perm == Svc::MemoryPermission::None ? KMemoryPermission::NotMapped
|
||||
: KMemoryPermission::None));
|
||||
}
|
||||
|
||||
enum class KMemoryAttribute : u8 {
|
||||
@@ -169,6 +178,8 @@ enum class KMemoryAttribute : u8 {
|
||||
DeviceShared = static_cast<u8>(Svc::MemoryAttribute::DeviceShared),
|
||||
Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached),
|
||||
|
||||
SetMask = Uncached,
|
||||
|
||||
IpcAndDeviceMapped = IpcLocked | DeviceShared,
|
||||
LockedAndIpcLocked = Locked | IpcLocked,
|
||||
DeviceSharedAndUncached = DeviceShared | Uncached
|
||||
@@ -215,6 +226,15 @@ struct KMemoryInfo {
|
||||
constexpr VAddr GetLastAddress() const {
|
||||
return GetEndAddress() - 1;
|
||||
}
|
||||
constexpr KMemoryState GetState() const {
|
||||
return state;
|
||||
}
|
||||
constexpr KMemoryAttribute GetAttribute() const {
|
||||
return attribute;
|
||||
}
|
||||
constexpr KMemoryPermission GetPermission() const {
|
||||
return perm;
|
||||
}
|
||||
};
|
||||
|
||||
class KMemoryBlock final {
|
||||
|
||||
@@ -8,12 +8,16 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
#include "core/device_memory.h"
|
||||
#include "core/hle/kernel/k_memory_manager.h"
|
||||
#include "core/hle/kernel/k_page_linked_list.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
KMemoryManager::KMemoryManager(Core::System& system_) : system{system_} {}
|
||||
|
||||
std::size_t KMemoryManager::Impl::Initialize(Pool new_pool, u64 start_address, u64 end_address) {
|
||||
const auto size{end_address - start_address};
|
||||
|
||||
@@ -81,7 +85,7 @@ VAddr KMemoryManager::AllocateAndOpenContinuous(std::size_t num_pages, std::size
|
||||
}
|
||||
|
||||
ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
|
||||
Direction dir) {
|
||||
Direction dir, u32 heap_fill_value) {
|
||||
ASSERT(page_list.GetNumPages() == 0);
|
||||
|
||||
// Early return if we're allocating no pages
|
||||
@@ -139,6 +143,12 @@ ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_
|
||||
}
|
||||
}
|
||||
|
||||
// Clear allocated memory.
|
||||
for (const auto& it : page_list.Nodes()) {
|
||||
std::memset(system.DeviceMemory().GetPointer(it.GetAddress()), heap_fill_value,
|
||||
it.GetSize());
|
||||
}
|
||||
|
||||
// Only succeed if we allocated as many pages as we wanted
|
||||
if (num_pages) {
|
||||
return ResultOutOfMemory;
|
||||
@@ -146,11 +156,12 @@ ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_
|
||||
|
||||
// We succeeded!
|
||||
group_guard.Cancel();
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KMemoryManager::Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
|
||||
Direction dir) {
|
||||
Direction dir, u32 heap_fill_value) {
|
||||
// Early return if we're freeing no pages
|
||||
if (!num_pages) {
|
||||
return ResultSuccess;
|
||||
|
||||
@@ -8,16 +8,24 @@
|
||||
#include <mutex>
|
||||
#include <tuple>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/k_page_heap.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KPageLinkedList;
|
||||
|
||||
class KMemoryManager final : NonCopyable {
|
||||
class KMemoryManager final {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KMemoryManager);
|
||||
YUZU_NON_MOVEABLE(KMemoryManager);
|
||||
|
||||
enum class Pool : u32 {
|
||||
Application = 0,
|
||||
Applet = 1,
|
||||
@@ -42,7 +50,7 @@ public:
|
||||
Mask = (0xF << Shift),
|
||||
};
|
||||
|
||||
KMemoryManager() = default;
|
||||
explicit KMemoryManager(Core::System& system_);
|
||||
|
||||
constexpr std::size_t GetSize(Pool pool) const {
|
||||
return managers[static_cast<std::size_t>(pool)].GetSize();
|
||||
@@ -51,10 +59,10 @@ public:
|
||||
void InitializeManager(Pool pool, u64 start_address, u64 end_address);
|
||||
|
||||
VAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option);
|
||||
ResultCode Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
|
||||
Direction dir = Direction::FromFront);
|
||||
ResultCode Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
|
||||
Direction dir = Direction::FromFront);
|
||||
ResultCode Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, Direction dir,
|
||||
u32 heap_fill_value = 0);
|
||||
ResultCode Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, Direction dir,
|
||||
u32 heap_fill_value = 0);
|
||||
|
||||
static constexpr std::size_t MaxManagerCount = 10;
|
||||
|
||||
@@ -84,26 +92,13 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
class Impl final : NonCopyable {
|
||||
private:
|
||||
using RefCount = u16;
|
||||
|
||||
private:
|
||||
KPageHeap heap;
|
||||
Pool pool{};
|
||||
|
||||
class Impl final {
|
||||
public:
|
||||
static std::size_t CalculateManagementOverheadSize(std::size_t region_size);
|
||||
YUZU_NON_COPYABLE(Impl);
|
||||
YUZU_NON_MOVEABLE(Impl);
|
||||
|
||||
static constexpr std::size_t CalculateOptimizedProcessOverheadSize(
|
||||
std::size_t region_size) {
|
||||
return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
|
||||
Common::BitSize<u64>()) *
|
||||
sizeof(u64);
|
||||
}
|
||||
|
||||
public:
|
||||
Impl() = default;
|
||||
~Impl() = default;
|
||||
|
||||
std::size_t Initialize(Pool new_pool, u64 start_address, u64 end_address);
|
||||
|
||||
@@ -126,9 +121,25 @@ private:
|
||||
constexpr VAddr GetEndAddress() const {
|
||||
return heap.GetEndAddress();
|
||||
}
|
||||
|
||||
static std::size_t CalculateManagementOverheadSize(std::size_t region_size);
|
||||
|
||||
static constexpr std::size_t CalculateOptimizedProcessOverheadSize(
|
||||
std::size_t region_size) {
|
||||
return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
|
||||
Common::BitSize<u64>()) *
|
||||
sizeof(u64);
|
||||
}
|
||||
|
||||
private:
|
||||
using RefCount = u16;
|
||||
|
||||
KPageHeap heap;
|
||||
Pool pool{};
|
||||
};
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
std::array<std::mutex, static_cast<std::size_t>(Pool::Count)> pool_locks;
|
||||
std::array<Impl, MaxManagerCount> managers;
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/intrusive_red_black_tree.h"
|
||||
#include "core/hle/kernel/k_memory_region_type.h"
|
||||
@@ -13,11 +14,13 @@ namespace Kernel {
|
||||
|
||||
class KMemoryRegionAllocator;
|
||||
|
||||
class KMemoryRegion final : public Common::IntrusiveRedBlackTreeBaseNode<KMemoryRegion>,
|
||||
NonCopyable {
|
||||
class KMemoryRegion final : public Common::IntrusiveRedBlackTreeBaseNode<KMemoryRegion> {
|
||||
friend class KMemoryRegionTree;
|
||||
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KMemoryRegion);
|
||||
YUZU_NON_MOVEABLE(KMemoryRegion);
|
||||
|
||||
constexpr KMemoryRegion() = default;
|
||||
constexpr KMemoryRegion(u64 address_, u64 last_address_)
|
||||
: address{address_}, last_address{last_address_} {}
|
||||
@@ -29,6 +32,8 @@ public:
|
||||
: KMemoryRegion(address_, last_address_, std::numeric_limits<u64>::max(), attributes_,
|
||||
type_id_) {}
|
||||
|
||||
~KMemoryRegion() = default;
|
||||
|
||||
static constexpr int Compare(const KMemoryRegion& lhs, const KMemoryRegion& rhs) {
|
||||
if (lhs.GetAddress() < rhs.GetAddress()) {
|
||||
return -1;
|
||||
@@ -39,16 +44,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr void Reset(u64 a, u64 la, u64 p, u32 r, u32 t) {
|
||||
address = a;
|
||||
pair_address = p;
|
||||
last_address = la;
|
||||
attributes = r;
|
||||
type_id = t;
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr u64 GetAddress() const {
|
||||
return address;
|
||||
}
|
||||
@@ -108,6 +103,14 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr void Reset(u64 a, u64 la, u64 p, u32 r, u32 t) {
|
||||
address = a;
|
||||
pair_address = p;
|
||||
last_address = la;
|
||||
attributes = r;
|
||||
type_id = t;
|
||||
}
|
||||
|
||||
u64 address{};
|
||||
u64 last_address{};
|
||||
u64 pair_address{};
|
||||
@@ -115,8 +118,25 @@ private:
|
||||
u32 type_id{};
|
||||
};
|
||||
|
||||
class KMemoryRegionTree final : NonCopyable {
|
||||
class KMemoryRegionTree final {
|
||||
private:
|
||||
using TreeType =
|
||||
Common::IntrusiveRedBlackTreeBaseTraits<KMemoryRegion>::TreeType<KMemoryRegion>;
|
||||
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KMemoryRegionTree);
|
||||
YUZU_NON_MOVEABLE(KMemoryRegionTree);
|
||||
|
||||
using value_type = TreeType::value_type;
|
||||
using size_type = TreeType::size_type;
|
||||
using difference_type = TreeType::difference_type;
|
||||
using pointer = TreeType::pointer;
|
||||
using const_pointer = TreeType::const_pointer;
|
||||
using reference = TreeType::reference;
|
||||
using const_reference = TreeType::const_reference;
|
||||
using iterator = TreeType::iterator;
|
||||
using const_iterator = TreeType::const_iterator;
|
||||
|
||||
struct DerivedRegionExtents {
|
||||
const KMemoryRegion* first_region{};
|
||||
const KMemoryRegion* last_region{};
|
||||
@@ -140,29 +160,9 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
using TreeType =
|
||||
Common::IntrusiveRedBlackTreeBaseTraits<KMemoryRegion>::TreeType<KMemoryRegion>;
|
||||
|
||||
public:
|
||||
using value_type = TreeType::value_type;
|
||||
using size_type = TreeType::size_type;
|
||||
using difference_type = TreeType::difference_type;
|
||||
using pointer = TreeType::pointer;
|
||||
using const_pointer = TreeType::const_pointer;
|
||||
using reference = TreeType::reference;
|
||||
using const_reference = TreeType::const_reference;
|
||||
using iterator = TreeType::iterator;
|
||||
using const_iterator = TreeType::const_iterator;
|
||||
|
||||
private:
|
||||
TreeType m_tree{};
|
||||
KMemoryRegionAllocator& memory_region_allocator;
|
||||
|
||||
public:
|
||||
explicit KMemoryRegionTree(KMemoryRegionAllocator& memory_region_allocator_);
|
||||
~KMemoryRegionTree() = default;
|
||||
|
||||
public:
|
||||
KMemoryRegion* FindModifiable(u64 address) {
|
||||
if (auto it = this->find(KMemoryRegion(address, address, 0, 0)); it != this->end()) {
|
||||
return std::addressof(*it);
|
||||
@@ -241,7 +241,6 @@ public:
|
||||
return GetDerivedRegionExtents(static_cast<KMemoryRegionType>(type_id));
|
||||
}
|
||||
|
||||
public:
|
||||
void InsertDirectly(u64 address, u64 last_address, u32 attr = 0, u32 type_id = 0);
|
||||
bool Insert(u64 address, size_t size, u32 type_id, u32 new_attr = 0, u32 old_attr = 0);
|
||||
|
||||
@@ -252,7 +251,6 @@ public:
|
||||
return this->GetRandomAlignedRegion(size + 2 * guard_size, alignment, type_id) + guard_size;
|
||||
}
|
||||
|
||||
public:
|
||||
// Iterator accessors.
|
||||
iterator begin() {
|
||||
return m_tree.begin();
|
||||
@@ -322,13 +320,21 @@ public:
|
||||
iterator nfind(const_reference ref) const {
|
||||
return m_tree.nfind(ref);
|
||||
}
|
||||
|
||||
private:
|
||||
TreeType m_tree{};
|
||||
KMemoryRegionAllocator& memory_region_allocator;
|
||||
};
|
||||
|
||||
class KMemoryRegionAllocator final : NonCopyable {
|
||||
class KMemoryRegionAllocator final {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KMemoryRegionAllocator);
|
||||
YUZU_NON_MOVEABLE(KMemoryRegionAllocator);
|
||||
|
||||
static constexpr size_t MaxMemoryRegions = 200;
|
||||
|
||||
constexpr KMemoryRegionAllocator() = default;
|
||||
constexpr ~KMemoryRegionAllocator() = default;
|
||||
|
||||
template <typename... Args>
|
||||
KMemoryRegion* Allocate(Args&&... args) {
|
||||
|
||||
@@ -8,14 +8,44 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/k_page_bitmap.h"
|
||||
#include "core/hle/kernel/memory_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KPageHeap final : NonCopyable {
|
||||
class KPageHeap final {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KPageHeap);
|
||||
YUZU_NON_MOVEABLE(KPageHeap);
|
||||
|
||||
KPageHeap() = default;
|
||||
~KPageHeap() = default;
|
||||
|
||||
constexpr VAddr GetAddress() const {
|
||||
return heap_address;
|
||||
}
|
||||
constexpr std::size_t GetSize() const {
|
||||
return heap_size;
|
||||
}
|
||||
constexpr VAddr GetEndAddress() const {
|
||||
return GetAddress() + GetSize();
|
||||
}
|
||||
constexpr std::size_t GetPageOffset(VAddr block) const {
|
||||
return (block - GetAddress()) / PageSize;
|
||||
}
|
||||
|
||||
void Initialize(VAddr heap_address, std::size_t heap_size, std::size_t metadata_size);
|
||||
VAddr AllocateBlock(s32 index, bool random);
|
||||
void Free(VAddr addr, std::size_t num_pages);
|
||||
|
||||
void UpdateUsedSize() {
|
||||
used_size = heap_size - (GetNumFreePages() * PageSize);
|
||||
}
|
||||
|
||||
static std::size_t CalculateManagementOverheadSize(std::size_t region_size);
|
||||
|
||||
static constexpr s32 GetAlignedBlockIndex(std::size_t num_pages, std::size_t align_pages) {
|
||||
const auto target_pages{std::max(num_pages, align_pages)};
|
||||
for (std::size_t i = 0; i < NumMemoryBlockPageShifts; i++) {
|
||||
@@ -45,21 +75,13 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr std::size_t NumMemoryBlockPageShifts{7};
|
||||
static constexpr std::array<std::size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{
|
||||
0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E,
|
||||
};
|
||||
|
||||
class Block final : NonCopyable {
|
||||
private:
|
||||
KPageBitmap bitmap;
|
||||
VAddr heap_address{};
|
||||
uintptr_t end_offset{};
|
||||
std::size_t block_shift{};
|
||||
std::size_t next_block_shift{};
|
||||
|
||||
class Block final {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(Block);
|
||||
YUZU_NON_MOVEABLE(Block);
|
||||
|
||||
Block() = default;
|
||||
~Block() = default;
|
||||
|
||||
constexpr std::size_t GetShift() const {
|
||||
return block_shift;
|
||||
@@ -129,7 +151,6 @@ private:
|
||||
return heap_address + (offset << GetShift());
|
||||
}
|
||||
|
||||
public:
|
||||
static constexpr std::size_t CalculateManagementOverheadSize(std::size_t region_size,
|
||||
std::size_t cur_block_shift,
|
||||
std::size_t next_block_shift) {
|
||||
@@ -139,35 +160,15 @@ private:
|
||||
return KPageBitmap::CalculateManagementOverheadSize(
|
||||
(align * 2 + Common::AlignUp(region_size, align)) / cur_block_size);
|
||||
}
|
||||
|
||||
private:
|
||||
KPageBitmap bitmap;
|
||||
VAddr heap_address{};
|
||||
uintptr_t end_offset{};
|
||||
std::size_t block_shift{};
|
||||
std::size_t next_block_shift{};
|
||||
};
|
||||
|
||||
public:
|
||||
KPageHeap() = default;
|
||||
|
||||
constexpr VAddr GetAddress() const {
|
||||
return heap_address;
|
||||
}
|
||||
constexpr std::size_t GetSize() const {
|
||||
return heap_size;
|
||||
}
|
||||
constexpr VAddr GetEndAddress() const {
|
||||
return GetAddress() + GetSize();
|
||||
}
|
||||
constexpr std::size_t GetPageOffset(VAddr block) const {
|
||||
return (block - GetAddress()) / PageSize;
|
||||
}
|
||||
|
||||
void Initialize(VAddr heap_address, std::size_t heap_size, std::size_t metadata_size);
|
||||
VAddr AllocateBlock(s32 index, bool random);
|
||||
void Free(VAddr addr, std::size_t num_pages);
|
||||
|
||||
void UpdateUsedSize() {
|
||||
used_size = heap_size - (GetNumFreePages() * PageSize);
|
||||
}
|
||||
|
||||
static std::size_t CalculateManagementOverheadSize(std::size_t region_size);
|
||||
|
||||
private:
|
||||
constexpr std::size_t GetNumFreePages() const {
|
||||
std::size_t num_free{};
|
||||
|
||||
@@ -180,6 +181,11 @@ private:
|
||||
|
||||
void FreeBlock(VAddr block, s32 index);
|
||||
|
||||
static constexpr std::size_t NumMemoryBlockPageShifts{7};
|
||||
static constexpr std::array<std::size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{
|
||||
0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E,
|
||||
};
|
||||
|
||||
VAddr heap_address{};
|
||||
std::size_t heap_size{};
|
||||
std::size_t used_size{};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,11 +5,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/page_table.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/hle/kernel/k_light_lock.h"
|
||||
#include "core/hle/kernel/k_memory_block.h"
|
||||
#include "core/hle/kernel/k_memory_manager.h"
|
||||
#include "core/hle/result.h"
|
||||
@@ -22,35 +23,39 @@ namespace Kernel {
|
||||
|
||||
class KMemoryBlockManager;
|
||||
|
||||
class KPageTable final : NonCopyable {
|
||||
class KPageTable final {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KPageTable);
|
||||
YUZU_NON_MOVEABLE(KPageTable);
|
||||
|
||||
explicit KPageTable(Core::System& system_);
|
||||
~KPageTable();
|
||||
|
||||
ResultCode InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
|
||||
VAddr code_addr, std::size_t code_size,
|
||||
KMemoryManager::Pool pool);
|
||||
ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state,
|
||||
KMemoryPermission perm);
|
||||
ResultCode MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
|
||||
ResultCode UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
|
||||
ResultCode MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
|
||||
ResultCode UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
|
||||
ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table,
|
||||
VAddr src_addr);
|
||||
ResultCode MapPhysicalMemory(VAddr addr, std::size_t size);
|
||||
ResultCode UnmapPhysicalMemory(VAddr addr, std::size_t size);
|
||||
ResultCode UnmapMemory(VAddr addr, std::size_t size);
|
||||
ResultCode Map(VAddr dst_addr, VAddr src_addr, std::size_t size);
|
||||
ResultCode Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size);
|
||||
ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
|
||||
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
|
||||
ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
|
||||
KMemoryPermission perm);
|
||||
ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state);
|
||||
ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size, KMemoryPermission perm);
|
||||
ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size,
|
||||
Svc::MemoryPermission svc_perm);
|
||||
KMemoryInfo QueryInfo(VAddr addr);
|
||||
ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm);
|
||||
ResultCode ResetTransferMemory(VAddr addr, std::size_t size);
|
||||
ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryAttribute mask,
|
||||
KMemoryAttribute value);
|
||||
ResultCode SetHeapCapacity(std::size_t new_heap_capacity);
|
||||
ResultVal<VAddr> SetHeapSize(std::size_t size);
|
||||
ResultCode SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm);
|
||||
ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr);
|
||||
ResultCode SetMaxHeapSize(std::size_t size);
|
||||
ResultCode SetHeapSize(VAddr* out, std::size_t size);
|
||||
ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align,
|
||||
bool is_map_only, VAddr region_start,
|
||||
std::size_t region_num_pages, KMemoryState state,
|
||||
@@ -87,7 +92,6 @@ private:
|
||||
ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
|
||||
KMemoryPermission perm);
|
||||
ResultCode UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list);
|
||||
void MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end);
|
||||
bool IsRegionMapped(VAddr address, u64 size);
|
||||
bool IsRegionContiguous(VAddr addr, u64 size) const;
|
||||
void AddRegionToPages(VAddr start, std::size_t num_pages, KPageLinkedList& page_linked_list);
|
||||
@@ -101,30 +105,54 @@ private:
|
||||
constexpr VAddr GetRegionAddress(KMemoryState state) const;
|
||||
constexpr std::size_t GetRegionSize(KMemoryState state) const;
|
||||
|
||||
constexpr ResultCode CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
|
||||
ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
|
||||
std::size_t size, KMemoryState state_mask,
|
||||
KMemoryState state, KMemoryPermission perm_mask,
|
||||
KMemoryPermission perm, KMemoryAttribute attr_mask,
|
||||
KMemoryAttribute attr) const;
|
||||
ResultCode CheckMemoryStateContiguous(VAddr addr, std::size_t size, KMemoryState state_mask,
|
||||
KMemoryState state, KMemoryPermission perm_mask,
|
||||
KMemoryPermission perm, KMemoryAttribute attr_mask,
|
||||
KMemoryAttribute attr) const {
|
||||
return this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask,
|
||||
perm, attr_mask, attr);
|
||||
}
|
||||
|
||||
ResultCode CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
|
||||
KMemoryState state, KMemoryPermission perm_mask,
|
||||
KMemoryPermission perm, KMemoryAttribute attr_mask,
|
||||
KMemoryAttribute attr) const;
|
||||
ResultCode CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
|
||||
KMemoryAttribute* out_attr, VAddr addr, std::size_t size,
|
||||
KMemoryState state_mask, KMemoryState state,
|
||||
KMemoryPermission perm_mask, KMemoryPermission perm,
|
||||
KMemoryAttribute attr_mask, KMemoryAttribute attr,
|
||||
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr);
|
||||
ResultCode CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask,
|
||||
KMemoryAttribute* out_attr, std::size_t* out_blocks_needed,
|
||||
VAddr addr, std::size_t size, KMemoryState state_mask,
|
||||
KMemoryState state, KMemoryPermission perm_mask,
|
||||
KMemoryPermission perm, KMemoryAttribute attr_mask,
|
||||
KMemoryAttribute attr,
|
||||
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) {
|
||||
return CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask,
|
||||
perm, attr_mask, attr, ignore_attr);
|
||||
}
|
||||
ResultCode CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size,
|
||||
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
|
||||
ResultCode CheckMemoryState(std::size_t* out_blocks_needed, VAddr addr, std::size_t size,
|
||||
KMemoryState state_mask, KMemoryState state,
|
||||
KMemoryPermission perm_mask, KMemoryPermission perm,
|
||||
KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
|
||||
KMemoryAttribute attr_mask, KMemoryAttribute attr,
|
||||
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
|
||||
return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
|
||||
state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr);
|
||||
}
|
||||
ResultCode CheckMemoryState(VAddr addr, size_t size, KMemoryState state_mask,
|
||||
KMemoryState state, KMemoryPermission perm_mask,
|
||||
KMemoryPermission perm, KMemoryAttribute attr_mask,
|
||||
KMemoryAttribute attr,
|
||||
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
|
||||
return this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm,
|
||||
attr_mask, attr, ignore_attr);
|
||||
}
|
||||
|
||||
bool IsLockedByCurrentThread() const {
|
||||
return general_lock.IsLockedByCurrentThread();
|
||||
}
|
||||
|
||||
mutable KLightLock general_lock;
|
||||
mutable KLightLock map_physical_memory_lock;
|
||||
|
||||
std::recursive_mutex page_table_lock;
|
||||
std::unique_ptr<KMemoryBlockManager> block_manager;
|
||||
|
||||
public:
|
||||
@@ -182,14 +210,15 @@ public:
|
||||
constexpr VAddr GetAliasCodeRegionSize() const {
|
||||
return alias_code_region_end - alias_code_region_start;
|
||||
}
|
||||
size_t GetNormalMemorySize() {
|
||||
KScopedLightLock lk(general_lock);
|
||||
return GetHeapSize() + mapped_physical_memory_size;
|
||||
}
|
||||
constexpr std::size_t GetAddressSpaceWidth() const {
|
||||
return address_space_width;
|
||||
}
|
||||
constexpr std::size_t GetHeapSize() {
|
||||
return current_heap_addr - heap_region_start;
|
||||
}
|
||||
constexpr std::size_t GetTotalHeapSize() {
|
||||
return GetHeapSize() + physical_memory_usage;
|
||||
constexpr std::size_t GetHeapSize() const {
|
||||
return current_heap_end - heap_region_start;
|
||||
}
|
||||
constexpr bool IsInsideAddressSpace(VAddr address, std::size_t size) const {
|
||||
return address_space_start <= address && address + size - 1 <= address_space_end - 1;
|
||||
@@ -224,8 +253,12 @@ public:
|
||||
constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const {
|
||||
return !IsOutsideASLRRegion(address, size);
|
||||
}
|
||||
constexpr PAddr GetPhysicalAddr(VAddr addr) {
|
||||
return page_table_impl.backing_addr[addr >> PageBits] + addr;
|
||||
|
||||
PAddr GetPhysicalAddr(VAddr addr) {
|
||||
ASSERT(IsLockedByCurrentThread());
|
||||
const auto backing_addr = page_table_impl.backing_addr[addr >> PageBits];
|
||||
ASSERT(backing_addr);
|
||||
return backing_addr + addr;
|
||||
}
|
||||
constexpr bool Contains(VAddr addr) const {
|
||||
return address_space_start <= addr && addr <= address_space_end - 1;
|
||||
@@ -269,10 +302,8 @@ private:
|
||||
VAddr code_region_end{};
|
||||
VAddr alias_code_region_start{};
|
||||
VAddr alias_code_region_end{};
|
||||
VAddr current_heap_addr{};
|
||||
|
||||
std::size_t heap_capacity{};
|
||||
std::size_t physical_memory_usage{};
|
||||
std::size_t mapped_physical_memory_size{};
|
||||
std::size_t max_heap_size{};
|
||||
std::size_t max_physical_memory_size{};
|
||||
std::size_t address_space_width{};
|
||||
@@ -281,6 +312,7 @@ private:
|
||||
bool is_aslr_enabled{};
|
||||
|
||||
KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application};
|
||||
KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront};
|
||||
|
||||
Common::PageTable page_table_impl;
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ concept KPriorityQueueMember = !std::is_reference_v<T> && requires(T & t) {
|
||||
|
||||
{ t.GetActiveCore() } -> Common::ConvertibleTo<s32>;
|
||||
{ t.GetPriority() } -> Common::ConvertibleTo<s32>;
|
||||
{ t.IsDummyThread() } -> Common::ConvertibleTo<bool>;
|
||||
};
|
||||
|
||||
template <typename Member, size_t NumCores_, int LowestPriority, int HighestPriority>
|
||||
@@ -257,7 +258,7 @@ private:
|
||||
|
||||
private:
|
||||
constexpr void ClearAffinityBit(u64& affinity, s32 core) {
|
||||
affinity &= ~(u64(1) << core);
|
||||
affinity &= ~(UINT64_C(1) << core);
|
||||
}
|
||||
|
||||
constexpr s32 GetNextCore(u64& affinity) {
|
||||
@@ -349,24 +350,49 @@ public:
|
||||
|
||||
// Mutators.
|
||||
constexpr void PushBack(Member* member) {
|
||||
// This is for host (dummy) threads that we do not want to enter the priority queue.
|
||||
if (member->IsDummyThread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->PushBack(member->GetPriority(), member);
|
||||
}
|
||||
|
||||
constexpr void Remove(Member* member) {
|
||||
// This is for host (dummy) threads that we do not want to enter the priority queue.
|
||||
if (member->IsDummyThread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->Remove(member->GetPriority(), member);
|
||||
}
|
||||
|
||||
constexpr void MoveToScheduledFront(Member* member) {
|
||||
// This is for host (dummy) threads that we do not want to enter the priority queue.
|
||||
if (member->IsDummyThread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->scheduled_queue.MoveToFront(member->GetPriority(), member->GetActiveCore(), member);
|
||||
}
|
||||
|
||||
constexpr KThread* MoveToScheduledBack(Member* member) {
|
||||
// This is for host (dummy) threads that we do not want to enter the priority queue.
|
||||
if (member->IsDummyThread()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return this->scheduled_queue.MoveToBack(member->GetPriority(), member->GetActiveCore(),
|
||||
member);
|
||||
}
|
||||
|
||||
// First class fancy operations.
|
||||
constexpr void ChangePriority(s32 prev_priority, bool is_running, Member* member) {
|
||||
// This is for host (dummy) threads that we do not want to enter the priority queue.
|
||||
if (member->IsDummyThread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(IsValidPriority(prev_priority));
|
||||
|
||||
// Remove the member from the queues.
|
||||
@@ -383,6 +409,11 @@ public:
|
||||
|
||||
constexpr void ChangeAffinityMask(s32 prev_core, const AffinityMaskType& prev_affinity,
|
||||
Member* member) {
|
||||
// This is for host (dummy) threads that we do not want to enter the priority queue.
|
||||
if (member->IsDummyThread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the new information.
|
||||
const s32 priority = member->GetPriority();
|
||||
const AffinityMaskType& new_affinity = member->GetAffinityMask();
|
||||
@@ -412,6 +443,11 @@ public:
|
||||
}
|
||||
|
||||
constexpr void ChangeCore(s32 prev_core, Member* member, bool to_front = false) {
|
||||
// This is for host (dummy) threads that we do not want to enter the priority queue.
|
||||
if (member->IsDummyThread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the new information.
|
||||
const s32 new_core = member->GetActiveCore();
|
||||
const s32 priority = member->GetPriority();
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
@@ -147,33 +146,41 @@ ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::st
|
||||
// Open a reference to the resource limit.
|
||||
process->resource_limit->Open();
|
||||
|
||||
// Clear remaining fields.
|
||||
process->num_running_threads = 0;
|
||||
process->is_signaled = false;
|
||||
process->exception_thread = nullptr;
|
||||
process->is_suspended = false;
|
||||
process->schedule_count = 0;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void KProcess::DoWorkerTaskImpl() {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
KResourceLimit* KProcess::GetResourceLimit() const {
|
||||
return resource_limit;
|
||||
}
|
||||
|
||||
void KProcess::IncrementThreadCount() {
|
||||
ASSERT(num_threads >= 0);
|
||||
num_created_threads++;
|
||||
|
||||
if (const auto count = ++num_threads; count > peak_num_threads) {
|
||||
peak_num_threads = count;
|
||||
}
|
||||
void KProcess::IncrementRunningThreadCount() {
|
||||
ASSERT(num_running_threads.load() >= 0);
|
||||
++num_running_threads;
|
||||
}
|
||||
|
||||
void KProcess::DecrementThreadCount() {
|
||||
ASSERT(num_threads > 0);
|
||||
void KProcess::DecrementRunningThreadCount() {
|
||||
ASSERT(num_running_threads.load() > 0);
|
||||
|
||||
if (const auto count = --num_threads; count == 0) {
|
||||
LOG_WARNING(Kernel, "Process termination is not fully implemented.");
|
||||
if (const auto prev = num_running_threads--; prev == 1) {
|
||||
// TODO(bunnei): Process termination to be implemented when multiprocess is supported.
|
||||
UNIMPLEMENTED_MSG("KProcess termination is not implemennted!");
|
||||
}
|
||||
}
|
||||
|
||||
u64 KProcess::GetTotalPhysicalMemoryAvailable() const {
|
||||
const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) +
|
||||
page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size +
|
||||
page_table->GetNormalMemorySize() + GetSystemResourceSize() + image_size +
|
||||
main_thread_stack_size};
|
||||
if (const auto pool_size = kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application);
|
||||
capacity != pool_size) {
|
||||
@@ -190,7 +197,7 @@ u64 KProcess::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const {
|
||||
}
|
||||
|
||||
u64 KProcess::GetTotalPhysicalMemoryUsed() const {
|
||||
return image_size + main_thread_stack_size + page_table->GetTotalHeapSize() +
|
||||
return image_size + main_thread_stack_size + page_table->GetNormalMemorySize() +
|
||||
GetSystemResourceSize();
|
||||
}
|
||||
|
||||
@@ -221,30 +228,28 @@ bool KProcess::ReleaseUserException(KThread* thread) {
|
||||
}
|
||||
}
|
||||
|
||||
void KProcess::PinCurrentThread() {
|
||||
void KProcess::PinCurrentThread(s32 core_id) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
// Get the current thread.
|
||||
const s32 core_id = GetCurrentCoreId(kernel);
|
||||
KThread* cur_thread = GetCurrentThreadPointer(kernel);
|
||||
KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread();
|
||||
|
||||
// If the thread isn't terminated, pin it.
|
||||
if (!cur_thread->IsTerminationRequested()) {
|
||||
// Pin it.
|
||||
PinThread(core_id, cur_thread);
|
||||
cur_thread->Pin();
|
||||
cur_thread->Pin(core_id);
|
||||
|
||||
// An update is needed.
|
||||
KScheduler::SetSchedulerUpdateNeeded(kernel);
|
||||
}
|
||||
}
|
||||
|
||||
void KProcess::UnpinCurrentThread() {
|
||||
void KProcess::UnpinCurrentThread(s32 core_id) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
// Get the current thread.
|
||||
const s32 core_id = GetCurrentCoreId(kernel);
|
||||
KThread* cur_thread = GetCurrentThreadPointer(kernel);
|
||||
KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread();
|
||||
|
||||
// Unpin it.
|
||||
cur_thread->Unpin();
|
||||
@@ -411,8 +416,8 @@ void KProcess::Run(s32 main_thread_priority, u64 stack_size) {
|
||||
resource_limit->Reserve(LimitableResource::Threads, 1);
|
||||
resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size);
|
||||
|
||||
const std::size_t heap_capacity{memory_usage_capacity - main_thread_stack_size - image_size};
|
||||
ASSERT(!page_table->SetHeapCapacity(heap_capacity).IsError());
|
||||
const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)};
|
||||
ASSERT(!page_table->SetMaxHeapSize(heap_capacity).IsError());
|
||||
|
||||
ChangeStatus(ProcessStatus::Running);
|
||||
|
||||
@@ -480,7 +485,7 @@ void KProcess::Finalize() {
|
||||
}
|
||||
|
||||
// Perform inherited finalization.
|
||||
KAutoObjectWithSlabHeapAndContainer<KProcess, KSynchronizationObject>::Finalize();
|
||||
KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -512,7 +517,7 @@ VAddr KProcess::CreateTLSRegion() {
|
||||
const VAddr tls_page_addr{page_table
|
||||
->AllocateAndMapMemory(1, PageSize, true, start, size / PageSize,
|
||||
KMemoryState::ThreadLocal,
|
||||
KMemoryPermission::ReadAndWrite,
|
||||
KMemoryPermission::UserReadWrite,
|
||||
tls_map_addr)
|
||||
.ValueOr(0)};
|
||||
|
||||
@@ -543,18 +548,17 @@ void KProcess::FreeTLSRegion(VAddr tls_address) {
|
||||
}
|
||||
|
||||
void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) {
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
const auto ReprotectSegment = [&](const CodeSet::Segment& segment,
|
||||
KMemoryPermission permission) {
|
||||
Svc::MemoryPermission permission) {
|
||||
page_table->SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission);
|
||||
};
|
||||
|
||||
kernel.System().Memory().WriteBlock(*this, base_addr, code_set.memory.data(),
|
||||
code_set.memory.size());
|
||||
|
||||
ReprotectSegment(code_set.CodeSegment(), KMemoryPermission::ReadAndExecute);
|
||||
ReprotectSegment(code_set.RODataSegment(), KMemoryPermission::Read);
|
||||
ReprotectSegment(code_set.DataSegment(), KMemoryPermission::ReadAndWrite);
|
||||
ReprotectSegment(code_set.CodeSegment(), Svc::MemoryPermission::ReadExecute);
|
||||
ReprotectSegment(code_set.RODataSegment(), Svc::MemoryPermission::Read);
|
||||
ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite);
|
||||
}
|
||||
|
||||
bool KProcess::IsSignaled() const {
|
||||
@@ -591,7 +595,7 @@ ResultCode KProcess::AllocateMainThreadStack(std::size_t stack_size) {
|
||||
CASCADE_RESULT(main_thread_stack_top,
|
||||
page_table->AllocateAndMapMemory(
|
||||
main_thread_stack_size / PageSize, PageSize, false, start, size / PageSize,
|
||||
KMemoryState::Stack, KMemoryPermission::ReadAndWrite));
|
||||
KMemoryState::Stack, KMemoryPermission::UserReadWrite));
|
||||
|
||||
main_thread_stack_top += main_thread_stack_size;
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "core/hle/kernel/k_condition_variable.h"
|
||||
#include "core/hle/kernel/k_handle_table.h"
|
||||
#include "core/hle/kernel/k_synchronization_object.h"
|
||||
#include "core/hle/kernel/k_worker_task.h"
|
||||
#include "core/hle/kernel/process_capability.h"
|
||||
#include "core/hle/kernel/slab_helpers.h"
|
||||
#include "core/hle/result.h"
|
||||
@@ -62,8 +63,7 @@ enum class ProcessStatus {
|
||||
DebugBreak,
|
||||
};
|
||||
|
||||
class KProcess final
|
||||
: public KAutoObjectWithSlabHeapAndContainer<KProcess, KSynchronizationObject> {
|
||||
class KProcess final : public KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask> {
|
||||
KERNEL_AUTOOBJECT_TRAITS(KProcess, KSynchronizationObject);
|
||||
|
||||
public:
|
||||
@@ -235,8 +235,8 @@ public:
|
||||
++schedule_count;
|
||||
}
|
||||
|
||||
void IncrementThreadCount();
|
||||
void DecrementThreadCount();
|
||||
void IncrementRunningThreadCount();
|
||||
void DecrementRunningThreadCount();
|
||||
|
||||
void SetRunningThread(s32 core, KThread* thread, u64 idle_count) {
|
||||
running_threads[core] = thread;
|
||||
@@ -345,8 +345,10 @@ public:
|
||||
|
||||
bool IsSignaled() const override;
|
||||
|
||||
void PinCurrentThread();
|
||||
void UnpinCurrentThread();
|
||||
void DoWorkerTaskImpl();
|
||||
|
||||
void PinCurrentThread(s32 core_id);
|
||||
void UnpinCurrentThread(s32 core_id);
|
||||
void UnpinThread(KThread* thread);
|
||||
|
||||
KLightLock& GetStateLock() {
|
||||
@@ -471,9 +473,7 @@ private:
|
||||
bool is_suspended{};
|
||||
bool is_initialized{};
|
||||
|
||||
std::atomic<s32> num_created_threads{};
|
||||
std::atomic<u16> num_threads{};
|
||||
u16 peak_num_threads{};
|
||||
std::atomic<u16> num_running_threads{};
|
||||
|
||||
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> running_threads{};
|
||||
std::array<u64, Core::Hardware::NUM_CPU_CORES> running_thread_idle_counts{};
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/cpu_manager.h"
|
||||
#include "core/hle/kernel/k_interrupt_manager.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
|
||||
@@ -48,11 +49,16 @@ void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedul
|
||||
if (!must_context_switch || core != current_core) {
|
||||
auto& phys_core = kernel.PhysicalCore(core);
|
||||
phys_core.Interrupt();
|
||||
} else {
|
||||
must_context_switch = true;
|
||||
}
|
||||
cores_pending_reschedule &= ~(1ULL << core);
|
||||
}
|
||||
|
||||
for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; ++core_id) {
|
||||
if (kernel.PhysicalCore(core_id).IsInterrupted()) {
|
||||
KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_id));
|
||||
}
|
||||
}
|
||||
|
||||
if (must_context_switch) {
|
||||
auto core_scheduler = kernel.CurrentScheduler();
|
||||
kernel.ExitSVCProfile();
|
||||
@@ -400,6 +406,9 @@ void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduli
|
||||
} else {
|
||||
RescheduleCores(kernel, cores_needing_scheduling);
|
||||
}
|
||||
|
||||
// Special case to ensure dummy threads that are waiting block.
|
||||
current_thread->IfDummyThreadTryWait();
|
||||
}
|
||||
|
||||
u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
|
||||
@@ -701,23 +710,19 @@ void KScheduler::Unload(KThread* thread) {
|
||||
}
|
||||
|
||||
void KScheduler::Reload(KThread* thread) {
|
||||
LOG_TRACE(Kernel, "core {}, reload thread {}", core_id, thread ? thread->GetName() : "nullptr");
|
||||
LOG_TRACE(Kernel, "core {}, reload thread {}", core_id, thread->GetName());
|
||||
|
||||
if (thread) {
|
||||
ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable.");
|
||||
|
||||
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
|
||||
cpu_core.LoadContext(thread->GetContext32());
|
||||
cpu_core.LoadContext(thread->GetContext64());
|
||||
cpu_core.SetTlsAddress(thread->GetTLSAddress());
|
||||
cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
|
||||
cpu_core.ClearExclusiveState();
|
||||
}
|
||||
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
|
||||
cpu_core.LoadContext(thread->GetContext32());
|
||||
cpu_core.LoadContext(thread->GetContext64());
|
||||
cpu_core.SetTlsAddress(thread->GetTLSAddress());
|
||||
cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
|
||||
cpu_core.ClearExclusiveState();
|
||||
}
|
||||
|
||||
void KScheduler::SwitchContextStep2() {
|
||||
// Load context of new thread
|
||||
Reload(current_thread.load());
|
||||
Reload(GetCurrentThread());
|
||||
|
||||
RescheduleCurrentCore();
|
||||
}
|
||||
@@ -726,13 +731,23 @@ void KScheduler::ScheduleImpl() {
|
||||
KThread* previous_thread = GetCurrentThread();
|
||||
KThread* next_thread = state.highest_priority_thread;
|
||||
|
||||
state.needs_scheduling = false;
|
||||
state.needs_scheduling.store(false);
|
||||
|
||||
// We never want to schedule a null thread, so use the idle thread if we don't have a next.
|
||||
if (next_thread == nullptr) {
|
||||
next_thread = idle_thread;
|
||||
}
|
||||
|
||||
if (next_thread->GetCurrentCore() != core_id) {
|
||||
next_thread->SetCurrentCore(core_id);
|
||||
}
|
||||
|
||||
// We never want to schedule a dummy thread, as these are only used by host threads for locking.
|
||||
if (next_thread->GetThreadType() == ThreadType::Dummy) {
|
||||
ASSERT_MSG(false, "Dummy threads should never be scheduled!");
|
||||
next_thread = idle_thread;
|
||||
}
|
||||
|
||||
// If we're not actually switching thread, there's nothing to do.
|
||||
if (next_thread == current_thread.load()) {
|
||||
previous_thread->EnableDispatch();
|
||||
@@ -740,14 +755,8 @@ void KScheduler::ScheduleImpl() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (next_thread->GetCurrentCore() != core_id) {
|
||||
next_thread->SetCurrentCore(core_id);
|
||||
}
|
||||
|
||||
current_thread.store(next_thread);
|
||||
|
||||
// Update the CPU time tracking variables.
|
||||
KProcess* const previous_process = system.Kernel().CurrentProcess();
|
||||
|
||||
UpdateLastContextSwitchTime(previous_thread, previous_process);
|
||||
|
||||
// Save context for previous thread
|
||||
@@ -755,6 +764,10 @@ void KScheduler::ScheduleImpl() {
|
||||
|
||||
std::shared_ptr<Common::Fiber>* old_context;
|
||||
old_context = &previous_thread->GetHostContext();
|
||||
|
||||
// Set the new thread.
|
||||
current_thread.store(next_thread);
|
||||
|
||||
guard.Unlock();
|
||||
|
||||
Common::Fiber::YieldTo(*old_context, *switch_fiber);
|
||||
@@ -782,8 +795,8 @@ void KScheduler::SwitchToCurrent() {
|
||||
do {
|
||||
auto next_thread = current_thread.load();
|
||||
if (next_thread != nullptr) {
|
||||
next_thread->context_guard.Lock();
|
||||
if (next_thread->GetRawState() != ThreadState::Runnable) {
|
||||
const auto locked = next_thread->context_guard.TryLock();
|
||||
if (state.needs_scheduling.load()) {
|
||||
next_thread->context_guard.Unlock();
|
||||
break;
|
||||
}
|
||||
@@ -791,6 +804,9 @@ void KScheduler::SwitchToCurrent() {
|
||||
next_thread->context_guard.Unlock();
|
||||
break;
|
||||
}
|
||||
if (!locked) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
auto thread = next_thread ? next_thread : idle_thread;
|
||||
Common::Fiber::YieldTo(switch_fiber, *thread->GetHostContext());
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
@@ -123,20 +122,10 @@ ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memor
|
||||
|
||||
context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
|
||||
|
||||
// In the event that something fails here, stub a result to prevent the game from crashing.
|
||||
// This is a work-around in the event that somehow we process a service request after the
|
||||
// session has been closed by the game. This has been observed to happen rarely in Pokemon
|
||||
// Sword/Shield and is likely a result of us using host threads/scheduling for services.
|
||||
// TODO(bunnei): Find a better solution here.
|
||||
auto error_guard = SCOPE_GUARD({ CompleteSyncRequest(*context); });
|
||||
|
||||
// Ensure we have a session request handler
|
||||
if (manager->HasSessionRequestHandler(*context)) {
|
||||
if (auto strong_ptr = manager->GetServiceThread().lock()) {
|
||||
strong_ptr->QueueSyncRequest(*parent, std::move(context));
|
||||
|
||||
// We succeeded.
|
||||
error_guard.Cancel();
|
||||
} else {
|
||||
ASSERT_MSG(false, "strong_ptr is nullptr!");
|
||||
}
|
||||
@@ -171,13 +160,8 @@ ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) {
|
||||
convert_to_domain = false;
|
||||
}
|
||||
|
||||
// Some service requests require the thread to block
|
||||
{
|
||||
KScopedSchedulerLock lock(kernel);
|
||||
if (!context.IsThreadWaiting()) {
|
||||
context.GetThread().EndWait(result);
|
||||
}
|
||||
}
|
||||
// The calling thread is waiting for this request to complete, so wake it up.
|
||||
context.GetThread().EndWait(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <atomic>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
@@ -15,13 +16,17 @@ class KernelCore;
|
||||
|
||||
namespace impl {
|
||||
|
||||
class KSlabHeapImpl final : NonCopyable {
|
||||
class KSlabHeapImpl final {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KSlabHeapImpl);
|
||||
YUZU_NON_MOVEABLE(KSlabHeapImpl);
|
||||
|
||||
struct Node {
|
||||
Node* next{};
|
||||
};
|
||||
|
||||
constexpr KSlabHeapImpl() = default;
|
||||
constexpr ~KSlabHeapImpl() = default;
|
||||
|
||||
void Initialize(std::size_t size) {
|
||||
ASSERT(head == nullptr);
|
||||
@@ -64,9 +69,13 @@ private:
|
||||
|
||||
} // namespace impl
|
||||
|
||||
class KSlabHeapBase : NonCopyable {
|
||||
class KSlabHeapBase {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KSlabHeapBase);
|
||||
YUZU_NON_MOVEABLE(KSlabHeapBase);
|
||||
|
||||
constexpr KSlabHeapBase() = default;
|
||||
constexpr ~KSlabHeapBase() = default;
|
||||
|
||||
constexpr bool Contains(uintptr_t addr) const {
|
||||
return start <= addr && addr < end;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user