Compare commits
182 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba3af04da1 | ||
|
|
83ac715e76 | ||
|
|
03da34b330 | ||
|
|
9a06b85b24 | ||
|
|
e704da9192 | ||
|
|
3870ba670f | ||
|
|
8a83e3412a | ||
|
|
bb18a6533d | ||
|
|
0b172d12c0 | ||
|
|
abb0124b84 | ||
|
|
c5b517aa5f | ||
|
|
973bf306ed | ||
|
|
92942fe01b | ||
|
|
74790e4f33 | ||
|
|
0f48292de1 | ||
|
|
78651b5476 | ||
|
|
5fc8393125 | ||
|
|
f9bfeaa2bc | ||
|
|
b2955479e5 | ||
|
|
c4ff7ecf51 | ||
|
|
f3caf53648 | ||
|
|
422a15ee75 | ||
|
|
3d89398b84 | ||
|
|
c8f86edee6 | ||
|
|
8d4dfc98ec | ||
|
|
de12dbb01a | ||
|
|
a4454329c1 | ||
|
|
391e823c79 | ||
|
|
8150c65c07 | ||
|
|
a98b6c8f07 | ||
|
|
56afd4ab4b | ||
|
|
c11b4c45e1 | ||
|
|
c978f3144c | ||
|
|
8b5655a98e | ||
|
|
2817ef1a53 | ||
|
|
58180f9fa8 | ||
|
|
827483409b | ||
|
|
9951322e5a | ||
|
|
fa2aac1bf5 | ||
|
|
0c0c1a039e | ||
|
|
7f85abb281 | ||
|
|
f332d4a9b5 | ||
|
|
588ab44470 | ||
|
|
7b0d8bd1fb | ||
|
|
ee67460ff0 | ||
|
|
5ba28325b2 | ||
|
|
c4609c92ee | ||
|
|
621f3f5f47 | ||
|
|
740edacc8d | ||
|
|
5105318bbc | ||
|
|
a7837a3791 | ||
|
|
fbb170857f | ||
|
|
f738c6b231 | ||
|
|
c1b8e59ea0 | ||
|
|
46ec0ee55b | ||
|
|
ebd38d66db | ||
|
|
aa79ca7a7a | ||
|
|
4547b2735a | ||
|
|
6755025310 | ||
|
|
781c85b951 | ||
|
|
fa8a0065ca | ||
|
|
74f0087bfa | ||
|
|
b259e95c09 | ||
|
|
ec5674a6ad | ||
|
|
2aa6a8d889 | ||
|
|
b2971b48ed | ||
|
|
86d832ab9a | ||
|
|
61c7a81ec8 | ||
|
|
fbad68de0f | ||
|
|
c63ea608aa | ||
|
|
6eeb532c96 | ||
|
|
5857067a18 | ||
|
|
2d32fc2318 | ||
|
|
75a4ac12c6 | ||
|
|
eb9deffab6 | ||
|
|
15483c07c6 | ||
|
|
f9c3e2e872 | ||
|
|
3c621d37f0 | ||
|
|
dd8577e91d | ||
|
|
b3eb08254b | ||
|
|
f09c9b5fcc | ||
|
|
b8fb9b3f11 | ||
|
|
08d798b6fe | ||
|
|
3b5673daca | ||
|
|
a493ab2678 | ||
|
|
5ac018d1df | ||
|
|
df91c9f5e6 | ||
|
|
f611506dca | ||
|
|
28eb8c83d4 | ||
|
|
9db569b2d9 | ||
|
|
ada4242c01 | ||
|
|
93f93cb8bc | ||
|
|
afd0e2eb0b | ||
|
|
384cbe3829 | ||
|
|
6119836795 | ||
|
|
df8a2e3ad8 | ||
|
|
c7c99905f4 | ||
|
|
058196a089 | ||
|
|
31dac5d95f | ||
|
|
1bccbc424c | ||
|
|
71a3c60d95 | ||
|
|
2e1c58b905 | ||
|
|
25b73e135f | ||
|
|
fefc76e5da | ||
|
|
07f6646f7f | ||
|
|
27ce97fd42 | ||
|
|
9ff8504452 | ||
|
|
287a0f72a5 | ||
|
|
1feefabeba | ||
|
|
c8b3d92836 | ||
|
|
1d1f616063 | ||
|
|
cb5fe12ee1 | ||
|
|
166f5d1612 | ||
|
|
7395cd3124 | ||
|
|
890acfa2c0 | ||
|
|
ddc47e6df8 | ||
|
|
e4fed17f59 | ||
|
|
55dd027115 | ||
|
|
5a6d002bf0 | ||
|
|
395cc0c32f | ||
|
|
b840dd9af8 | ||
|
|
c4c256f56a | ||
|
|
c41451af75 | ||
|
|
4ea171fa5e | ||
|
|
d6006e9a3f | ||
|
|
65d42a428f | ||
|
|
04e52ffed0 | ||
|
|
a0e4c2e1fc | ||
|
|
12c1766997 | ||
|
|
377cd301b3 | ||
|
|
50866199a4 | ||
|
|
dba7bcd489 | ||
|
|
a1eeb9908d | ||
|
|
91f559a71f | ||
|
|
c17e1bd7a8 | ||
|
|
4a3d57e469 | ||
|
|
8aeb425669 | ||
|
|
859ba21f6d | ||
|
|
65b389da70 | ||
|
|
348ca07e0d | ||
|
|
0a6f685ad0 | ||
|
|
3aed797466 | ||
|
|
185e405bc1 | ||
|
|
519ddfae04 | ||
|
|
1914a1d21c | ||
|
|
f34176996e | ||
|
|
ac48e059bc | ||
|
|
95f45112e9 | ||
|
|
a5ebba7e36 | ||
|
|
a6cfc73cb2 | ||
|
|
90c3dd300f | ||
|
|
f072f48806 | ||
|
|
a323bc5af8 | ||
|
|
2069430baa | ||
|
|
fc708b396b | ||
|
|
e41c8b6780 | ||
|
|
646622ccd4 | ||
|
|
c7c4ef9d43 | ||
|
|
cdabc9064b | ||
|
|
8592f8a2b4 | ||
|
|
42a7c5d017 | ||
|
|
5388e6db84 | ||
|
|
38dbe57797 | ||
|
|
ddb186e61d | ||
|
|
5a8cd1b118 | ||
|
|
d2d7a5060f | ||
|
|
16ff0161b3 | ||
|
|
420b1f89d3 | ||
|
|
ee099b2697 | ||
|
|
945effcc75 | ||
|
|
5554de3933 | ||
|
|
8171ad65cd | ||
|
|
c68255f70f | ||
|
|
247cd92216 | ||
|
|
3d2e80daed | ||
|
|
19454e71d8 | ||
|
|
65e20f424a | ||
|
|
bc38d4c81b | ||
|
|
cfb9bcbe42 | ||
|
|
db7abfecdd | ||
|
|
ea4b7226a6 | ||
|
|
3a6e2922a2 |
@@ -9,11 +9,5 @@ cp "${REV_NAME}-source.tar.xz" "$DIR_NAME"
|
||||
|
||||
tar $COMPRESSION_FLAGS "$ARCHIVE_NAME" "$DIR_NAME"
|
||||
|
||||
mv "$DIR_NAME" $RELEASE_NAME
|
||||
mv "${REV_NAME}-source.tar.xz" $RELEASE_NAME
|
||||
|
||||
7z a "$REV_NAME.7z" $RELEASE_NAME
|
||||
|
||||
# move the compiled archive into the artifacts directory to be uploaded by travis releases
|
||||
mv "$ARCHIVE_NAME" "${ARTIFACTS_DIR}/"
|
||||
mv "$REV_NAME.7z" "${ARTIFACTS_DIR}/"
|
||||
|
||||
@@ -47,3 +47,6 @@ python3 .ci/scripts/windows/scan_dll.py package/imageformats/*.dll "package/"
|
||||
EXTERNALS_PATH="$(pwd)/build/externals"
|
||||
FFMPEG_DLL_PATH="$(find ${EXTERNALS_PATH} -maxdepth 1 -type d | grep ffmpeg)/bin"
|
||||
find ${FFMPEG_DLL_PATH} -type f -regex ".*\.dll" -exec cp -v {} package/ ';'
|
||||
|
||||
# copy libraries from yuzu.exe path
|
||||
find "$(pwd)/build/bin/" -type f -regex ".*\.dll" -exec cp -v {} package/ ';'
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
. .ci/scripts/common/pre-upload.sh
|
||||
|
||||
REV_NAME="yuzu-windows-mingw-${GITDATE}-${GITREV}"
|
||||
ARCHIVE_NAME="${REV_NAME}.tar.gz"
|
||||
COMPRESSION_FLAGS="-czvf"
|
||||
ARCHIVE_NAME="${REV_NAME}.tar.xz"
|
||||
COMPRESSION_FLAGS="-cJvf"
|
||||
|
||||
if [ "${RELEASE_NAME}" = "mainline" ]; then
|
||||
DIR_NAME="${REV_NAME}"
|
||||
|
||||
@@ -8,7 +8,7 @@ steps:
|
||||
displayName: 'Install vulkan-sdk'
|
||||
- script: python -m pip install --upgrade pip conan
|
||||
displayName: 'Install conan'
|
||||
- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cmake --install . --config Release && cd ..
|
||||
- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} -DCMAKE_BUILD_TYPE=Release .. && cd ..
|
||||
displayName: 'Configure CMake'
|
||||
- task: MSBuild@1
|
||||
displayName: 'Build'
|
||||
|
||||
5
.gitmodules
vendored
5
.gitmodules
vendored
@@ -18,7 +18,7 @@
|
||||
url = https://github.com/libusb/libusb.git
|
||||
[submodule "discord-rpc"]
|
||||
path = externals/discord-rpc
|
||||
url = https://github.com/discordapp/discord-rpc.git
|
||||
url = https://github.com/discord/discord-rpc.git
|
||||
[submodule "Vulkan-Headers"]
|
||||
path = externals/Vulkan-Headers
|
||||
url = https://github.com/KhronosGroup/Vulkan-Headers.git
|
||||
@@ -43,3 +43,6 @@
|
||||
[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
|
||||
|
||||
@@ -23,6 +23,8 @@ option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
|
||||
|
||||
option(YUZU_USE_BUNDLED_BOOST "Download bundled Boost" OFF)
|
||||
|
||||
option(YUZU_USE_BUNDLED_LIBUSB "Compile bundled libusb" OFF)
|
||||
|
||||
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" ON "WIN32" OFF)
|
||||
|
||||
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
|
||||
@@ -420,14 +422,22 @@ elseif (TARGET Boost::boost)
|
||||
endif()
|
||||
|
||||
# Ensure libusb is properly configured (based on dolphin libusb include)
|
||||
if(NOT APPLE)
|
||||
if(NOT APPLE AND NOT YUZU_USE_BUNDLED_LIBUSB)
|
||||
include(FindPkgConfig)
|
||||
find_package(LibUSB)
|
||||
endif()
|
||||
if (NOT LIBUSB_FOUND)
|
||||
add_subdirectory(externals/libusb)
|
||||
set(LIBUSB_INCLUDE_DIR "")
|
||||
set(LIBUSB_LIBRARIES usb)
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(LIBUSB QUIET libusb-1.0>=1.0.24)
|
||||
else()
|
||||
find_package(LibUSB)
|
||||
endif()
|
||||
|
||||
if (LIBUSB_FOUND)
|
||||
add_library(usb INTERFACE)
|
||||
target_include_directories(usb INTERFACE "${LIBUSB_INCLUDE_DIRS}")
|
||||
target_link_libraries(usb INTERFACE "${LIBUSB_LIBRARIES}")
|
||||
else()
|
||||
message(WARNING "libusb not found, falling back to externals")
|
||||
set(YUZU_USE_BUNDLED_LIBUSB ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# List of all FFmpeg components required
|
||||
|
||||
29
externals/CMakeLists.txt
vendored
29
externals/CMakeLists.txt
vendored
@@ -45,21 +45,28 @@ target_include_directories(microprofile INTERFACE ./microprofile)
|
||||
add_library(unicorn-headers INTERFACE)
|
||||
target_include_directories(unicorn-headers INTERFACE ./unicorn/include)
|
||||
|
||||
# libusb
|
||||
if (NOT LIBUSB_FOUND OR YUZU_USE_BUNDLED_LIBUSB)
|
||||
add_subdirectory(libusb)
|
||||
endif()
|
||||
|
||||
# SDL2
|
||||
if (NOT SDL2_FOUND AND ENABLE_SDL2)
|
||||
# Yuzu itself needs: Events Joystick Haptic Sensor Timers
|
||||
# Yuzu-cmd also needs: Video (depends on Loadso/Dlopen)
|
||||
set(SDL_UNUSED_SUBSYSTEMS
|
||||
Atomic Audio Render Power Threads
|
||||
File CPUinfo Filesystem Locale)
|
||||
foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS})
|
||||
string(TOUPPER ${_SUB} _OPT)
|
||||
option(SDL_${_OPT} "" OFF)
|
||||
endforeach()
|
||||
if (NOT WIN32)
|
||||
# Yuzu itself needs: Events Joystick Haptic Sensor Timers Audio
|
||||
# Yuzu-cmd also needs: Video (depends on Loadso/Dlopen)
|
||||
set(SDL_UNUSED_SUBSYSTEMS
|
||||
Atomic Render Power Threads
|
||||
File CPUinfo Filesystem Locale)
|
||||
foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS})
|
||||
string(TOUPPER ${_SUB} _OPT)
|
||||
option(SDL_${_OPT} "" OFF)
|
||||
endforeach()
|
||||
|
||||
option(HIDAPI "" ON)
|
||||
endif()
|
||||
set(SDL_STATIC ON)
|
||||
set(SDL_SHARED OFF)
|
||||
option(HIDAPI "" ON)
|
||||
|
||||
add_subdirectory(SDL EXCLUDE_FROM_ALL)
|
||||
add_library(SDL2 ALIAS SDL2-static)
|
||||
@@ -108,7 +115,7 @@ if (ENABLE_WEB_SERVICE)
|
||||
|
||||
# httplib
|
||||
add_library(httplib INTERFACE)
|
||||
target_include_directories(httplib INTERFACE ./httplib)
|
||||
target_include_directories(httplib INTERFACE ./cpp-httplib)
|
||||
target_compile_definitions(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT)
|
||||
target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES})
|
||||
if (WIN32)
|
||||
|
||||
2
externals/SDL
vendored
2
externals/SDL
vendored
Submodule externals/SDL updated: 107db2d899...2f248a2a31
1
externals/cpp-httplib
vendored
Submodule
1
externals/cpp-httplib
vendored
Submodule
Submodule externals/cpp-httplib added at 9648f950f5
2
externals/discord-rpc
vendored
2
externals/discord-rpc
vendored
Submodule externals/discord-rpc updated: e32d001809...963aa9f3e5
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: b2a4da5e65...0c12614d1a
15
externals/httplib/README.md
vendored
15
externals/httplib/README.md
vendored
@@ -1,15 +0,0 @@
|
||||
From https://github.com/yhirose/cpp-httplib/tree/ff5677ad197947177c158fe857caff4f0e242045 with https://github.com/yhirose/cpp-httplib/pull/701
|
||||
|
||||
MIT License
|
||||
|
||||
===
|
||||
|
||||
cpp-httplib
|
||||
|
||||
A C++11 header-only HTTP library.
|
||||
|
||||
It's extremely easy to setup. Just include httplib.h file in your code!
|
||||
|
||||
Inspired by Sinatra and express.
|
||||
|
||||
© 2017 Yuji Hirose
|
||||
6714
externals/httplib/httplib.h
vendored
6714
externals/httplib/httplib.h
vendored
File diff suppressed because it is too large
Load Diff
378
externals/libusb/CMakeLists.txt
vendored
378
externals/libusb/CMakeLists.txt
vendored
@@ -1,155 +1,259 @@
|
||||
# Ensure libusb compiles with UTF-8 encoding on MSVC
|
||||
if(MSVC)
|
||||
add_compile_options(/utf-8)
|
||||
endif()
|
||||
if (MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux"))
|
||||
set(LIBUSB_FOUND ON CACHE BOOL "libusb is present" FORCE)
|
||||
set(LIBUSB_VERSION "1.0.24" CACHE STRING "libusb version string" FORCE)
|
||||
|
||||
add_library(usb STATIC EXCLUDE_FROM_ALL
|
||||
libusb/libusb/core.c
|
||||
libusb/libusb/core.c
|
||||
libusb/libusb/descriptor.c
|
||||
libusb/libusb/hotplug.c
|
||||
libusb/libusb/io.c
|
||||
libusb/libusb/strerror.c
|
||||
libusb/libusb/sync.c
|
||||
)
|
||||
set_target_properties(usb PROPERTIES VERSION 1.0.23)
|
||||
if(WIN32)
|
||||
target_include_directories(usb
|
||||
BEFORE
|
||||
PUBLIC
|
||||
libusb/libusb
|
||||
# GNU toolchains for some reason doesn't work with the later half of this CMakeLists after
|
||||
# updating to 1.0.24, so we do it the old-fashioned way for now.
|
||||
|
||||
PRIVATE
|
||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||
)
|
||||
set(LIBUSB_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/libusb")
|
||||
set(LIBUSB_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libusb")
|
||||
|
||||
if (NOT MINGW)
|
||||
target_include_directories(usb BEFORE PRIVATE libusb/msvc)
|
||||
# Workarounds for MSYS/MinGW
|
||||
if (MSYS)
|
||||
# CMake on Windows passes `C:/`, but we need `/C/` or `/c/` to use `configure`
|
||||
string(REPLACE ":/" "/" LIBUSB_SRC_DIR "${LIBUSB_SRC_DIR}")
|
||||
set(LIBUSB_SRC_DIR "/${LIBUSB_SRC_DIR}")
|
||||
|
||||
# And now that we are using /C/ for srcdir but everything else is using C:/, we need to
|
||||
# compile everything in the source directory, else `configure` won't think the build
|
||||
# environment is sane.
|
||||
set(LIBUSB_PREFIX "${LIBUSB_SRC_DIR}")
|
||||
endif()
|
||||
|
||||
# Works around other libraries providing their own definition of USB GUIDs (e.g. SDL2)
|
||||
target_compile_definitions(usb PRIVATE "-DGUID_DEVINTERFACE_USB_DEVICE=(GUID){ 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED}}")
|
||||
else()
|
||||
target_include_directories(usb
|
||||
# turns out other projects also have "config.h", so make sure the
|
||||
# LibUSB one comes first
|
||||
BEFORE
|
||||
set(LIBUSB_CONFIGURE "${LIBUSB_SRC_DIR}/configure")
|
||||
set(LIBUSB_MAKEFILE "${LIBUSB_PREFIX}/Makefile")
|
||||
|
||||
PUBLIC
|
||||
libusb/libusb
|
||||
if (MINGW)
|
||||
set(LIBUSB_LIBRARIES "${LIBUSB_PREFIX}/libusb/.libs/libusb-1.0.dll.a" CACHE PATH "libusb library path" FORCE)
|
||||
set(LIBUSB_SHARED_LIBRARY "${LIBUSB_PREFIX}/libusb/.libs/libusb-1.0.dll")
|
||||
set(LIBUSB_SHARED_LIBRARY_DEST "${CMAKE_BINARY_DIR}/bin/libusb-1.0.dll")
|
||||
|
||||
PRIVATE
|
||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WIN32 OR CYGWIN)
|
||||
target_sources(usb PRIVATE
|
||||
libusb/libusb/os/threads_windows.c
|
||||
libusb/libusb/os/windows_winusb.c
|
||||
libusb/libusb/os/windows_usbdk.c
|
||||
libusb/libusb/os/windows_nt_common.c
|
||||
)
|
||||
set(OS_WINDOWS TRUE)
|
||||
elseif(APPLE)
|
||||
target_sources(usb PRIVATE
|
||||
libusb/libusb/os/darwin_usb.c
|
||||
)
|
||||
find_library(COREFOUNDATION_LIBRARY CoreFoundation)
|
||||
find_library(IOKIT_LIBRARY IOKit)
|
||||
find_library(OBJC_LIBRARY objc)
|
||||
target_link_libraries(usb PRIVATE
|
||||
${COREFOUNDATION_LIBRARY}
|
||||
${IOKIT_LIBRARY}
|
||||
${OBJC_LIBRARY}
|
||||
)
|
||||
set(OS_DARWIN TRUE)
|
||||
elseif(ANDROID)
|
||||
target_sources(usb PRIVATE
|
||||
libusb/libusb/os/linux_usbfs.c
|
||||
libusb/libusb/os/linux_netlink.c
|
||||
)
|
||||
find_library(LOG_LIBRARY log)
|
||||
target_link_libraries(usb PRIVATE ${LOG_LIBRARY})
|
||||
set(OS_LINUX TRUE)
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
target_sources(usb PRIVATE
|
||||
libusb/libusb/os/linux_usbfs.c
|
||||
)
|
||||
find_package(Libudev)
|
||||
if(LIBUDEV_FOUND)
|
||||
target_sources(usb PRIVATE
|
||||
libusb/libusb/os/linux_udev.c
|
||||
)
|
||||
target_link_libraries(usb PRIVATE "${LIBUDEV_LIBRARIES}")
|
||||
target_include_directories(usb PRIVATE "${LIBUDEV_INCLUDE_DIR}")
|
||||
set(HAVE_LIBUDEV TRUE)
|
||||
set(USE_UDEV TRUE)
|
||||
set(LIBUSB_CONFIGURE_ARGS --host=x86_64-w64-mingw32 --build=x86_64-windows)
|
||||
else()
|
||||
set(LIBUSB_LIBRARIES "${LIBUSB_PREFIX}/libusb/.libs/libusb-1.0.a" CACHE PATH "libusb library path" FORCE)
|
||||
endif()
|
||||
|
||||
set(LIBUSB_INCLUDE_DIRS "${LIBUSB_SRC_DIR}/libusb" CACHE PATH "libusb headers path" FORCE)
|
||||
|
||||
# MINGW: causes "externals/libusb/libusb/libusb/os/windows_winusb.c:1427:2: error: conversion to non-scalar type requested", so cannot statically link it for now.
|
||||
if (NOT MINGW)
|
||||
set(LIBUSB_CFLAGS "-DGUID_DEVINTERFACE_USB_DEVICE=\\(GUID\\){0xA5DCBF10,0x6530,0x11D2,{0x90,0x1F,0x00,0xC0,0x4F,0xB9,0x51,0xED}}")
|
||||
endif()
|
||||
|
||||
make_directory("${LIBUSB_PREFIX}")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
"${LIBUSB_LIBRARIES}"
|
||||
COMMAND
|
||||
make
|
||||
WORKING_DIRECTORY
|
||||
"${LIBUSB_PREFIX}"
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
"${LIBUSB_MAKEFILE}"
|
||||
COMMAND
|
||||
env
|
||||
CFLAGS="${LIBUSB_CFLAGS}"
|
||||
sh "${LIBUSB_CONFIGURE}"
|
||||
${LIBUSB_CONFIGURE_ARGS}
|
||||
--srcdir="${LIBUSB_SRC_DIR}"
|
||||
WORKING_DIRECTORY
|
||||
"${LIBUSB_PREFIX}"
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
"${LIBUSB_CONFIGURE}"
|
||||
COMMAND
|
||||
sh "${LIBUSB_SRC_DIR}/bootstrap.sh"
|
||||
WORKING_DIRECTORY
|
||||
"${LIBUSB_SRC_DIR}"
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
"${LIBUSB_SHARED_LIBRARY_DEST}"
|
||||
COMMAND
|
||||
cp "${LIBUSB_SHARED_LIBRARY}" "${LIBUSB_SHARED_LIBRARY_DEST}"
|
||||
)
|
||||
|
||||
add_custom_target(usb-bootstrap DEPENDS "${LIBUSB_CONFIGURE}")
|
||||
add_custom_target(usb-configure DEPENDS "${LIBUSB_MAKEFILE}" usb-bootstrap)
|
||||
add_custom_target(usb-build ALL DEPENDS "${LIBUSB_LIBRARIES}" usb-configure)
|
||||
# Workaround since static linking didn't work out -- We need to copy the DLL to the bin directory
|
||||
add_custom_target(usb-copy ALL DEPENDS "${LIBUSB_SHARED_LIBRARY_DEST}" usb-build)
|
||||
|
||||
add_library(usb INTERFACE)
|
||||
add_dependencies(usb usb-copy)
|
||||
target_link_libraries(usb INTERFACE "${LIBUSB_LIBRARIES}")
|
||||
target_include_directories(usb INTERFACE "${LIBUSB_INCLUDE_DIRS}")
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
Include(FindPkgConfig)
|
||||
pkg_check_modules(LIBUDEV REQUIRED libudev)
|
||||
|
||||
if (LIBUDEV_FOUND)
|
||||
target_include_directories(usb INTERFACE "${LIBUDEV_INCLUDE_DIRS}")
|
||||
target_link_libraries(usb INTERFACE "${LIBUDEV_STATIC_LIBRARIES}")
|
||||
endif()
|
||||
endif()
|
||||
else() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
# Ensure libusb compiles with UTF-8 encoding on MSVC
|
||||
if(MSVC)
|
||||
add_compile_options(/utf-8)
|
||||
endif()
|
||||
|
||||
add_library(usb STATIC EXCLUDE_FROM_ALL
|
||||
libusb/libusb/core.c
|
||||
libusb/libusb/core.c
|
||||
libusb/libusb/descriptor.c
|
||||
libusb/libusb/hotplug.c
|
||||
libusb/libusb/io.c
|
||||
libusb/libusb/strerror.c
|
||||
libusb/libusb/sync.c
|
||||
)
|
||||
set_target_properties(usb PROPERTIES VERSION 1.0.24)
|
||||
if(WIN32)
|
||||
target_include_directories(usb
|
||||
BEFORE
|
||||
PUBLIC
|
||||
libusb/libusb
|
||||
|
||||
PRIVATE
|
||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||
)
|
||||
|
||||
if (NOT MINGW)
|
||||
target_include_directories(usb BEFORE PRIVATE libusb/msvc)
|
||||
endif()
|
||||
|
||||
# Works around other libraries providing their own definition of USB GUIDs (e.g. SDL2)
|
||||
target_compile_definitions(usb PRIVATE "-DGUID_DEVINTERFACE_USB_DEVICE=(GUID){ 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED}}")
|
||||
else()
|
||||
target_include_directories(usb
|
||||
# turns out other projects also have "config.h", so make sure the
|
||||
# LibUSB one comes first
|
||||
BEFORE
|
||||
|
||||
PUBLIC
|
||||
libusb/libusb
|
||||
|
||||
PRIVATE
|
||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WIN32 OR CYGWIN)
|
||||
target_sources(usb PRIVATE
|
||||
libusb/libusb/os/threads_windows.c
|
||||
libusb/libusb/os/windows_winusb.c
|
||||
libusb/libusb/os/windows_usbdk.c
|
||||
libusb/libusb/os/windows_common.c
|
||||
)
|
||||
set(OS_WINDOWS TRUE)
|
||||
elseif(APPLE)
|
||||
target_sources(usb PRIVATE
|
||||
libusb/libusb/os/darwin_usb.c
|
||||
)
|
||||
find_library(COREFOUNDATION_LIBRARY CoreFoundation)
|
||||
find_library(IOKIT_LIBRARY IOKit)
|
||||
find_library(OBJC_LIBRARY objc)
|
||||
target_link_libraries(usb PRIVATE
|
||||
${COREFOUNDATION_LIBRARY}
|
||||
${IOKIT_LIBRARY}
|
||||
${OBJC_LIBRARY}
|
||||
)
|
||||
set(OS_DARWIN TRUE)
|
||||
elseif(ANDROID)
|
||||
target_sources(usb PRIVATE
|
||||
libusb/libusb/os/linux_usbfs.c
|
||||
libusb/libusb/os/linux_netlink.c
|
||||
)
|
||||
find_library(LOG_LIBRARY log)
|
||||
target_link_libraries(usb PRIVATE ${LOG_LIBRARY})
|
||||
set(OS_LINUX TRUE)
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
target_sources(usb PRIVATE
|
||||
libusb/libusb/os/linux_usbfs.c
|
||||
)
|
||||
find_package(Libudev)
|
||||
if(LIBUDEV_FOUND)
|
||||
target_sources(usb PRIVATE
|
||||
libusb/libusb/os/linux_udev.c
|
||||
)
|
||||
target_link_libraries(usb PRIVATE "${LIBUDEV_LIBRARIES}")
|
||||
target_include_directories(usb PRIVATE "${LIBUDEV_INCLUDE_DIR}")
|
||||
set(HAVE_LIBUDEV TRUE)
|
||||
set(USE_UDEV TRUE)
|
||||
else()
|
||||
target_sources(usb PRIVATE
|
||||
libusb/libusb/os/linux_netlink.c
|
||||
)
|
||||
endif()
|
||||
set(OS_LINUX TRUE)
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "NetBSD")
|
||||
target_sources(usb PRIVATE
|
||||
libusb/libusb/os/netbsd_usb.c
|
||||
)
|
||||
set(OS_NETBSD TRUE)
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD")
|
||||
target_sources(usb PRIVATE
|
||||
libusb/libusb/os/openbsd_usb.c
|
||||
)
|
||||
set(OS_OPENBSD TRUE)
|
||||
endif()
|
||||
set(OS_LINUX TRUE)
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "NetBSD")
|
||||
target_sources(usb PRIVATE
|
||||
libusb/libusb/os/netbsd_usb.c
|
||||
)
|
||||
set(OS_NETBSD TRUE)
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD")
|
||||
target_sources(usb PRIVATE
|
||||
libusb/libusb/os/openbsd_usb.c
|
||||
)
|
||||
set(OS_OPENBSD TRUE)
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
target_sources(usb PRIVATE
|
||||
libusb/libusb/os/poll_posix.c
|
||||
libusb/libusb/os/threads_posix.c
|
||||
)
|
||||
find_package(Threads REQUIRED)
|
||||
if(THREADS_HAVE_PTHREAD_ARG)
|
||||
target_compile_options(usb PUBLIC "-pthread")
|
||||
if(UNIX)
|
||||
target_sources(usb PRIVATE
|
||||
libusb/libusb/os/events_posix.c
|
||||
libusb/libusb/os/threads_posix.c
|
||||
)
|
||||
find_package(Threads REQUIRED)
|
||||
if(THREADS_HAVE_PTHREAD_ARG)
|
||||
target_compile_options(usb PUBLIC "-pthread")
|
||||
endif()
|
||||
if(CMAKE_THREAD_LIBS_INIT)
|
||||
target_link_libraries(usb PRIVATE "${CMAKE_THREAD_LIBS_INIT}")
|
||||
endif()
|
||||
set(THREADS_POSIX TRUE)
|
||||
elseif(WIN32)
|
||||
target_sources(usb PRIVATE
|
||||
libusb/libusb/os/events_windows.c
|
||||
libusb/libusb/os/threads_windows.c
|
||||
)
|
||||
endif()
|
||||
if(CMAKE_THREAD_LIBS_INIT)
|
||||
target_link_libraries(usb PRIVATE "${CMAKE_THREAD_LIBS_INIT}")
|
||||
|
||||
include(CheckFunctionExists)
|
||||
include(CheckIncludeFiles)
|
||||
include(CheckTypeSize)
|
||||
check_include_files(asm/types.h HAVE_ASM_TYPES_H)
|
||||
check_function_exists(gettimeofday HAVE_GETTIMEOFDAY)
|
||||
check_include_files(linux/filter.h HAVE_LINUX_FILTER_H)
|
||||
check_include_files(linux/netlink.h HAVE_LINUX_NETLINK_H)
|
||||
check_include_files(poll.h HAVE_POLL_H)
|
||||
check_include_files(signal.h HAVE_SIGNAL_H)
|
||||
check_include_files(strings.h HAVE_STRINGS_H)
|
||||
check_type_size("struct timespec" STRUCT_TIMESPEC)
|
||||
check_function_exists(syslog HAVE_SYSLOG_FUNC)
|
||||
check_include_files(syslog.h HAVE_SYSLOG_H)
|
||||
check_include_files(sys/socket.h HAVE_SYS_SOCKET_H)
|
||||
check_include_files(sys/time.h HAVE_SYS_TIME_H)
|
||||
check_include_files(sys/types.h HAVE_SYS_TYPES_H)
|
||||
|
||||
set(CMAKE_EXTRA_INCLUDE_FILES poll.h)
|
||||
check_type_size("nfds_t" nfds_t)
|
||||
unset(CMAKE_EXTRA_INCLUDE_FILES)
|
||||
if(HAVE_NFDS_T)
|
||||
set(POLL_NFDS_TYPE "nfds_t")
|
||||
else()
|
||||
set(POLL_NFDS_TYPE "unsigned int")
|
||||
endif()
|
||||
set(THREADS_POSIX TRUE)
|
||||
elseif(WIN32)
|
||||
target_sources(usb PRIVATE
|
||||
libusb/libusb/os/poll_windows.c
|
||||
libusb/libusb/os/threads_windows.c
|
||||
)
|
||||
endif()
|
||||
|
||||
include(CheckFunctionExists)
|
||||
include(CheckIncludeFiles)
|
||||
include(CheckTypeSize)
|
||||
check_include_files(asm/types.h HAVE_ASM_TYPES_H)
|
||||
check_function_exists(gettimeofday HAVE_GETTIMEOFDAY)
|
||||
check_include_files(linux/filter.h HAVE_LINUX_FILTER_H)
|
||||
check_include_files(linux/netlink.h HAVE_LINUX_NETLINK_H)
|
||||
check_include_files(poll.h HAVE_POLL_H)
|
||||
check_include_files(signal.h HAVE_SIGNAL_H)
|
||||
check_include_files(strings.h HAVE_STRINGS_H)
|
||||
check_type_size("struct timespec" STRUCT_TIMESPEC)
|
||||
check_function_exists(syslog HAVE_SYSLOG_FUNC)
|
||||
check_include_files(syslog.h HAVE_SYSLOG_H)
|
||||
check_include_files(sys/socket.h HAVE_SYS_SOCKET_H)
|
||||
check_include_files(sys/time.h HAVE_SYS_TIME_H)
|
||||
check_include_files(sys/types.h HAVE_SYS_TYPES_H)
|
||||
|
||||
set(CMAKE_EXTRA_INCLUDE_FILES poll.h)
|
||||
check_type_size("nfds_t" nfds_t)
|
||||
unset(CMAKE_EXTRA_INCLUDE_FILES)
|
||||
if(HAVE_NFDS_T)
|
||||
set(POLL_NFDS_TYPE "nfds_t")
|
||||
else()
|
||||
set(POLL_NFDS_TYPE "unsigned int")
|
||||
endif()
|
||||
|
||||
check_include_files(sys/timerfd.h USBI_TIMERFD_AVAILABLE)
|
||||
check_include_files(sys/timerfd.h USBI_TIMERFD_AVAILABLE)
|
||||
|
||||
|
||||
configure_file(config.h.in config.h)
|
||||
configure_file(config.h.in config.h)
|
||||
endif() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
|
||||
2
externals/libusb/libusb
vendored
2
externals/libusb/libusb
vendored
Submodule externals/libusb/libusb updated: e782eeb251...c6a35c5601
@@ -42,6 +42,7 @@ add_library(audio_core STATIC
|
||||
voice_context.h
|
||||
|
||||
$<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h>
|
||||
$<$<BOOL:${ENABLE_SDL2}>:sdl2_sink.cpp sdl2_sink.h>
|
||||
)
|
||||
|
||||
create_target_directory_groups(audio_core)
|
||||
@@ -71,3 +72,7 @@ if(ENABLE_CUBEB)
|
||||
target_link_libraries(audio_core PRIVATE cubeb)
|
||||
target_compile_definitions(audio_core PRIVATE -DHAVE_CUBEB=1)
|
||||
endif()
|
||||
if(ENABLE_SDL2)
|
||||
target_link_libraries(audio_core PRIVATE SDL2)
|
||||
target_compile_definitions(audio_core PRIVATE HAVE_SDL2)
|
||||
endif()
|
||||
|
||||
@@ -197,7 +197,7 @@ ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_param
|
||||
|
||||
ReleaseAndQueueBuffers();
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||
|
||||
@@ -407,7 +407,7 @@ ResultCode InfoUpdater::UpdateMixes(MixContext& mix_context, std::size_t mix_buf
|
||||
|
||||
// TODO(ogniK): Sort when splitter is suppoorted
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
bool InfoUpdater::UpdateSinks(SinkContext& sink_context) {
|
||||
|
||||
163
src/audio_core/sdl2_sink.cpp
Normal file
163
src/audio_core/sdl2_sink.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include "audio_core/sdl2_sink.h"
|
||||
#include "audio_core/stream.h"
|
||||
#include "audio_core/time_stretch.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
//#include "common/settings.h"
|
||||
|
||||
// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
|
||||
#endif
|
||||
#include <SDL.h>
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class SDLSinkStream final : public SinkStream {
|
||||
public:
|
||||
SDLSinkStream(u32 sample_rate, u32 num_channels_, const std::string& output_device)
|
||||
: num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate, num_channels} {
|
||||
|
||||
SDL_AudioSpec spec;
|
||||
spec.freq = sample_rate;
|
||||
spec.channels = static_cast<u8>(num_channels);
|
||||
spec.format = AUDIO_S16SYS;
|
||||
spec.samples = 4096;
|
||||
spec.callback = nullptr;
|
||||
|
||||
SDL_AudioSpec obtained;
|
||||
if (output_device.empty()) {
|
||||
dev = SDL_OpenAudioDevice(nullptr, 0, &spec, &obtained, 0);
|
||||
} else {
|
||||
dev = SDL_OpenAudioDevice(output_device.c_str(), 0, &spec, &obtained, 0);
|
||||
}
|
||||
|
||||
if (dev == 0) {
|
||||
LOG_CRITICAL(Audio_Sink, "Error opening sdl audio device: {}", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_PauseAudioDevice(dev, 0);
|
||||
}
|
||||
|
||||
~SDLSinkStream() override {
|
||||
if (dev == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_CloseAudioDevice(dev);
|
||||
}
|
||||
|
||||
void EnqueueSamples(u32 source_num_channels, const std::vector<s16>& samples) override {
|
||||
if (source_num_channels > num_channels) {
|
||||
// Downsample 6 channels to 2
|
||||
ASSERT_MSG(source_num_channels == 6, "Channel count must be 6");
|
||||
|
||||
std::vector<s16> buf;
|
||||
buf.reserve(samples.size() * num_channels / source_num_channels);
|
||||
for (std::size_t i = 0; i < samples.size(); i += source_num_channels) {
|
||||
// Downmixing implementation taken from the ATSC standard
|
||||
const s16 left{samples[i + 0]};
|
||||
const s16 right{samples[i + 1]};
|
||||
const s16 center{samples[i + 2]};
|
||||
const s16 surround_left{samples[i + 4]};
|
||||
const s16 surround_right{samples[i + 5]};
|
||||
// Not used in the ATSC reference implementation
|
||||
[[maybe_unused]] const s16 low_frequency_effects{samples[i + 3]};
|
||||
|
||||
constexpr s32 clev{707}; // center mixing level coefficient
|
||||
constexpr s32 slev{707}; // surround mixing level coefficient
|
||||
|
||||
buf.push_back(static_cast<s16>(left + (clev * center / 1000) +
|
||||
(slev * surround_left / 1000)));
|
||||
buf.push_back(static_cast<s16>(right + (clev * center / 1000) +
|
||||
(slev * surround_right / 1000)));
|
||||
}
|
||||
int ret = SDL_QueueAudio(dev, static_cast<const void*>(buf.data()),
|
||||
static_cast<u32>(buf.size() * sizeof(s16)));
|
||||
if (ret < 0)
|
||||
LOG_WARNING(Audio_Sink, "Could not queue audio buffer: {}", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
int ret = SDL_QueueAudio(dev, static_cast<const void*>(samples.data()),
|
||||
static_cast<u32>(samples.size() * sizeof(s16)));
|
||||
if (ret < 0)
|
||||
LOG_WARNING(Audio_Sink, "Could not queue audio buffer: {}", SDL_GetError());
|
||||
}
|
||||
|
||||
std::size_t SamplesInQueue(u32 channel_count) const override {
|
||||
if (dev == 0)
|
||||
return 0;
|
||||
|
||||
return SDL_GetQueuedAudioSize(dev) / (channel_count * sizeof(s16));
|
||||
}
|
||||
|
||||
void Flush() override {
|
||||
should_flush = true;
|
||||
}
|
||||
|
||||
u32 GetNumChannels() const {
|
||||
return num_channels;
|
||||
}
|
||||
|
||||
private:
|
||||
SDL_AudioDeviceID dev = 0;
|
||||
u32 num_channels{};
|
||||
std::atomic<bool> should_flush{};
|
||||
TimeStretcher time_stretch;
|
||||
};
|
||||
|
||||
SDLSink::SDLSink(std::string_view target_device_name) {
|
||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
||||
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (target_device_name != auto_device_name && !target_device_name.empty()) {
|
||||
output_device = target_device_name;
|
||||
} else {
|
||||
output_device.clear();
|
||||
}
|
||||
}
|
||||
|
||||
SDLSink::~SDLSink() = default;
|
||||
|
||||
SinkStream& SDLSink::AcquireSinkStream(u32 sample_rate, u32 num_channels, const std::string&) {
|
||||
sink_streams.push_back(
|
||||
std::make_unique<SDLSinkStream>(sample_rate, num_channels, output_device));
|
||||
return *sink_streams.back();
|
||||
}
|
||||
|
||||
std::vector<std::string> ListSDLSinkDevices() {
|
||||
std::vector<std::string> device_list;
|
||||
|
||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
||||
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const int device_count = SDL_GetNumAudioDevices(0);
|
||||
for (int i = 0; i < device_count; ++i) {
|
||||
device_list.emplace_back(SDL_GetAudioDeviceName(i, 0));
|
||||
}
|
||||
|
||||
return device_list;
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
29
src/audio_core/sdl2_sink.h
Normal file
29
src/audio_core/sdl2_sink.h
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "audio_core/sink.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class SDLSink final : public Sink {
|
||||
public:
|
||||
explicit SDLSink(std::string_view device_id);
|
||||
~SDLSink() override;
|
||||
|
||||
SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
|
||||
const std::string& name) override;
|
||||
|
||||
private:
|
||||
std::string output_device;
|
||||
std::vector<SinkStreamPtr> sink_streams;
|
||||
};
|
||||
|
||||
std::vector<std::string> ListSDLSinkDevices();
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -11,6 +11,9 @@
|
||||
#ifdef HAVE_CUBEB
|
||||
#include "audio_core/cubeb_sink.h"
|
||||
#endif
|
||||
#ifdef HAVE_SDL2
|
||||
#include "audio_core/sdl2_sink.h"
|
||||
#endif
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace AudioCore {
|
||||
@@ -35,6 +38,13 @@ constexpr SinkDetails sink_details[] = {
|
||||
return std::make_unique<CubebSink>(device_id);
|
||||
},
|
||||
&ListCubebSinkDevices},
|
||||
#endif
|
||||
#ifdef HAVE_SDL2
|
||||
SinkDetails{"sdl2",
|
||||
[](std::string_view device_id) -> std::unique_ptr<Sink> {
|
||||
return std::make_unique<SDLSink>(device_id);
|
||||
},
|
||||
&ListSDLSinkDevices},
|
||||
#endif
|
||||
SinkDetails{"null",
|
||||
[](std::string_view device_id) -> std::unique_ptr<Sink> {
|
||||
|
||||
@@ -107,9 +107,12 @@ void Stream::PlayNextBuffer(std::chrono::nanoseconds ns_late) {
|
||||
active_buffer = queued_buffers.front();
|
||||
queued_buffers.pop();
|
||||
|
||||
VolumeAdjustSamples(active_buffer->GetSamples(), game_volume);
|
||||
auto& samples = active_buffer->GetSamples();
|
||||
|
||||
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
|
||||
VolumeAdjustSamples(samples, game_volume);
|
||||
|
||||
sink_stream.EnqueueSamples(GetNumChannels(), samples);
|
||||
played_samples += samples.size();
|
||||
|
||||
const auto buffer_release_ns = GetBufferReleaseNS(*active_buffer);
|
||||
|
||||
|
||||
@@ -89,6 +89,11 @@ public:
|
||||
return sample_rate;
|
||||
}
|
||||
|
||||
/// Gets the number of samples played so far
|
||||
[[nodiscard]] u64 GetPlayedSampleCount() const {
|
||||
return played_samples;
|
||||
}
|
||||
|
||||
/// Gets the number of channels
|
||||
[[nodiscard]] u32 GetNumChannels() const;
|
||||
|
||||
@@ -106,6 +111,7 @@ private:
|
||||
[[nodiscard]] std::chrono::nanoseconds GetBufferReleaseNS(const Buffer& buffer) const;
|
||||
|
||||
u32 sample_rate; ///< Sample rate of the stream
|
||||
u64 played_samples{}; ///< The current played sample count
|
||||
Format format; ///< Format of the stream
|
||||
float game_volume = 1.0f; ///< The volume the game currently has set
|
||||
ReleaseCallback release_callback; ///< Buffer release callback for the stream
|
||||
|
||||
@@ -21,14 +21,14 @@ find_package(Git QUIET)
|
||||
|
||||
add_custom_command(OUTPUT scm_rev.cpp
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DSRC_DIR="${CMAKE_SOURCE_DIR}"
|
||||
-DBUILD_REPOSITORY="${BUILD_REPOSITORY}"
|
||||
-DTITLE_BAR_FORMAT_IDLE="${TITLE_BAR_FORMAT_IDLE}"
|
||||
-DTITLE_BAR_FORMAT_RUNNING="${TITLE_BAR_FORMAT_RUNNING}"
|
||||
-DBUILD_TAG="${BUILD_TAG}"
|
||||
-DBUILD_ID="${DISPLAY_VERSION}"
|
||||
-DGIT_EXECUTABLE="${GIT_EXECUTABLE}"
|
||||
-P "${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake"
|
||||
-DSRC_DIR=${CMAKE_SOURCE_DIR}
|
||||
-DBUILD_REPOSITORY=${BUILD_REPOSITORY}
|
||||
-DTITLE_BAR_FORMAT_IDLE=${TITLE_BAR_FORMAT_IDLE}
|
||||
-DTITLE_BAR_FORMAT_RUNNING=${TITLE_BAR_FORMAT_RUNNING}
|
||||
-DBUILD_TAG=${BUILD_TAG}
|
||||
-DBUILD_ID=${DISPLAY_VERSION}
|
||||
-DGIT_EXECUTABLE=${GIT_EXECUTABLE}
|
||||
-P ${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake
|
||||
DEPENDS
|
||||
# WARNING! It was too much work to try and make a common location for this list,
|
||||
# so if you need to change it, please update CMakeModules/GenerateSCMRev.cmake as well
|
||||
@@ -92,6 +92,7 @@ add_custom_command(OUTPUT scm_rev.cpp
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.h"
|
||||
# technically we should regenerate if the git version changed, but its not worth the effort imo
|
||||
"${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_library(common STATIC
|
||||
@@ -130,6 +131,8 @@ add_library(common STATIC
|
||||
hash.h
|
||||
hex_util.cpp
|
||||
hex_util.h
|
||||
host_memory.cpp
|
||||
host_memory.h
|
||||
intrusive_red_black_tree.h
|
||||
logging/backend.cpp
|
||||
logging/backend.h
|
||||
@@ -138,6 +141,7 @@ add_library(common STATIC
|
||||
logging/log.h
|
||||
logging/text_formatter.cpp
|
||||
logging/text_formatter.h
|
||||
logging/types.h
|
||||
lz4_compression.cpp
|
||||
lz4_compression.h
|
||||
math_util.h
|
||||
@@ -154,6 +158,7 @@ add_library(common STATIC
|
||||
param_package.cpp
|
||||
param_package.h
|
||||
parent_of_member.h
|
||||
point.h
|
||||
quaternion.h
|
||||
ring_buffer.h
|
||||
scm_rev.cpp
|
||||
|
||||
@@ -97,17 +97,6 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
|
||||
return static_cast<T>(key) == 0; \
|
||||
}
|
||||
|
||||
/// Evaluates a boolean expression, and returns a result unless that expression is true.
|
||||
#define R_UNLESS(expr, res) \
|
||||
{ \
|
||||
if (!(expr)) { \
|
||||
if (res.IsError()) { \
|
||||
LOG_ERROR(Kernel, "Failed with result: {}", res.raw); \
|
||||
} \
|
||||
return res; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define YUZU_NON_COPYABLE(cls) \
|
||||
cls(const cls&) = delete; \
|
||||
cls& operator=(const cls&) = delete
|
||||
@@ -116,20 +105,6 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
|
||||
cls(cls&&) = delete; \
|
||||
cls& operator=(cls&&) = delete
|
||||
|
||||
#define R_SUCCEEDED(res) (res.IsSuccess())
|
||||
|
||||
/// Evaluates an expression that returns a result, and returns the result if it would fail.
|
||||
#define R_TRY(res_expr) \
|
||||
{ \
|
||||
const auto _tmp_r_try_rc = (res_expr); \
|
||||
if (_tmp_r_try_rc.IsError()) { \
|
||||
return _tmp_r_try_rc; \
|
||||
} \
|
||||
}
|
||||
|
||||
/// Evaluates a boolean expression, and succeeds if that expression is true.
|
||||
#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), RESULT_SUCCESS)
|
||||
|
||||
namespace Common {
|
||||
|
||||
[[nodiscard]] constexpr u32 MakeMagic(char a, char b, char c, char d) {
|
||||
|
||||
@@ -183,10 +183,6 @@ size_t WriteStringToFile(const std::filesystem::path& path, FileType type,
|
||||
|
||||
size_t AppendStringToFile(const std::filesystem::path& path, FileType type,
|
||||
std::string_view string) {
|
||||
if (!Exists(path)) {
|
||||
return WriteStringToFile(path, type, string);
|
||||
}
|
||||
|
||||
if (!IsFile(path)) {
|
||||
return 0;
|
||||
}
|
||||
@@ -309,7 +305,11 @@ bool IOFile::Flush() const {
|
||||
|
||||
errno = 0;
|
||||
|
||||
const auto flush_result = std::fflush(file) == 0;
|
||||
#ifdef _WIN32
|
||||
const auto flush_result = std::fflush(file) == 0 && _commit(fileno(file)) == 0;
|
||||
#else
|
||||
const auto flush_result = std::fflush(file) == 0 && fsync(fileno(file)) == 0;
|
||||
#endif
|
||||
|
||||
if (!flush_result) {
|
||||
const auto ec = std::error_code{errno, std::generic_category()};
|
||||
|
||||
@@ -71,7 +71,7 @@ template <typename Path>
|
||||
|
||||
/**
|
||||
* Writes a string to a file at path and returns the number of characters successfully written.
|
||||
* If an file already exists at path, its contents will be erased.
|
||||
* If a file already exists at path, its contents will be erased.
|
||||
* If the filesystem object at path is not a file, this function returns 0.
|
||||
*
|
||||
* @param path Filesystem path
|
||||
@@ -95,7 +95,6 @@ template <typename Path>
|
||||
|
||||
/**
|
||||
* Appends a string to a file at path and returns the number of characters successfully written.
|
||||
* If a file does not exist at path, WriteStringToFile is called instead.
|
||||
* If the filesystem object at path is not a file, this function returns 0.
|
||||
*
|
||||
* @param path Filesystem path
|
||||
|
||||
@@ -321,7 +321,8 @@ bool RemoveDirContentsRecursively(const fs::path& path) {
|
||||
|
||||
std::error_code ec;
|
||||
|
||||
for (const auto& entry : fs::recursive_directory_iterator(path, ec)) {
|
||||
// TODO (Morph): Replace this with recursive_directory_iterator once it's fixed in MSVC.
|
||||
for (const auto& entry : fs::directory_iterator(path, ec)) {
|
||||
if (ec) {
|
||||
LOG_ERROR(Common_Filesystem,
|
||||
"Failed to completely enumerate the directory at path={}, ec_message={}",
|
||||
@@ -337,6 +338,12 @@ bool RemoveDirContentsRecursively(const fs::path& path) {
|
||||
PathToUTF8String(entry.path()), ec.message());
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO (Morph): Remove this when MSVC fixes recursive_directory_iterator.
|
||||
// recursive_directory_iterator throws an exception despite passing in a std::error_code.
|
||||
if (entry.status().type() == fs::file_type::directory) {
|
||||
return RemoveDirContentsRecursively(entry.path());
|
||||
}
|
||||
}
|
||||
|
||||
if (ec) {
|
||||
@@ -475,7 +482,8 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path,
|
||||
|
||||
std::error_code ec;
|
||||
|
||||
for (const auto& entry : fs::recursive_directory_iterator(path, ec)) {
|
||||
// TODO (Morph): Replace this with recursive_directory_iterator once it's fixed in MSVC.
|
||||
for (const auto& entry : fs::directory_iterator(path, ec)) {
|
||||
if (ec) {
|
||||
break;
|
||||
}
|
||||
@@ -495,6 +503,12 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path,
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO (Morph): Remove this when MSVC fixes recursive_directory_iterator.
|
||||
// recursive_directory_iterator throws an exception despite passing in a std::error_code.
|
||||
if (entry.status().type() == fs::file_type::directory) {
|
||||
IterateDirEntriesRecursively(entry.path(), callback, filter);
|
||||
}
|
||||
}
|
||||
|
||||
if (callback_error || ec) {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "common/fs/fs_util.h"
|
||||
|
||||
namespace Common::FS {
|
||||
@@ -10,4 +12,16 @@ std::u8string ToU8String(std::string_view utf8_string) {
|
||||
return std::u8string{utf8_string.begin(), utf8_string.end()};
|
||||
}
|
||||
|
||||
std::u8string BufferToU8String(std::span<const u8> buffer) {
|
||||
return std::u8string{buffer.begin(), std::ranges::find(buffer, u8{0})};
|
||||
}
|
||||
|
||||
std::string ToUTF8String(std::u8string_view u8_string) {
|
||||
return std::string{u8_string.begin(), u8_string.end()};
|
||||
}
|
||||
|
||||
std::string PathToUTF8String(const std::filesystem::path& path) {
|
||||
return ToUTF8String(path.u8string());
|
||||
}
|
||||
|
||||
} // namespace Common::FS
|
||||
|
||||
@@ -5,9 +5,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <filesystem>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common::FS {
|
||||
|
||||
template <typename T>
|
||||
@@ -22,4 +26,33 @@ concept IsChar = std::same_as<T, char>;
|
||||
*/
|
||||
[[nodiscard]] std::u8string ToU8String(std::string_view utf8_string);
|
||||
|
||||
/**
|
||||
* Converts a buffer of bytes to a UTF8-encoded std::u8string.
|
||||
* This converts from the start of the buffer until the first encountered null-terminator.
|
||||
* If no null-terminator is found, this converts the entire buffer instead.
|
||||
*
|
||||
* @param buffer Buffer of bytes
|
||||
*
|
||||
* @returns UTF-8 encoded std::u8string.
|
||||
*/
|
||||
[[nodiscard]] std::u8string BufferToU8String(std::span<const u8> buffer);
|
||||
|
||||
/**
|
||||
* Converts a std::u8string or std::u8string_view to a UTF-8 encoded std::string.
|
||||
*
|
||||
* @param u8_string UTF-8 encoded u8string
|
||||
*
|
||||
* @returns UTF-8 encoded std::string.
|
||||
*/
|
||||
[[nodiscard]] std::string ToUTF8String(std::u8string_view u8_string);
|
||||
|
||||
/**
|
||||
* Converts a filesystem path to a UTF-8 encoded std::string.
|
||||
*
|
||||
* @param path Filesystem path
|
||||
*
|
||||
* @returns UTF-8 encoded std::string.
|
||||
*/
|
||||
[[nodiscard]] std::string PathToUTF8String(const std::filesystem::path& path);
|
||||
|
||||
} // namespace Common::FS
|
||||
|
||||
@@ -129,12 +129,6 @@ private:
|
||||
std::unordered_map<YuzuPath, fs::path> yuzu_paths;
|
||||
};
|
||||
|
||||
std::string PathToUTF8String(const fs::path& path) {
|
||||
const auto utf8_string = path.u8string();
|
||||
|
||||
return std::string{utf8_string.begin(), utf8_string.end()};
|
||||
}
|
||||
|
||||
bool ValidatePath(const fs::path& path) {
|
||||
if (path.empty()) {
|
||||
LOG_ERROR(Common_Filesystem, "Input path is empty, path={}", PathToUTF8String(path));
|
||||
|
||||
@@ -25,15 +25,6 @@ enum class YuzuPath {
|
||||
ShaderDir, // Where shaders are stored.
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts a filesystem path to a UTF-8 encoded std::string.
|
||||
*
|
||||
* @param path Filesystem path
|
||||
*
|
||||
* @returns UTF-8 encoded std::string.
|
||||
*/
|
||||
[[nodiscard]] std::string PathToUTF8String(const std::filesystem::path& path);
|
||||
|
||||
/**
|
||||
* Validates a given path.
|
||||
*
|
||||
@@ -218,7 +209,7 @@ void SetYuzuPath(YuzuPath yuzu_path, const std::filesystem::path& new_path);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
[[nodiscard]] void SetYuzuPath(YuzuPath yuzu_path, const Path& new_path) {
|
||||
void SetYuzuPath(YuzuPath yuzu_path, const Path& new_path) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
SetYuzuPath(yuzu_path, ToU8String(new_path));
|
||||
} else {
|
||||
|
||||
538
src/common/host_memory.cpp
Normal file
538
src/common/host_memory.cpp
Normal file
@@ -0,0 +1,538 @@
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <iterator>
|
||||
#include <unordered_map>
|
||||
#include <boost/icl/separate_interval_set.hpp>
|
||||
#include <windows.h>
|
||||
#include "common/dynamic_library.h"
|
||||
|
||||
#elif defined(__linux__) // ^^^ Windows ^^^ vvv Linux vvv
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#endif // ^^^ Linux ^^^
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/host_memory.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
constexpr size_t PageAlignment = 0x1000;
|
||||
constexpr size_t HugePageSize = 0x200000;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
// Manually imported for MinGW compatibility
|
||||
#ifndef MEM_RESERVE_PLACEHOLDER
|
||||
#define MEM_RESERVE_PLACEHOLDER 0x00040000
|
||||
#endif
|
||||
#ifndef MEM_REPLACE_PLACEHOLDER
|
||||
#define MEM_REPLACE_PLACEHOLDER 0x00004000
|
||||
#endif
|
||||
#ifndef MEM_COALESCE_PLACEHOLDERS
|
||||
#define MEM_COALESCE_PLACEHOLDERS 0x00000001
|
||||
#endif
|
||||
#ifndef MEM_PRESERVE_PLACEHOLDER
|
||||
#define MEM_PRESERVE_PLACEHOLDER 0x00000002
|
||||
#endif
|
||||
|
||||
using PFN_CreateFileMapping2 = _Ret_maybenull_ HANDLE(WINAPI*)(
|
||||
_In_ HANDLE File, _In_opt_ SECURITY_ATTRIBUTES* SecurityAttributes, _In_ ULONG DesiredAccess,
|
||||
_In_ ULONG PageProtection, _In_ ULONG AllocationAttributes, _In_ ULONG64 MaximumSize,
|
||||
_In_opt_ PCWSTR Name,
|
||||
_Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters,
|
||||
_In_ ULONG ParameterCount);
|
||||
|
||||
using PFN_VirtualAlloc2 = _Ret_maybenull_ PVOID(WINAPI*)(
|
||||
_In_opt_ HANDLE Process, _In_opt_ PVOID BaseAddress, _In_ SIZE_T Size,
|
||||
_In_ ULONG AllocationType, _In_ ULONG PageProtection,
|
||||
_Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters,
|
||||
_In_ ULONG ParameterCount);
|
||||
|
||||
using PFN_MapViewOfFile3 = _Ret_maybenull_ PVOID(WINAPI*)(
|
||||
_In_ HANDLE FileMapping, _In_opt_ HANDLE Process, _In_opt_ PVOID BaseAddress,
|
||||
_In_ ULONG64 Offset, _In_ SIZE_T ViewSize, _In_ ULONG AllocationType, _In_ ULONG PageProtection,
|
||||
_Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters,
|
||||
_In_ ULONG ParameterCount);
|
||||
|
||||
using PFN_UnmapViewOfFile2 = BOOL(WINAPI*)(_In_ HANDLE Process, _In_ PVOID BaseAddress,
|
||||
_In_ ULONG UnmapFlags);
|
||||
|
||||
template <typename T>
|
||||
static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn) {
|
||||
if (!dll.GetSymbol(name, &pfn)) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to load {}", name);
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
}
|
||||
|
||||
class HostMemory::Impl {
|
||||
public:
|
||||
explicit Impl(size_t backing_size_, size_t virtual_size_)
|
||||
: backing_size{backing_size_}, virtual_size{virtual_size_}, process{GetCurrentProcess()},
|
||||
kernelbase_dll("Kernelbase") {
|
||||
if (!kernelbase_dll.IsOpen()) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to load Kernelbase.dll");
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
GetFuncAddress(kernelbase_dll, "CreateFileMapping2", pfn_CreateFileMapping2);
|
||||
GetFuncAddress(kernelbase_dll, "VirtualAlloc2", pfn_VirtualAlloc2);
|
||||
GetFuncAddress(kernelbase_dll, "MapViewOfFile3", pfn_MapViewOfFile3);
|
||||
GetFuncAddress(kernelbase_dll, "UnmapViewOfFile2", pfn_UnmapViewOfFile2);
|
||||
|
||||
// Allocate backing file map
|
||||
backing_handle =
|
||||
pfn_CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_WRITE | FILE_MAP_READ,
|
||||
PAGE_READWRITE, SEC_COMMIT, backing_size, nullptr, nullptr, 0);
|
||||
if (!backing_handle) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to allocate {} MiB of backing memory",
|
||||
backing_size >> 20);
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
// Allocate a virtual memory for the backing file map as placeholder
|
||||
backing_base = static_cast<u8*>(pfn_VirtualAlloc2(process, nullptr, backing_size,
|
||||
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
|
||||
PAGE_NOACCESS, nullptr, 0));
|
||||
if (!backing_base) {
|
||||
Release();
|
||||
LOG_CRITICAL(HW_Memory, "Failed to reserve {} MiB of virtual memory",
|
||||
backing_size >> 20);
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
// Map backing placeholder
|
||||
void* const ret = pfn_MapViewOfFile3(backing_handle, process, backing_base, 0, backing_size,
|
||||
MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0);
|
||||
if (ret != backing_base) {
|
||||
Release();
|
||||
LOG_CRITICAL(HW_Memory, "Failed to map {} MiB of virtual memory", backing_size >> 20);
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
// Allocate virtual address placeholder
|
||||
virtual_base = static_cast<u8*>(pfn_VirtualAlloc2(process, nullptr, virtual_size,
|
||||
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
|
||||
PAGE_NOACCESS, nullptr, 0));
|
||||
if (!virtual_base) {
|
||||
Release();
|
||||
LOG_CRITICAL(HW_Memory, "Failed to reserve {} GiB of virtual memory",
|
||||
virtual_size >> 30);
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
}
|
||||
|
||||
~Impl() {
|
||||
Release();
|
||||
}
|
||||
|
||||
void Map(size_t virtual_offset, size_t host_offset, size_t length) {
|
||||
std::unique_lock lock{placeholder_mutex};
|
||||
if (!IsNiechePlaceholder(virtual_offset, length)) {
|
||||
Split(virtual_offset, length);
|
||||
}
|
||||
ASSERT(placeholders.find({virtual_offset, virtual_offset + length}) == placeholders.end());
|
||||
TrackPlaceholder(virtual_offset, host_offset, length);
|
||||
|
||||
MapView(virtual_offset, host_offset, length);
|
||||
}
|
||||
|
||||
void Unmap(size_t virtual_offset, size_t length) {
|
||||
std::lock_guard lock{placeholder_mutex};
|
||||
|
||||
// Unmap until there are no more placeholders
|
||||
while (UnmapOnePlaceholder(virtual_offset, length)) {
|
||||
}
|
||||
}
|
||||
|
||||
void Protect(size_t virtual_offset, size_t length, bool read, bool write) {
|
||||
DWORD new_flags{};
|
||||
if (read && write) {
|
||||
new_flags = PAGE_READWRITE;
|
||||
} else if (read && !write) {
|
||||
new_flags = PAGE_READONLY;
|
||||
} else if (!read && !write) {
|
||||
new_flags = PAGE_NOACCESS;
|
||||
} else {
|
||||
UNIMPLEMENTED_MSG("Protection flag combination read={} write={}", read, write);
|
||||
}
|
||||
const size_t virtual_end = virtual_offset + length;
|
||||
|
||||
std::lock_guard lock{placeholder_mutex};
|
||||
auto [it, end] = placeholders.equal_range({virtual_offset, virtual_end});
|
||||
while (it != end) {
|
||||
const size_t offset = std::max(it->lower(), virtual_offset);
|
||||
const size_t protect_length = std::min(it->upper(), virtual_end) - offset;
|
||||
DWORD old_flags{};
|
||||
if (!VirtualProtect(virtual_base + offset, protect_length, new_flags, &old_flags)) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to change virtual memory protect rules");
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
const size_t backing_size; ///< Size of the backing memory in bytes
|
||||
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
|
||||
|
||||
u8* backing_base{};
|
||||
u8* virtual_base{};
|
||||
|
||||
private:
|
||||
/// Release all resources in the object
|
||||
void Release() {
|
||||
if (!placeholders.empty()) {
|
||||
for (const auto& placeholder : placeholders) {
|
||||
if (!pfn_UnmapViewOfFile2(process, virtual_base + placeholder.lower(),
|
||||
MEM_PRESERVE_PLACEHOLDER)) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to unmap virtual memory placeholder");
|
||||
}
|
||||
}
|
||||
Coalesce(0, virtual_size);
|
||||
}
|
||||
if (virtual_base) {
|
||||
if (!VirtualFree(virtual_base, 0, MEM_RELEASE)) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to free virtual memory");
|
||||
}
|
||||
}
|
||||
if (backing_base) {
|
||||
if (!pfn_UnmapViewOfFile2(process, backing_base, MEM_PRESERVE_PLACEHOLDER)) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to unmap backing memory placeholder");
|
||||
}
|
||||
if (!VirtualFreeEx(process, backing_base, 0, MEM_RELEASE)) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to free backing memory");
|
||||
}
|
||||
}
|
||||
if (!CloseHandle(backing_handle)) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to free backing memory file handle");
|
||||
}
|
||||
}
|
||||
|
||||
/// Unmap one placeholder in the given range (partial unmaps are supported)
|
||||
/// Return true when there are no more placeholders to unmap
|
||||
bool UnmapOnePlaceholder(size_t virtual_offset, size_t length) {
|
||||
const auto it = placeholders.find({virtual_offset, virtual_offset + length});
|
||||
const auto begin = placeholders.begin();
|
||||
const auto end = placeholders.end();
|
||||
if (it == end) {
|
||||
return false;
|
||||
}
|
||||
const size_t placeholder_begin = it->lower();
|
||||
const size_t placeholder_end = it->upper();
|
||||
const size_t unmap_begin = std::max(virtual_offset, placeholder_begin);
|
||||
const size_t unmap_end = std::min(virtual_offset + length, placeholder_end);
|
||||
ASSERT(unmap_begin >= placeholder_begin && unmap_begin < placeholder_end);
|
||||
ASSERT(unmap_end <= placeholder_end && unmap_end > placeholder_begin);
|
||||
|
||||
const auto host_pointer_it = placeholder_host_pointers.find(placeholder_begin);
|
||||
ASSERT(host_pointer_it != placeholder_host_pointers.end());
|
||||
const size_t host_offset = host_pointer_it->second;
|
||||
|
||||
const bool split_left = unmap_begin > placeholder_begin;
|
||||
const bool split_right = unmap_end < placeholder_end;
|
||||
|
||||
if (!pfn_UnmapViewOfFile2(process, virtual_base + placeholder_begin,
|
||||
MEM_PRESERVE_PLACEHOLDER)) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to unmap placeholder");
|
||||
}
|
||||
// If we have to remap memory regions due to partial unmaps, we are in a data race as
|
||||
// Windows doesn't support remapping memory without unmapping first. Avoid adding any extra
|
||||
// logic within the panic region described below.
|
||||
|
||||
// Panic region, we are in a data race right now
|
||||
if (split_left || split_right) {
|
||||
Split(unmap_begin, unmap_end - unmap_begin);
|
||||
}
|
||||
if (split_left) {
|
||||
MapView(placeholder_begin, host_offset, unmap_begin - placeholder_begin);
|
||||
}
|
||||
if (split_right) {
|
||||
MapView(unmap_end, host_offset + unmap_end - placeholder_begin,
|
||||
placeholder_end - unmap_end);
|
||||
}
|
||||
// End panic region
|
||||
|
||||
size_t coalesce_begin = unmap_begin;
|
||||
if (!split_left) {
|
||||
// Try to coalesce pages to the left
|
||||
coalesce_begin = it == begin ? 0 : std::prev(it)->upper();
|
||||
if (coalesce_begin != placeholder_begin) {
|
||||
Coalesce(coalesce_begin, unmap_end - coalesce_begin);
|
||||
}
|
||||
}
|
||||
if (!split_right) {
|
||||
// Try to coalesce pages to the right
|
||||
const auto next = std::next(it);
|
||||
const size_t next_begin = next == end ? virtual_size : next->lower();
|
||||
if (placeholder_end != next_begin) {
|
||||
// We can coalesce to the right
|
||||
Coalesce(coalesce_begin, next_begin - coalesce_begin);
|
||||
}
|
||||
}
|
||||
// Remove and reinsert placeholder trackers
|
||||
UntrackPlaceholder(it);
|
||||
if (split_left) {
|
||||
TrackPlaceholder(placeholder_begin, host_offset, unmap_begin - placeholder_begin);
|
||||
}
|
||||
if (split_right) {
|
||||
TrackPlaceholder(unmap_end, host_offset + unmap_end - placeholder_begin,
|
||||
placeholder_end - unmap_end);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MapView(size_t virtual_offset, size_t host_offset, size_t length) {
|
||||
if (!pfn_MapViewOfFile3(backing_handle, process, virtual_base + virtual_offset, host_offset,
|
||||
length, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0)) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to map placeholder");
|
||||
}
|
||||
}
|
||||
|
||||
void Split(size_t virtual_offset, size_t length) {
|
||||
if (!VirtualFreeEx(process, reinterpret_cast<LPVOID>(virtual_base + virtual_offset), length,
|
||||
MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER)) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to split placeholder");
|
||||
}
|
||||
}
|
||||
|
||||
void Coalesce(size_t virtual_offset, size_t length) {
|
||||
if (!VirtualFreeEx(process, reinterpret_cast<LPVOID>(virtual_base + virtual_offset), length,
|
||||
MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS)) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to coalesce placeholders");
|
||||
}
|
||||
}
|
||||
|
||||
void TrackPlaceholder(size_t virtual_offset, size_t host_offset, size_t length) {
|
||||
placeholders.insert({virtual_offset, virtual_offset + length});
|
||||
placeholder_host_pointers.emplace(virtual_offset, host_offset);
|
||||
}
|
||||
|
||||
void UntrackPlaceholder(boost::icl::separate_interval_set<size_t>::iterator it) {
|
||||
placeholders.erase(it);
|
||||
placeholder_host_pointers.erase(it->lower());
|
||||
}
|
||||
|
||||
/// Return true when a given memory region is a "nieche" and the placeholders don't have to be
|
||||
/// splitted.
|
||||
bool IsNiechePlaceholder(size_t virtual_offset, size_t length) const {
|
||||
const auto it = placeholders.upper_bound({virtual_offset, virtual_offset + length});
|
||||
if (it != placeholders.end() && it->lower() == virtual_offset + length) {
|
||||
const bool is_root = it == placeholders.begin() && virtual_offset == 0;
|
||||
return is_root || std::prev(it)->upper() == virtual_offset;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
HANDLE process{}; ///< Current process handle
|
||||
HANDLE backing_handle{}; ///< File based backing memory
|
||||
|
||||
DynamicLibrary kernelbase_dll;
|
||||
PFN_CreateFileMapping2 pfn_CreateFileMapping2{};
|
||||
PFN_VirtualAlloc2 pfn_VirtualAlloc2{};
|
||||
PFN_MapViewOfFile3 pfn_MapViewOfFile3{};
|
||||
PFN_UnmapViewOfFile2 pfn_UnmapViewOfFile2{};
|
||||
|
||||
std::mutex placeholder_mutex; ///< Mutex for placeholders
|
||||
boost::icl::separate_interval_set<size_t> placeholders; ///< Mapped placeholders
|
||||
std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset
|
||||
};
|
||||
|
||||
#elif defined(__linux__) // ^^^ Windows ^^^ vvv Linux vvv
|
||||
|
||||
class HostMemory::Impl {
|
||||
public:
|
||||
explicit Impl(size_t backing_size_, size_t virtual_size_)
|
||||
: backing_size{backing_size_}, virtual_size{virtual_size_} {
|
||||
bool good = false;
|
||||
SCOPE_EXIT({
|
||||
if (!good) {
|
||||
Release();
|
||||
}
|
||||
});
|
||||
|
||||
// Backing memory initialization
|
||||
fd = memfd_create("HostMemory", 0);
|
||||
if (fd == -1) {
|
||||
LOG_CRITICAL(HW_Memory, "memfd_create failed: {}", strerror(errno));
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
|
||||
// Defined to extend the file with zeros
|
||||
int ret = ftruncate(fd, backing_size);
|
||||
if (ret != 0) {
|
||||
LOG_CRITICAL(HW_Memory, "ftruncate failed with {}, are you out-of-memory?",
|
||||
strerror(errno));
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
|
||||
backing_base = static_cast<u8*>(
|
||||
mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
|
||||
if (backing_base == MAP_FAILED) {
|
||||
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
|
||||
// Virtual memory initialization
|
||||
virtual_base = static_cast<u8*>(
|
||||
mmap(nullptr, virtual_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
if (virtual_base == MAP_FAILED) {
|
||||
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
|
||||
good = true;
|
||||
}
|
||||
|
||||
~Impl() {
|
||||
Release();
|
||||
}
|
||||
|
||||
void Map(size_t virtual_offset, size_t host_offset, size_t length) {
|
||||
|
||||
void* ret = mmap(virtual_base + virtual_offset, length, PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED | MAP_FIXED, fd, host_offset);
|
||||
ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
|
||||
}
|
||||
|
||||
void Unmap(size_t virtual_offset, size_t length) {
|
||||
// The method name is wrong. We're still talking about the virtual range.
|
||||
// We don't want to unmap, we want to reserve this memory.
|
||||
|
||||
void* ret = mmap(virtual_base + virtual_offset, length, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
|
||||
ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
|
||||
}
|
||||
|
||||
void Protect(size_t virtual_offset, size_t length, bool read, bool write) {
|
||||
int flags = 0;
|
||||
if (read) {
|
||||
flags |= PROT_READ;
|
||||
}
|
||||
if (write) {
|
||||
flags |= PROT_WRITE;
|
||||
}
|
||||
int ret = mprotect(virtual_base + virtual_offset, length, flags);
|
||||
ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno));
|
||||
}
|
||||
|
||||
const size_t backing_size; ///< Size of the backing memory in bytes
|
||||
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
|
||||
|
||||
u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)};
|
||||
u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)};
|
||||
|
||||
private:
|
||||
/// Release all resources in the object
|
||||
void Release() {
|
||||
if (virtual_base != MAP_FAILED) {
|
||||
int ret = munmap(virtual_base, virtual_size);
|
||||
ASSERT_MSG(ret == 0, "munmap failed: {}", strerror(errno));
|
||||
}
|
||||
|
||||
if (backing_base != MAP_FAILED) {
|
||||
int ret = munmap(backing_base, backing_size);
|
||||
ASSERT_MSG(ret == 0, "munmap failed: {}", strerror(errno));
|
||||
}
|
||||
|
||||
if (fd != -1) {
|
||||
int ret = close(fd);
|
||||
ASSERT_MSG(ret == 0, "close failed: {}", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create
|
||||
};
|
||||
|
||||
#else // ^^^ Linux ^^^ vvv Generic vvv
|
||||
|
||||
class HostMemory::Impl {
|
||||
public:
|
||||
explicit Impl(size_t /*backing_size */, size_t /* virtual_size */) {
|
||||
// This is just a place holder.
|
||||
// Please implement fastmem in a propper way on your platform.
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
|
||||
void Map(size_t virtual_offset, size_t host_offset, size_t length) {}
|
||||
|
||||
void Unmap(size_t virtual_offset, size_t length) {}
|
||||
|
||||
void Protect(size_t virtual_offset, size_t length, bool read, bool write) {}
|
||||
|
||||
u8* backing_base{nullptr};
|
||||
u8* virtual_base{nullptr};
|
||||
};
|
||||
|
||||
#endif // ^^^ Generic ^^^
|
||||
|
||||
HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
|
||||
: backing_size(backing_size_), virtual_size(virtual_size_) {
|
||||
try {
|
||||
// Try to allocate a fastmem arena.
|
||||
// The implementation will fail with std::bad_alloc on errors.
|
||||
impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment),
|
||||
AlignUp(virtual_size, PageAlignment) +
|
||||
3 * HugePageSize);
|
||||
backing_base = impl->backing_base;
|
||||
virtual_base = impl->virtual_base;
|
||||
|
||||
if (virtual_base) {
|
||||
virtual_base += 2 * HugePageSize - 1;
|
||||
virtual_base -= reinterpret_cast<size_t>(virtual_base) & (HugePageSize - 1);
|
||||
virtual_base_offset = virtual_base - impl->virtual_base;
|
||||
}
|
||||
|
||||
} catch (const std::bad_alloc&) {
|
||||
LOG_CRITICAL(HW_Memory,
|
||||
"Fastmem unavailable, falling back to VirtualBuffer for memory allocation");
|
||||
fallback_buffer = std::make_unique<Common::VirtualBuffer<u8>>(backing_size);
|
||||
backing_base = fallback_buffer->data();
|
||||
virtual_base = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
HostMemory::~HostMemory() = default;
|
||||
|
||||
HostMemory::HostMemory(HostMemory&&) noexcept = default;
|
||||
|
||||
HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default;
|
||||
|
||||
void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length) {
|
||||
ASSERT(virtual_offset % PageAlignment == 0);
|
||||
ASSERT(host_offset % PageAlignment == 0);
|
||||
ASSERT(length % PageAlignment == 0);
|
||||
ASSERT(virtual_offset + length <= virtual_size);
|
||||
ASSERT(host_offset + length <= backing_size);
|
||||
if (length == 0 || !virtual_base || !impl) {
|
||||
return;
|
||||
}
|
||||
impl->Map(virtual_offset + virtual_base_offset, host_offset, length);
|
||||
}
|
||||
|
||||
void HostMemory::Unmap(size_t virtual_offset, size_t length) {
|
||||
ASSERT(virtual_offset % PageAlignment == 0);
|
||||
ASSERT(length % PageAlignment == 0);
|
||||
ASSERT(virtual_offset + length <= virtual_size);
|
||||
if (length == 0 || !virtual_base || !impl) {
|
||||
return;
|
||||
}
|
||||
impl->Unmap(virtual_offset + virtual_base_offset, length);
|
||||
}
|
||||
|
||||
void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write) {
|
||||
ASSERT(virtual_offset % PageAlignment == 0);
|
||||
ASSERT(length % PageAlignment == 0);
|
||||
ASSERT(virtual_offset + length <= virtual_size);
|
||||
if (length == 0 || !virtual_base || !impl) {
|
||||
return;
|
||||
}
|
||||
impl->Protect(virtual_offset + virtual_base_offset, length, read, write);
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
70
src/common/host_memory.h
Normal file
70
src/common/host_memory.h
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "common/common_types.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
/**
|
||||
* A low level linear memory buffer, which supports multiple mappings
|
||||
* Its purpose is to rebuild a given sparse memory layout, including mirrors.
|
||||
*/
|
||||
class HostMemory {
|
||||
public:
|
||||
explicit HostMemory(size_t backing_size_, size_t virtual_size_);
|
||||
~HostMemory();
|
||||
|
||||
/**
|
||||
* Copy constructors. They shall return a copy of the buffer without the mappings.
|
||||
* TODO: Implement them with COW if needed.
|
||||
*/
|
||||
HostMemory(const HostMemory& other) = delete;
|
||||
HostMemory& operator=(const HostMemory& other) = delete;
|
||||
|
||||
/**
|
||||
* Move constructors. They will move the buffer and the mappings to the new object.
|
||||
*/
|
||||
HostMemory(HostMemory&& other) noexcept;
|
||||
HostMemory& operator=(HostMemory&& other) noexcept;
|
||||
|
||||
void Map(size_t virtual_offset, size_t host_offset, size_t length);
|
||||
|
||||
void Unmap(size_t virtual_offset, size_t length);
|
||||
|
||||
void Protect(size_t virtual_offset, size_t length, bool read, bool write);
|
||||
|
||||
[[nodiscard]] u8* BackingBasePointer() noexcept {
|
||||
return backing_base;
|
||||
}
|
||||
[[nodiscard]] const u8* BackingBasePointer() const noexcept {
|
||||
return backing_base;
|
||||
}
|
||||
|
||||
[[nodiscard]] u8* VirtualBasePointer() noexcept {
|
||||
return virtual_base;
|
||||
}
|
||||
[[nodiscard]] const u8* VirtualBasePointer() const noexcept {
|
||||
return virtual_base;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t backing_size{};
|
||||
size_t virtual_size{};
|
||||
|
||||
// Low level handler for the platform dependent memory routines
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
u8* backing_base{};
|
||||
u8* virtual_base{};
|
||||
size_t virtual_base_offset{};
|
||||
|
||||
// Fallback if fastmem is not supported on this platform
|
||||
std::unique_ptr<Common::VirtualBuffer<u8>> fallback_buffer;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -17,6 +17,7 @@
|
||||
#endif
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/fs/file.h"
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/log.h"
|
||||
@@ -140,10 +141,14 @@ private:
|
||||
std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
|
||||
};
|
||||
|
||||
ConsoleBackend::~ConsoleBackend() = default;
|
||||
|
||||
void ConsoleBackend::Write(const Entry& entry) {
|
||||
PrintMessage(entry);
|
||||
}
|
||||
|
||||
ColorConsoleBackend::~ColorConsoleBackend() = default;
|
||||
|
||||
void ColorConsoleBackend::Write(const Entry& entry) {
|
||||
PrintColoredMessage(entry);
|
||||
}
|
||||
@@ -157,16 +162,19 @@ FileBackend::FileBackend(const std::filesystem::path& filename) {
|
||||
void(FS::RemoveFile(old_filename));
|
||||
void(FS::RenameFile(filename, old_filename));
|
||||
|
||||
file = FS::IOFile(filename, FS::FileAccessMode::Write, FS::FileType::TextFile);
|
||||
file =
|
||||
std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write, FS::FileType::TextFile);
|
||||
}
|
||||
|
||||
FileBackend::~FileBackend() = default;
|
||||
|
||||
void FileBackend::Write(const Entry& entry) {
|
||||
// prevent logs from going over the maximum size (in case its spamming and the user doesn't
|
||||
// know)
|
||||
constexpr std::size_t MAX_BYTES_WRITTEN = 100 * 1024 * 1024;
|
||||
constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1024 * 1024 * 1024;
|
||||
|
||||
if (!file.IsOpen()) {
|
||||
if (!file->IsOpen()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -176,147 +184,20 @@ void FileBackend::Write(const Entry& entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n'));
|
||||
bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n'));
|
||||
if (entry.log_level >= Level::Error) {
|
||||
void(file.Flush());
|
||||
void(file->Flush());
|
||||
}
|
||||
}
|
||||
|
||||
DebuggerBackend::~DebuggerBackend() = default;
|
||||
|
||||
void DebuggerBackend::Write(const Entry& entry) {
|
||||
#ifdef _WIN32
|
||||
::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
|
||||
#define ALL_LOG_CLASSES() \
|
||||
CLS(Log) \
|
||||
CLS(Common) \
|
||||
SUB(Common, Filesystem) \
|
||||
SUB(Common, Memory) \
|
||||
CLS(Core) \
|
||||
SUB(Core, ARM) \
|
||||
SUB(Core, Timing) \
|
||||
CLS(Config) \
|
||||
CLS(Debug) \
|
||||
SUB(Debug, Emulated) \
|
||||
SUB(Debug, GPU) \
|
||||
SUB(Debug, Breakpoint) \
|
||||
SUB(Debug, GDBStub) \
|
||||
CLS(Kernel) \
|
||||
SUB(Kernel, SVC) \
|
||||
CLS(Service) \
|
||||
SUB(Service, ACC) \
|
||||
SUB(Service, Audio) \
|
||||
SUB(Service, AM) \
|
||||
SUB(Service, AOC) \
|
||||
SUB(Service, APM) \
|
||||
SUB(Service, ARP) \
|
||||
SUB(Service, BCAT) \
|
||||
SUB(Service, BPC) \
|
||||
SUB(Service, BGTC) \
|
||||
SUB(Service, BTDRV) \
|
||||
SUB(Service, BTM) \
|
||||
SUB(Service, Capture) \
|
||||
SUB(Service, ERPT) \
|
||||
SUB(Service, ETicket) \
|
||||
SUB(Service, EUPLD) \
|
||||
SUB(Service, Fatal) \
|
||||
SUB(Service, FGM) \
|
||||
SUB(Service, Friend) \
|
||||
SUB(Service, FS) \
|
||||
SUB(Service, GRC) \
|
||||
SUB(Service, HID) \
|
||||
SUB(Service, IRS) \
|
||||
SUB(Service, LBL) \
|
||||
SUB(Service, LDN) \
|
||||
SUB(Service, LDR) \
|
||||
SUB(Service, LM) \
|
||||
SUB(Service, Migration) \
|
||||
SUB(Service, Mii) \
|
||||
SUB(Service, MM) \
|
||||
SUB(Service, NCM) \
|
||||
SUB(Service, NFC) \
|
||||
SUB(Service, NFP) \
|
||||
SUB(Service, NIFM) \
|
||||
SUB(Service, NIM) \
|
||||
SUB(Service, NPNS) \
|
||||
SUB(Service, NS) \
|
||||
SUB(Service, NVDRV) \
|
||||
SUB(Service, OLSC) \
|
||||
SUB(Service, PCIE) \
|
||||
SUB(Service, PCTL) \
|
||||
SUB(Service, PCV) \
|
||||
SUB(Service, PM) \
|
||||
SUB(Service, PREPO) \
|
||||
SUB(Service, PSC) \
|
||||
SUB(Service, PSM) \
|
||||
SUB(Service, SET) \
|
||||
SUB(Service, SM) \
|
||||
SUB(Service, SPL) \
|
||||
SUB(Service, SSL) \
|
||||
SUB(Service, TCAP) \
|
||||
SUB(Service, Time) \
|
||||
SUB(Service, USB) \
|
||||
SUB(Service, VI) \
|
||||
SUB(Service, WLAN) \
|
||||
CLS(HW) \
|
||||
SUB(HW, Memory) \
|
||||
SUB(HW, LCD) \
|
||||
SUB(HW, GPU) \
|
||||
SUB(HW, AES) \
|
||||
CLS(IPC) \
|
||||
CLS(Frontend) \
|
||||
CLS(Render) \
|
||||
SUB(Render, Software) \
|
||||
SUB(Render, OpenGL) \
|
||||
SUB(Render, Vulkan) \
|
||||
CLS(Audio) \
|
||||
SUB(Audio, DSP) \
|
||||
SUB(Audio, Sink) \
|
||||
CLS(Input) \
|
||||
CLS(Network) \
|
||||
CLS(Loader) \
|
||||
CLS(CheatEngine) \
|
||||
CLS(Crypto) \
|
||||
CLS(WebService)
|
||||
|
||||
// GetClassName is a macro defined by Windows.h, grrr...
|
||||
const char* GetLogClassName(Class log_class) {
|
||||
switch (log_class) {
|
||||
#define CLS(x) \
|
||||
case Class::x: \
|
||||
return #x;
|
||||
#define SUB(x, y) \
|
||||
case Class::x##_##y: \
|
||||
return #x "." #y;
|
||||
ALL_LOG_CLASSES()
|
||||
#undef CLS
|
||||
#undef SUB
|
||||
case Class::Count:
|
||||
break;
|
||||
}
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
const char* GetLevelName(Level log_level) {
|
||||
#define LVL(x) \
|
||||
case Level::x: \
|
||||
return #x
|
||||
switch (log_level) {
|
||||
LVL(Trace);
|
||||
LVL(Debug);
|
||||
LVL(Info);
|
||||
LVL(Warning);
|
||||
LVL(Error);
|
||||
LVL(Critical);
|
||||
case Level::Count:
|
||||
break;
|
||||
}
|
||||
#undef LVL
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
void SetGlobalFilter(const Filter& filter) {
|
||||
Impl::Instance().SetGlobalFilter(filter);
|
||||
}
|
||||
|
||||
@@ -1,36 +1,24 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include "common/fs/file.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace Common::FS {
|
||||
class IOFile;
|
||||
}
|
||||
|
||||
namespace Common::Log {
|
||||
|
||||
class Filter;
|
||||
|
||||
/**
|
||||
* A log entry. Log entries are store in a structured format to permit more varied output
|
||||
* formatting on different frontends, as well as facilitating filtering and aggregation.
|
||||
*/
|
||||
struct Entry {
|
||||
std::chrono::microseconds timestamp;
|
||||
Class log_class{};
|
||||
Level log_level{};
|
||||
const char* filename = nullptr;
|
||||
unsigned int line_num = 0;
|
||||
std::string function;
|
||||
std::string message;
|
||||
bool final_entry = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface for logging backends. As loggers can be created and removed at runtime, this can be
|
||||
* used by a frontend for adding a custom logging backend as needed
|
||||
@@ -38,6 +26,7 @@ struct Entry {
|
||||
class Backend {
|
||||
public:
|
||||
virtual ~Backend() = default;
|
||||
|
||||
virtual void SetFilter(const Filter& new_filter) {
|
||||
filter = new_filter;
|
||||
}
|
||||
@@ -53,6 +42,8 @@ private:
|
||||
*/
|
||||
class ConsoleBackend : public Backend {
|
||||
public:
|
||||
~ConsoleBackend() override;
|
||||
|
||||
static const char* Name() {
|
||||
return "console";
|
||||
}
|
||||
@@ -67,6 +58,8 @@ public:
|
||||
*/
|
||||
class ColorConsoleBackend : public Backend {
|
||||
public:
|
||||
~ColorConsoleBackend() override;
|
||||
|
||||
static const char* Name() {
|
||||
return "color_console";
|
||||
}
|
||||
@@ -83,6 +76,7 @@ public:
|
||||
class FileBackend : public Backend {
|
||||
public:
|
||||
explicit FileBackend(const std::filesystem::path& filename);
|
||||
~FileBackend() override;
|
||||
|
||||
static const char* Name() {
|
||||
return "file";
|
||||
@@ -95,7 +89,7 @@ public:
|
||||
void Write(const Entry& entry) override;
|
||||
|
||||
private:
|
||||
FS::IOFile file;
|
||||
std::unique_ptr<FS::IOFile> file;
|
||||
std::size_t bytes_written = 0;
|
||||
};
|
||||
|
||||
@@ -104,6 +98,8 @@ private:
|
||||
*/
|
||||
class DebuggerBackend : public Backend {
|
||||
public:
|
||||
~DebuggerBackend() override;
|
||||
|
||||
static const char* Name() {
|
||||
return "debugger";
|
||||
}
|
||||
@@ -119,17 +115,6 @@ void RemoveBackend(std::string_view backend_name);
|
||||
|
||||
Backend* GetBackend(std::string_view backend_name);
|
||||
|
||||
/**
|
||||
* Returns the name of the passed log class as a C-string. Subclasses are separated by periods
|
||||
* instead of underscores as in the enumeration.
|
||||
*/
|
||||
const char* GetLogClassName(Class log_class);
|
||||
|
||||
/**
|
||||
* Returns the name of the passed log level as a C-string.
|
||||
*/
|
||||
const char* GetLevelName(Level log_level);
|
||||
|
||||
/**
|
||||
* The global filter will prevent any messages from even being processed if they are filtered. Each
|
||||
* backend can have a filter, but if the level is lower than the global filter, the backend will
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/string_util.h"
|
||||
|
||||
@@ -22,7 +21,7 @@ Level GetLevelByName(const It begin, const It end) {
|
||||
|
||||
template <typename It>
|
||||
Class GetClassByName(const It begin, const It end) {
|
||||
for (ClassType i = 0; i < static_cast<ClassType>(Class::Count); ++i) {
|
||||
for (u8 i = 0; i < static_cast<u8>(Class::Count); ++i) {
|
||||
const char* level_name = GetLogClassName(static_cast<Class>(i));
|
||||
if (Common::ComparePartialString(begin, end, level_name)) {
|
||||
return static_cast<Class>(i);
|
||||
@@ -62,6 +61,135 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
|
||||
#define ALL_LOG_CLASSES() \
|
||||
CLS(Log) \
|
||||
CLS(Common) \
|
||||
SUB(Common, Filesystem) \
|
||||
SUB(Common, Memory) \
|
||||
CLS(Core) \
|
||||
SUB(Core, ARM) \
|
||||
SUB(Core, Timing) \
|
||||
CLS(Config) \
|
||||
CLS(Debug) \
|
||||
SUB(Debug, Emulated) \
|
||||
SUB(Debug, GPU) \
|
||||
SUB(Debug, Breakpoint) \
|
||||
SUB(Debug, GDBStub) \
|
||||
CLS(Kernel) \
|
||||
SUB(Kernel, SVC) \
|
||||
CLS(Service) \
|
||||
SUB(Service, ACC) \
|
||||
SUB(Service, Audio) \
|
||||
SUB(Service, AM) \
|
||||
SUB(Service, AOC) \
|
||||
SUB(Service, APM) \
|
||||
SUB(Service, ARP) \
|
||||
SUB(Service, BCAT) \
|
||||
SUB(Service, BPC) \
|
||||
SUB(Service, BGTC) \
|
||||
SUB(Service, BTDRV) \
|
||||
SUB(Service, BTM) \
|
||||
SUB(Service, Capture) \
|
||||
SUB(Service, ERPT) \
|
||||
SUB(Service, ETicket) \
|
||||
SUB(Service, EUPLD) \
|
||||
SUB(Service, Fatal) \
|
||||
SUB(Service, FGM) \
|
||||
SUB(Service, Friend) \
|
||||
SUB(Service, FS) \
|
||||
SUB(Service, GRC) \
|
||||
SUB(Service, HID) \
|
||||
SUB(Service, IRS) \
|
||||
SUB(Service, LBL) \
|
||||
SUB(Service, LDN) \
|
||||
SUB(Service, LDR) \
|
||||
SUB(Service, LM) \
|
||||
SUB(Service, Migration) \
|
||||
SUB(Service, Mii) \
|
||||
SUB(Service, MM) \
|
||||
SUB(Service, NCM) \
|
||||
SUB(Service, NFC) \
|
||||
SUB(Service, NFP) \
|
||||
SUB(Service, NIFM) \
|
||||
SUB(Service, NIM) \
|
||||
SUB(Service, NPNS) \
|
||||
SUB(Service, NS) \
|
||||
SUB(Service, NVDRV) \
|
||||
SUB(Service, OLSC) \
|
||||
SUB(Service, PCIE) \
|
||||
SUB(Service, PCTL) \
|
||||
SUB(Service, PCV) \
|
||||
SUB(Service, PM) \
|
||||
SUB(Service, PREPO) \
|
||||
SUB(Service, PSC) \
|
||||
SUB(Service, PSM) \
|
||||
SUB(Service, SET) \
|
||||
SUB(Service, SM) \
|
||||
SUB(Service, SPL) \
|
||||
SUB(Service, SSL) \
|
||||
SUB(Service, TCAP) \
|
||||
SUB(Service, Time) \
|
||||
SUB(Service, USB) \
|
||||
SUB(Service, VI) \
|
||||
SUB(Service, WLAN) \
|
||||
CLS(HW) \
|
||||
SUB(HW, Memory) \
|
||||
SUB(HW, LCD) \
|
||||
SUB(HW, GPU) \
|
||||
SUB(HW, AES) \
|
||||
CLS(IPC) \
|
||||
CLS(Frontend) \
|
||||
CLS(Render) \
|
||||
SUB(Render, Software) \
|
||||
SUB(Render, OpenGL) \
|
||||
SUB(Render, Vulkan) \
|
||||
CLS(Audio) \
|
||||
SUB(Audio, DSP) \
|
||||
SUB(Audio, Sink) \
|
||||
CLS(Input) \
|
||||
CLS(Network) \
|
||||
CLS(Loader) \
|
||||
CLS(CheatEngine) \
|
||||
CLS(Crypto) \
|
||||
CLS(WebService)
|
||||
|
||||
// GetClassName is a macro defined by Windows.h, grrr...
|
||||
const char* GetLogClassName(Class log_class) {
|
||||
switch (log_class) {
|
||||
#define CLS(x) \
|
||||
case Class::x: \
|
||||
return #x;
|
||||
#define SUB(x, y) \
|
||||
case Class::x##_##y: \
|
||||
return #x "." #y;
|
||||
ALL_LOG_CLASSES()
|
||||
#undef CLS
|
||||
#undef SUB
|
||||
case Class::Count:
|
||||
break;
|
||||
}
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
const char* GetLevelName(Level log_level) {
|
||||
#define LVL(x) \
|
||||
case Level::x: \
|
||||
return #x
|
||||
switch (log_level) {
|
||||
LVL(Trace);
|
||||
LVL(Debug);
|
||||
LVL(Info);
|
||||
LVL(Warning);
|
||||
LVL(Error);
|
||||
LVL(Critical);
|
||||
case Level::Count:
|
||||
break;
|
||||
}
|
||||
#undef LVL
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
Filter::Filter(Level default_level) {
|
||||
ResetAll(default_level);
|
||||
}
|
||||
|
||||
@@ -5,12 +5,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <string_view>
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace Common::Log {
|
||||
|
||||
/**
|
||||
* Returns the name of the passed log class as a C-string. Subclasses are separated by periods
|
||||
* instead of underscores as in the enumeration.
|
||||
*/
|
||||
const char* GetLogClassName(Class log_class);
|
||||
|
||||
/**
|
||||
* Returns the name of the passed log level as a C-string.
|
||||
*/
|
||||
const char* GetLevelName(Level log_level);
|
||||
|
||||
/**
|
||||
* Implements a log message filter which allows different log classes to have different minimum
|
||||
* severity levels. The filter can be changed at runtime and can be parsed from a string to allow
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/types.h"
|
||||
|
||||
namespace Common::Log {
|
||||
|
||||
@@ -18,124 +18,6 @@ constexpr const char* TrimSourcePath(std::string_view source) {
|
||||
return source.data() + idx;
|
||||
}
|
||||
|
||||
/// Specifies the severity or level of detail of the log message.
|
||||
enum class Level : u8 {
|
||||
Trace, ///< Extremely detailed and repetitive debugging information that is likely to
|
||||
///< pollute logs.
|
||||
Debug, ///< Less detailed debugging information.
|
||||
Info, ///< Status information from important points during execution.
|
||||
Warning, ///< Minor or potential problems found during execution of a task.
|
||||
Error, ///< Major problems found during execution of a task that prevent it from being
|
||||
///< completed.
|
||||
Critical, ///< Major problems during execution that threaten the stability of the entire
|
||||
///< application.
|
||||
|
||||
Count ///< Total number of logging levels
|
||||
};
|
||||
|
||||
typedef u8 ClassType;
|
||||
|
||||
/**
|
||||
* Specifies the sub-system that generated the log message.
|
||||
*
|
||||
* @note If you add a new entry here, also add a corresponding one to `ALL_LOG_CLASSES` in
|
||||
* backend.cpp.
|
||||
*/
|
||||
enum class Class : ClassType {
|
||||
Log, ///< Messages about the log system itself
|
||||
Common, ///< Library routines
|
||||
Common_Filesystem, ///< Filesystem interface library
|
||||
Common_Memory, ///< Memory mapping and management functions
|
||||
Core, ///< LLE emulation core
|
||||
Core_ARM, ///< ARM CPU core
|
||||
Core_Timing, ///< CoreTiming functions
|
||||
Config, ///< Emulator configuration (including commandline)
|
||||
Debug, ///< Debugging tools
|
||||
Debug_Emulated, ///< Debug messages from the emulated programs
|
||||
Debug_GPU, ///< GPU debugging tools
|
||||
Debug_Breakpoint, ///< Logging breakpoints and watchpoints
|
||||
Debug_GDBStub, ///< GDB Stub
|
||||
Kernel, ///< The HLE implementation of the CTR kernel
|
||||
Kernel_SVC, ///< Kernel system calls
|
||||
Service, ///< HLE implementation of system services. Each major service
|
||||
///< should have its own subclass.
|
||||
Service_ACC, ///< The ACC (Accounts) service
|
||||
Service_AM, ///< The AM (Applet manager) service
|
||||
Service_AOC, ///< The AOC (AddOn Content) service
|
||||
Service_APM, ///< The APM (Performance) service
|
||||
Service_ARP, ///< The ARP service
|
||||
Service_Audio, ///< The Audio (Audio control) service
|
||||
Service_BCAT, ///< The BCAT service
|
||||
Service_BGTC, ///< The BGTC (Background Task Controller) service
|
||||
Service_BPC, ///< The BPC service
|
||||
Service_BTDRV, ///< The Bluetooth driver service
|
||||
Service_BTM, ///< The BTM service
|
||||
Service_Capture, ///< The capture service
|
||||
Service_ERPT, ///< The error reporting service
|
||||
Service_ETicket, ///< The ETicket service
|
||||
Service_EUPLD, ///< The error upload service
|
||||
Service_Fatal, ///< The Fatal service
|
||||
Service_FGM, ///< The FGM service
|
||||
Service_Friend, ///< The friend service
|
||||
Service_FS, ///< The FS (Filesystem) service
|
||||
Service_GRC, ///< The game recording service
|
||||
Service_HID, ///< The HID (Human interface device) service
|
||||
Service_IRS, ///< The IRS service
|
||||
Service_LBL, ///< The LBL (LCD backlight) service
|
||||
Service_LDN, ///< The LDN (Local domain network) service
|
||||
Service_LDR, ///< The loader service
|
||||
Service_LM, ///< The LM (Logger) service
|
||||
Service_Migration, ///< The migration service
|
||||
Service_Mii, ///< The Mii service
|
||||
Service_MM, ///< The MM (Multimedia) service
|
||||
Service_NCM, ///< The NCM service
|
||||
Service_NFC, ///< The NFC (Near-field communication) service
|
||||
Service_NFP, ///< The NFP service
|
||||
Service_NIFM, ///< The NIFM (Network interface) service
|
||||
Service_NIM, ///< The NIM service
|
||||
Service_NPNS, ///< The NPNS service
|
||||
Service_NS, ///< The NS services
|
||||
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
|
||||
Service_OLSC, ///< The OLSC service
|
||||
Service_PCIE, ///< The PCIe service
|
||||
Service_PCTL, ///< The PCTL (Parental control) service
|
||||
Service_PCV, ///< The PCV service
|
||||
Service_PM, ///< The PM service
|
||||
Service_PREPO, ///< The PREPO (Play report) service
|
||||
Service_PSC, ///< The PSC service
|
||||
Service_PSM, ///< The PSM service
|
||||
Service_SET, ///< The SET (Settings) service
|
||||
Service_SM, ///< The SM (Service manager) service
|
||||
Service_SPL, ///< The SPL service
|
||||
Service_SSL, ///< The SSL service
|
||||
Service_TCAP, ///< The TCAP service.
|
||||
Service_Time, ///< The time service
|
||||
Service_USB, ///< The USB (Universal Serial Bus) service
|
||||
Service_VI, ///< The VI (Video interface) service
|
||||
Service_WLAN, ///< The WLAN (Wireless local area network) service
|
||||
HW, ///< Low-level hardware emulation
|
||||
HW_Memory, ///< Memory-map and address translation
|
||||
HW_LCD, ///< LCD register emulation
|
||||
HW_GPU, ///< GPU control emulation
|
||||
HW_AES, ///< AES engine emulation
|
||||
IPC, ///< IPC interface
|
||||
Frontend, ///< Emulator UI
|
||||
Render, ///< Emulator video output and hardware acceleration
|
||||
Render_Software, ///< Software renderer backend
|
||||
Render_OpenGL, ///< OpenGL backend
|
||||
Render_Vulkan, ///< Vulkan backend
|
||||
Audio, ///< Audio emulation
|
||||
Audio_DSP, ///< The HLE implementation of the DSP
|
||||
Audio_Sink, ///< Emulator audio output backend
|
||||
Loader, ///< ROM loader
|
||||
CheatEngine, ///< Memory manipulation and engine VM functions
|
||||
Crypto, ///< Cryptographic engine/functions
|
||||
Input, ///< Input emulation
|
||||
Network, ///< Network emulation
|
||||
WebService, ///< Interface to yuzu Web Services
|
||||
Count ///< Total number of logging classes
|
||||
};
|
||||
|
||||
/// Logs a message to the global logger, using fmt
|
||||
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
|
||||
unsigned int line_num, const char* function, const char* format,
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/logging/text_formatter.h"
|
||||
#include "common/string_util.h"
|
||||
|
||||
142
src/common/logging/types.h
Normal file
142
src/common/logging/types.h
Normal file
@@ -0,0 +1,142 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common::Log {
|
||||
|
||||
/// Specifies the severity or level of detail of the log message.
|
||||
enum class Level : u8 {
|
||||
Trace, ///< Extremely detailed and repetitive debugging information that is likely to
|
||||
///< pollute logs.
|
||||
Debug, ///< Less detailed debugging information.
|
||||
Info, ///< Status information from important points during execution.
|
||||
Warning, ///< Minor or potential problems found during execution of a task.
|
||||
Error, ///< Major problems found during execution of a task that prevent it from being
|
||||
///< completed.
|
||||
Critical, ///< Major problems during execution that threaten the stability of the entire
|
||||
///< application.
|
||||
|
||||
Count ///< Total number of logging levels
|
||||
};
|
||||
|
||||
/**
|
||||
* Specifies the sub-system that generated the log message.
|
||||
*
|
||||
* @note If you add a new entry here, also add a corresponding one to `ALL_LOG_CLASSES` in
|
||||
* filter.cpp.
|
||||
*/
|
||||
enum class Class : u8 {
|
||||
Log, ///< Messages about the log system itself
|
||||
Common, ///< Library routines
|
||||
Common_Filesystem, ///< Filesystem interface library
|
||||
Common_Memory, ///< Memory mapping and management functions
|
||||
Core, ///< LLE emulation core
|
||||
Core_ARM, ///< ARM CPU core
|
||||
Core_Timing, ///< CoreTiming functions
|
||||
Config, ///< Emulator configuration (including commandline)
|
||||
Debug, ///< Debugging tools
|
||||
Debug_Emulated, ///< Debug messages from the emulated programs
|
||||
Debug_GPU, ///< GPU debugging tools
|
||||
Debug_Breakpoint, ///< Logging breakpoints and watchpoints
|
||||
Debug_GDBStub, ///< GDB Stub
|
||||
Kernel, ///< The HLE implementation of the CTR kernel
|
||||
Kernel_SVC, ///< Kernel system calls
|
||||
Service, ///< HLE implementation of system services. Each major service
|
||||
///< should have its own subclass.
|
||||
Service_ACC, ///< The ACC (Accounts) service
|
||||
Service_AM, ///< The AM (Applet manager) service
|
||||
Service_AOC, ///< The AOC (AddOn Content) service
|
||||
Service_APM, ///< The APM (Performance) service
|
||||
Service_ARP, ///< The ARP service
|
||||
Service_Audio, ///< The Audio (Audio control) service
|
||||
Service_BCAT, ///< The BCAT service
|
||||
Service_BGTC, ///< The BGTC (Background Task Controller) service
|
||||
Service_BPC, ///< The BPC service
|
||||
Service_BTDRV, ///< The Bluetooth driver service
|
||||
Service_BTM, ///< The BTM service
|
||||
Service_Capture, ///< The capture service
|
||||
Service_ERPT, ///< The error reporting service
|
||||
Service_ETicket, ///< The ETicket service
|
||||
Service_EUPLD, ///< The error upload service
|
||||
Service_Fatal, ///< The Fatal service
|
||||
Service_FGM, ///< The FGM service
|
||||
Service_Friend, ///< The friend service
|
||||
Service_FS, ///< The FS (Filesystem) service
|
||||
Service_GRC, ///< The game recording service
|
||||
Service_HID, ///< The HID (Human interface device) service
|
||||
Service_IRS, ///< The IRS service
|
||||
Service_LBL, ///< The LBL (LCD backlight) service
|
||||
Service_LDN, ///< The LDN (Local domain network) service
|
||||
Service_LDR, ///< The loader service
|
||||
Service_LM, ///< The LM (Logger) service
|
||||
Service_Migration, ///< The migration service
|
||||
Service_Mii, ///< The Mii service
|
||||
Service_MM, ///< The MM (Multimedia) service
|
||||
Service_NCM, ///< The NCM service
|
||||
Service_NFC, ///< The NFC (Near-field communication) service
|
||||
Service_NFP, ///< The NFP service
|
||||
Service_NIFM, ///< The NIFM (Network interface) service
|
||||
Service_NIM, ///< The NIM service
|
||||
Service_NPNS, ///< The NPNS service
|
||||
Service_NS, ///< The NS services
|
||||
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
|
||||
Service_OLSC, ///< The OLSC service
|
||||
Service_PCIE, ///< The PCIe service
|
||||
Service_PCTL, ///< The PCTL (Parental control) service
|
||||
Service_PCV, ///< The PCV service
|
||||
Service_PM, ///< The PM service
|
||||
Service_PREPO, ///< The PREPO (Play report) service
|
||||
Service_PSC, ///< The PSC service
|
||||
Service_PSM, ///< The PSM service
|
||||
Service_SET, ///< The SET (Settings) service
|
||||
Service_SM, ///< The SM (Service manager) service
|
||||
Service_SPL, ///< The SPL service
|
||||
Service_SSL, ///< The SSL service
|
||||
Service_TCAP, ///< The TCAP service.
|
||||
Service_Time, ///< The time service
|
||||
Service_USB, ///< The USB (Universal Serial Bus) service
|
||||
Service_VI, ///< The VI (Video interface) service
|
||||
Service_WLAN, ///< The WLAN (Wireless local area network) service
|
||||
HW, ///< Low-level hardware emulation
|
||||
HW_Memory, ///< Memory-map and address translation
|
||||
HW_LCD, ///< LCD register emulation
|
||||
HW_GPU, ///< GPU control emulation
|
||||
HW_AES, ///< AES engine emulation
|
||||
IPC, ///< IPC interface
|
||||
Frontend, ///< Emulator UI
|
||||
Render, ///< Emulator video output and hardware acceleration
|
||||
Render_Software, ///< Software renderer backend
|
||||
Render_OpenGL, ///< OpenGL backend
|
||||
Render_Vulkan, ///< Vulkan backend
|
||||
Audio, ///< Audio emulation
|
||||
Audio_DSP, ///< The HLE implementation of the DSP
|
||||
Audio_Sink, ///< Emulator audio output backend
|
||||
Loader, ///< ROM loader
|
||||
CheatEngine, ///< Memory manipulation and engine VM functions
|
||||
Crypto, ///< Cryptographic engine/functions
|
||||
Input, ///< Input emulation
|
||||
Network, ///< Network emulation
|
||||
WebService, ///< Interface to yuzu Web Services
|
||||
Count ///< Total number of logging classes
|
||||
};
|
||||
|
||||
/**
|
||||
* A log entry. Log entries are store in a structured format to permit more varied output
|
||||
* formatting on different frontends, as well as facilitating filtering and aggregation.
|
||||
*/
|
||||
struct Entry {
|
||||
std::chrono::microseconds timestamp;
|
||||
Class log_class{};
|
||||
Level log_level{};
|
||||
const char* filename = nullptr;
|
||||
unsigned int line_num = 0;
|
||||
std::string function;
|
||||
std::string message;
|
||||
bool final_entry = false;
|
||||
};
|
||||
|
||||
} // namespace Common::Log
|
||||
@@ -14,6 +14,7 @@ void PageTable::Resize(size_t address_space_width_in_bits, size_t page_size_in_b
|
||||
const size_t num_page_table_entries{1ULL << (address_space_width_in_bits - page_size_in_bits)};
|
||||
pointers.resize(num_page_table_entries);
|
||||
backing_addr.resize(num_page_table_entries);
|
||||
current_address_space_width_in_bits = address_space_width_in_bits;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -98,6 +98,10 @@ struct PageTable {
|
||||
*/
|
||||
void Resize(size_t address_space_width_in_bits, size_t page_size_in_bits);
|
||||
|
||||
size_t GetAddressSpaceBits() const {
|
||||
return current_address_space_width_in_bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vector of memory pointers backing each page. An entry can only be non-null if the
|
||||
* corresponding attribute element is of type `Memory`.
|
||||
@@ -105,6 +109,10 @@ struct PageTable {
|
||||
VirtualBuffer<PageInfo> pointers;
|
||||
|
||||
VirtualBuffer<u64> backing_addr;
|
||||
|
||||
size_t current_address_space_width_in_bits;
|
||||
|
||||
u8* fastmem_arena;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
57
src/common/point.h
Normal file
57
src/common/point.h
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace Common {
|
||||
|
||||
// Represents a point within a 2D space.
|
||||
template <typename T>
|
||||
struct Point {
|
||||
static_assert(std::is_arithmetic_v<T>, "T must be an arithmetic type!");
|
||||
|
||||
T x{};
|
||||
T y{};
|
||||
|
||||
#define ARITHMETIC_OP(op, compound_op) \
|
||||
friend constexpr Point operator op(const Point& lhs, const Point& rhs) noexcept { \
|
||||
return { \
|
||||
.x = static_cast<T>(lhs.x op rhs.x), \
|
||||
.y = static_cast<T>(lhs.y op rhs.y), \
|
||||
}; \
|
||||
} \
|
||||
friend constexpr Point operator op(const Point& lhs, T value) noexcept { \
|
||||
return { \
|
||||
.x = static_cast<T>(lhs.x op value), \
|
||||
.y = static_cast<T>(lhs.y op value), \
|
||||
}; \
|
||||
} \
|
||||
friend constexpr Point operator op(T value, const Point& rhs) noexcept { \
|
||||
return { \
|
||||
.x = static_cast<T>(value op rhs.x), \
|
||||
.y = static_cast<T>(value op rhs.y), \
|
||||
}; \
|
||||
} \
|
||||
friend constexpr Point& operator compound_op(Point& lhs, const Point& rhs) noexcept { \
|
||||
lhs.x = static_cast<T>(lhs.x op rhs.x); \
|
||||
lhs.y = static_cast<T>(lhs.y op rhs.y); \
|
||||
return lhs; \
|
||||
} \
|
||||
friend constexpr Point& operator compound_op(Point& lhs, T value) noexcept { \
|
||||
lhs.x = static_cast<T>(lhs.x op value); \
|
||||
lhs.y = static_cast<T>(lhs.y op value); \
|
||||
return lhs; \
|
||||
}
|
||||
ARITHMETIC_OP(+, +=)
|
||||
ARITHMETIC_OP(-, -=)
|
||||
ARITHMETIC_OP(*, *=)
|
||||
ARITHMETIC_OP(/, /=)
|
||||
#undef ARITHMETIC_OP
|
||||
|
||||
friend constexpr bool operator==(const Point&, const Point&) = default;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -55,6 +55,7 @@ void LogSettings() {
|
||||
log_setting("Renderer_UseAsynchronousGpuEmulation",
|
||||
values.use_asynchronous_gpu_emulation.GetValue());
|
||||
log_setting("Renderer_UseNvdecEmulation", values.use_nvdec_emulation.GetValue());
|
||||
log_setting("Renderer_AccelerateASTC", values.accelerate_astc.GetValue());
|
||||
log_setting("Renderer_UseVsync", values.use_vsync.GetValue());
|
||||
log_setting("Renderer_UseAssemblyShaders", values.use_assembly_shaders.GetValue());
|
||||
log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
|
||||
@@ -90,6 +91,13 @@ bool IsGPULevelHigh() {
|
||||
values.gpu_accuracy.GetValue() == GPUAccuracy::High;
|
||||
}
|
||||
|
||||
bool IsFastmemEnabled() {
|
||||
if (values.cpu_accuracy.GetValue() == CPUAccuracy::DebugMode) {
|
||||
return values.cpuopt_fastmem;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
float Volume() {
|
||||
if (values.audio_muted) {
|
||||
return 0.0f;
|
||||
@@ -115,6 +123,7 @@ void RestoreGlobalState(bool is_powered_on) {
|
||||
values.cpuopt_unsafe_unfuse_fma.SetGlobal(true);
|
||||
values.cpuopt_unsafe_reduce_fp_error.SetGlobal(true);
|
||||
values.cpuopt_unsafe_inaccurate_nan.SetGlobal(true);
|
||||
values.cpuopt_unsafe_fastmem_check.SetGlobal(true);
|
||||
|
||||
// Renderer
|
||||
values.renderer_backend.SetGlobal(true);
|
||||
@@ -127,6 +136,7 @@ void RestoreGlobalState(bool is_powered_on) {
|
||||
values.gpu_accuracy.SetGlobal(true);
|
||||
values.use_asynchronous_gpu_emulation.SetGlobal(true);
|
||||
values.use_nvdec_emulation.SetGlobal(true);
|
||||
values.accelerate_astc.SetGlobal(true);
|
||||
values.use_vsync.SetGlobal(true);
|
||||
values.use_assembly_shaders.SetGlobal(true);
|
||||
values.use_asynchronous_shaders.SetGlobal(true);
|
||||
|
||||
@@ -125,10 +125,12 @@ struct Values {
|
||||
bool cpuopt_const_prop;
|
||||
bool cpuopt_misc_ir;
|
||||
bool cpuopt_reduce_misalign_checks;
|
||||
bool cpuopt_fastmem;
|
||||
|
||||
Setting<bool> cpuopt_unsafe_unfuse_fma;
|
||||
Setting<bool> cpuopt_unsafe_reduce_fp_error;
|
||||
Setting<bool> cpuopt_unsafe_inaccurate_nan;
|
||||
Setting<bool> cpuopt_unsafe_fastmem_check;
|
||||
|
||||
// Renderer
|
||||
Setting<RendererBackend> renderer_backend;
|
||||
@@ -145,6 +147,7 @@ struct Values {
|
||||
Setting<GPUAccuracy> gpu_accuracy;
|
||||
Setting<bool> use_asynchronous_gpu_emulation;
|
||||
Setting<bool> use_nvdec_emulation;
|
||||
Setting<bool> accelerate_astc;
|
||||
Setting<bool> use_vsync;
|
||||
Setting<bool> use_assembly_shaders;
|
||||
Setting<bool> use_asynchronous_shaders;
|
||||
@@ -216,6 +219,7 @@ struct Values {
|
||||
std::string program_args;
|
||||
bool dump_exefs;
|
||||
bool dump_nso;
|
||||
bool enable_fs_access_log;
|
||||
bool reporting_services;
|
||||
bool quest_flag;
|
||||
bool disable_macro_jit;
|
||||
@@ -249,6 +253,8 @@ void SetConfiguringGlobal(bool is_global);
|
||||
bool IsGPULevelExtreme();
|
||||
bool IsGPULevelHigh();
|
||||
|
||||
bool IsFastmemEnabled();
|
||||
|
||||
float Volume();
|
||||
|
||||
std::string GetTimeZoneString();
|
||||
|
||||
@@ -65,9 +65,6 @@ public:
|
||||
/// Step CPU by one instruction
|
||||
virtual void Step() = 0;
|
||||
|
||||
/// Exits execution from a callback, the callback must rewind the stack
|
||||
virtual void ExceptionalExit() = 0;
|
||||
|
||||
/// Clear all instruction cache
|
||||
virtual void ClearInstructionCache() = 0;
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
#include <cinttypes>
|
||||
#include <memory>
|
||||
#include <dynarmic/A32/a32.h>
|
||||
#include <dynarmic/A32/config.h>
|
||||
#include <dynarmic/A32/context.h>
|
||||
#include <dynarmic/interface/A32/a32.h>
|
||||
#include <dynarmic/interface/A32/config.h>
|
||||
#include <dynarmic/interface/A32/context.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/page_table.h"
|
||||
@@ -24,45 +24,46 @@ namespace Core {
|
||||
|
||||
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
|
||||
public:
|
||||
explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_) : parent{parent_} {}
|
||||
explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
|
||||
: parent{parent_}, memory(parent.system.Memory()) {}
|
||||
|
||||
u8 MemoryRead8(u32 vaddr) override {
|
||||
return parent.system.Memory().Read8(vaddr);
|
||||
return memory.Read8(vaddr);
|
||||
}
|
||||
u16 MemoryRead16(u32 vaddr) override {
|
||||
return parent.system.Memory().Read16(vaddr);
|
||||
return memory.Read16(vaddr);
|
||||
}
|
||||
u32 MemoryRead32(u32 vaddr) override {
|
||||
return parent.system.Memory().Read32(vaddr);
|
||||
return memory.Read32(vaddr);
|
||||
}
|
||||
u64 MemoryRead64(u32 vaddr) override {
|
||||
return parent.system.Memory().Read64(vaddr);
|
||||
return memory.Read64(vaddr);
|
||||
}
|
||||
|
||||
void MemoryWrite8(u32 vaddr, u8 value) override {
|
||||
parent.system.Memory().Write8(vaddr, value);
|
||||
memory.Write8(vaddr, value);
|
||||
}
|
||||
void MemoryWrite16(u32 vaddr, u16 value) override {
|
||||
parent.system.Memory().Write16(vaddr, value);
|
||||
memory.Write16(vaddr, value);
|
||||
}
|
||||
void MemoryWrite32(u32 vaddr, u32 value) override {
|
||||
parent.system.Memory().Write32(vaddr, value);
|
||||
memory.Write32(vaddr, value);
|
||||
}
|
||||
void MemoryWrite64(u32 vaddr, u64 value) override {
|
||||
parent.system.Memory().Write64(vaddr, value);
|
||||
memory.Write64(vaddr, value);
|
||||
}
|
||||
|
||||
bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override {
|
||||
return parent.system.Memory().WriteExclusive8(vaddr, value, expected);
|
||||
return memory.WriteExclusive8(vaddr, value, expected);
|
||||
}
|
||||
bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override {
|
||||
return parent.system.Memory().WriteExclusive16(vaddr, value, expected);
|
||||
return memory.WriteExclusive16(vaddr, value, expected);
|
||||
}
|
||||
bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override {
|
||||
return parent.system.Memory().WriteExclusive32(vaddr, value, expected);
|
||||
return memory.WriteExclusive32(vaddr, value, expected);
|
||||
}
|
||||
bool MemoryWriteExclusive64(u32 vaddr, u64 value, u64 expected) override {
|
||||
return parent.system.Memory().WriteExclusive64(vaddr, value, expected);
|
||||
return memory.WriteExclusive64(vaddr, value, expected);
|
||||
}
|
||||
|
||||
void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
|
||||
@@ -78,7 +79,9 @@ public:
|
||||
}
|
||||
|
||||
void CallSVC(u32 swi) override {
|
||||
Kernel::Svc::Call(parent.system, swi);
|
||||
parent.svc_called = true;
|
||||
parent.svc_swi = swi;
|
||||
parent.jit->HaltExecution();
|
||||
}
|
||||
|
||||
void AddTicks(u64 ticks) override {
|
||||
@@ -110,6 +113,7 @@ public:
|
||||
}
|
||||
|
||||
ARM_Dynarmic_32& parent;
|
||||
Core::Memory::Memory& memory;
|
||||
std::size_t num_interpreted_instructions{};
|
||||
static constexpr u64 minimum_run_cycles = 1000U;
|
||||
};
|
||||
@@ -124,6 +128,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
|
||||
if (page_table) {
|
||||
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>(
|
||||
page_table->pointers.data());
|
||||
config.fastmem_pointer = page_table->fastmem_arena;
|
||||
}
|
||||
config.absolute_offset_page_table = true;
|
||||
config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
|
||||
@@ -139,7 +144,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
|
||||
|
||||
// Code cache size
|
||||
config.code_cache_size = 512 * 1024 * 1024;
|
||||
config.far_code_offset = 256 * 1024 * 1024;
|
||||
config.far_code_offset = 400 * 1024 * 1024;
|
||||
|
||||
// Safe optimizations
|
||||
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::DebugMode) {
|
||||
@@ -167,6 +172,9 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
|
||||
if (!Settings::values.cpuopt_reduce_misalign_checks) {
|
||||
config.only_detect_misalignment_via_page_table_on_page_boundary = false;
|
||||
}
|
||||
if (!Settings::values.cpuopt_fastmem) {
|
||||
config.fastmem_pointer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe optimizations
|
||||
@@ -187,11 +195,17 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::Run() {
|
||||
jit->Run();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::ExceptionalExit() {
|
||||
jit->ExceptionalExit();
|
||||
while (true) {
|
||||
jit->Run();
|
||||
if (!svc_called) {
|
||||
break;
|
||||
}
|
||||
svc_called = false;
|
||||
Kernel::Svc::Call(system, svc_swi);
|
||||
if (shutdown) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::Step() {
|
||||
@@ -275,6 +289,7 @@ void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
|
||||
|
||||
void ARM_Dynarmic_32::PrepareReschedule() {
|
||||
jit->HaltExecution();
|
||||
shutdown = true;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::ClearInstructionCache() {
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <dynarmic/A32/a32.h>
|
||||
#include <dynarmic/A64/a64.h>
|
||||
#include <dynarmic/exclusive_monitor.h>
|
||||
#include <dynarmic/interface/A32/a32.h>
|
||||
#include <dynarmic/interface/A64/a64.h>
|
||||
#include <dynarmic/interface/exclusive_monitor.h>
|
||||
#include "common/common_types.h"
|
||||
#include "common/hash.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
@@ -42,7 +42,6 @@ public:
|
||||
u32 GetPSTATE() const override;
|
||||
void SetPSTATE(u32 pstate) override;
|
||||
void Run() override;
|
||||
void ExceptionalExit() override;
|
||||
void Step() override;
|
||||
VAddr GetTlsAddress() const override;
|
||||
void SetTlsAddress(VAddr address) override;
|
||||
@@ -82,6 +81,12 @@ private:
|
||||
std::size_t core_index;
|
||||
DynarmicExclusiveMonitor& exclusive_monitor;
|
||||
std::shared_ptr<Dynarmic::A32::Jit> jit;
|
||||
|
||||
// SVC callback
|
||||
u32 svc_swi{};
|
||||
bool svc_called{};
|
||||
|
||||
bool shutdown{};
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
#include <cinttypes>
|
||||
#include <memory>
|
||||
#include <dynarmic/A64/a64.h>
|
||||
#include <dynarmic/A64/config.h>
|
||||
#include <dynarmic/interface/A64/a64.h>
|
||||
#include <dynarmic/interface/A64/config.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/page_table.h"
|
||||
@@ -27,57 +27,56 @@ using Vector = Dynarmic::A64::Vector;
|
||||
|
||||
class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
|
||||
public:
|
||||
explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_) : parent{parent_} {}
|
||||
explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_)
|
||||
: parent{parent_}, memory(parent.system.Memory()) {}
|
||||
|
||||
u8 MemoryRead8(u64 vaddr) override {
|
||||
return parent.system.Memory().Read8(vaddr);
|
||||
return memory.Read8(vaddr);
|
||||
}
|
||||
u16 MemoryRead16(u64 vaddr) override {
|
||||
return parent.system.Memory().Read16(vaddr);
|
||||
return memory.Read16(vaddr);
|
||||
}
|
||||
u32 MemoryRead32(u64 vaddr) override {
|
||||
return parent.system.Memory().Read32(vaddr);
|
||||
return memory.Read32(vaddr);
|
||||
}
|
||||
u64 MemoryRead64(u64 vaddr) override {
|
||||
return parent.system.Memory().Read64(vaddr);
|
||||
return memory.Read64(vaddr);
|
||||
}
|
||||
Vector MemoryRead128(u64 vaddr) override {
|
||||
auto& memory = parent.system.Memory();
|
||||
return {memory.Read64(vaddr), memory.Read64(vaddr + 8)};
|
||||
}
|
||||
|
||||
void MemoryWrite8(u64 vaddr, u8 value) override {
|
||||
parent.system.Memory().Write8(vaddr, value);
|
||||
memory.Write8(vaddr, value);
|
||||
}
|
||||
void MemoryWrite16(u64 vaddr, u16 value) override {
|
||||
parent.system.Memory().Write16(vaddr, value);
|
||||
memory.Write16(vaddr, value);
|
||||
}
|
||||
void MemoryWrite32(u64 vaddr, u32 value) override {
|
||||
parent.system.Memory().Write32(vaddr, value);
|
||||
memory.Write32(vaddr, value);
|
||||
}
|
||||
void MemoryWrite64(u64 vaddr, u64 value) override {
|
||||
parent.system.Memory().Write64(vaddr, value);
|
||||
memory.Write64(vaddr, value);
|
||||
}
|
||||
void MemoryWrite128(u64 vaddr, Vector value) override {
|
||||
auto& memory = parent.system.Memory();
|
||||
memory.Write64(vaddr, value[0]);
|
||||
memory.Write64(vaddr + 8, value[1]);
|
||||
}
|
||||
|
||||
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override {
|
||||
return parent.system.Memory().WriteExclusive8(vaddr, value, expected);
|
||||
return memory.WriteExclusive8(vaddr, value, expected);
|
||||
}
|
||||
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override {
|
||||
return parent.system.Memory().WriteExclusive16(vaddr, value, expected);
|
||||
return memory.WriteExclusive16(vaddr, value, expected);
|
||||
}
|
||||
bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, std::uint32_t expected) override {
|
||||
return parent.system.Memory().WriteExclusive32(vaddr, value, expected);
|
||||
return memory.WriteExclusive32(vaddr, value, expected);
|
||||
}
|
||||
bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, std::uint64_t expected) override {
|
||||
return parent.system.Memory().WriteExclusive64(vaddr, value, expected);
|
||||
return memory.WriteExclusive64(vaddr, value, expected);
|
||||
}
|
||||
bool MemoryWriteExclusive128(u64 vaddr, Vector value, Vector expected) override {
|
||||
return parent.system.Memory().WriteExclusive128(vaddr, value, expected);
|
||||
return memory.WriteExclusive128(vaddr, value, expected);
|
||||
}
|
||||
|
||||
void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
|
||||
@@ -102,7 +101,9 @@ public:
|
||||
}
|
||||
|
||||
void CallSVC(u32 swi) override {
|
||||
Kernel::Svc::Call(parent.system, swi);
|
||||
parent.svc_called = true;
|
||||
parent.svc_swi = swi;
|
||||
parent.jit->HaltExecution();
|
||||
}
|
||||
|
||||
void AddTicks(u64 ticks) override {
|
||||
@@ -137,6 +138,7 @@ public:
|
||||
}
|
||||
|
||||
ARM_Dynarmic_64& parent;
|
||||
Core::Memory::Memory& memory;
|
||||
u64 tpidrro_el0 = 0;
|
||||
u64 tpidr_el0 = 0;
|
||||
static constexpr u64 minimum_run_cycles = 1000U;
|
||||
@@ -158,6 +160,10 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
|
||||
config.absolute_offset_page_table = true;
|
||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
||||
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
|
||||
|
||||
config.fastmem_pointer = page_table->fastmem_arena;
|
||||
config.fastmem_address_space_bits = address_space_bits;
|
||||
config.silently_mirror_fastmem = false;
|
||||
}
|
||||
|
||||
// Multi-process state
|
||||
@@ -179,7 +185,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
|
||||
|
||||
// Code cache size
|
||||
config.code_cache_size = 512 * 1024 * 1024;
|
||||
config.far_code_offset = 256 * 1024 * 1024;
|
||||
config.far_code_offset = 400 * 1024 * 1024;
|
||||
|
||||
// Safe optimizations
|
||||
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::DebugMode) {
|
||||
@@ -207,6 +213,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
|
||||
if (!Settings::values.cpuopt_reduce_misalign_checks) {
|
||||
config.only_detect_misalignment_via_page_table_on_page_boundary = false;
|
||||
}
|
||||
if (!Settings::values.cpuopt_fastmem) {
|
||||
config.fastmem_pointer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe optimizations
|
||||
@@ -221,17 +230,26 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
|
||||
if (Settings::values.cpuopt_unsafe_inaccurate_nan.GetValue()) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
|
||||
}
|
||||
if (Settings::values.cpuopt_unsafe_fastmem_check.GetValue()) {
|
||||
config.fastmem_address_space_bits = 64;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_shared<Dynarmic::A64::Jit>(config);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::Run() {
|
||||
jit->Run();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::ExceptionalExit() {
|
||||
jit->ExceptionalExit();
|
||||
while (true) {
|
||||
jit->Run();
|
||||
if (!svc_called) {
|
||||
break;
|
||||
}
|
||||
svc_called = false;
|
||||
Kernel::Svc::Call(system, svc_swi);
|
||||
if (shutdown) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::Step() {
|
||||
@@ -320,6 +338,7 @@ void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) {
|
||||
|
||||
void ARM_Dynarmic_64::PrepareReschedule() {
|
||||
jit->HaltExecution();
|
||||
shutdown = true;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::ClearInstructionCache() {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <dynarmic/A64/a64.h>
|
||||
#include <dynarmic/interface/A64/a64.h>
|
||||
#include "common/common_types.h"
|
||||
#include "common/hash.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
@@ -40,7 +40,6 @@ public:
|
||||
void SetPSTATE(u32 pstate) override;
|
||||
void Run() override;
|
||||
void Step() override;
|
||||
void ExceptionalExit() override;
|
||||
VAddr GetTlsAddress() const override;
|
||||
void SetTlsAddress(VAddr address) override;
|
||||
void SetTPIDR_EL0(u64 value) override;
|
||||
@@ -75,6 +74,12 @@ private:
|
||||
DynarmicExclusiveMonitor& exclusive_monitor;
|
||||
|
||||
std::shared_ptr<Dynarmic::A64::Jit> jit;
|
||||
|
||||
// SVC callback
|
||||
u32 svc_swi{};
|
||||
bool svc_called{};
|
||||
|
||||
bool shutdown{};
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include <dynarmic/A32/coprocessor.h>
|
||||
#include <dynarmic/interface/A32/coprocessor.h>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <dynarmic/exclusive_monitor.h>
|
||||
#include <dynarmic/interface/exclusive_monitor.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic_32.h"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
namespace Core {
|
||||
|
||||
DeviceMemory::DeviceMemory() : buffer{DramMemoryMap::Size} {}
|
||||
DeviceMemory::DeviceMemory() : buffer{DramMemoryMap::Size, 1ULL << 39} {}
|
||||
DeviceMemory::~DeviceMemory() = default;
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
#include "common/host_memory.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
@@ -21,27 +21,30 @@ enum : u64 {
|
||||
};
|
||||
}; // namespace DramMemoryMap
|
||||
|
||||
class DeviceMemory : NonCopyable {
|
||||
class DeviceMemory {
|
||||
public:
|
||||
explicit DeviceMemory();
|
||||
~DeviceMemory();
|
||||
|
||||
DeviceMemory& operator=(const DeviceMemory&) = delete;
|
||||
DeviceMemory(const DeviceMemory&) = delete;
|
||||
|
||||
template <typename T>
|
||||
PAddr GetPhysicalAddr(const T* ptr) const {
|
||||
return (reinterpret_cast<uintptr_t>(ptr) - reinterpret_cast<uintptr_t>(buffer.data())) +
|
||||
return (reinterpret_cast<uintptr_t>(ptr) -
|
||||
reinterpret_cast<uintptr_t>(buffer.BackingBasePointer())) +
|
||||
DramMemoryMap::Base;
|
||||
}
|
||||
|
||||
u8* GetPointer(PAddr addr) {
|
||||
return buffer.data() + (addr - DramMemoryMap::Base);
|
||||
return buffer.BackingBasePointer() + (addr - DramMemoryMap::Base);
|
||||
}
|
||||
|
||||
const u8* GetPointer(PAddr addr) const {
|
||||
return buffer.data() + (addr - DramMemoryMap::Base);
|
||||
return buffer.BackingBasePointer() + (addr - DramMemoryMap::Base);
|
||||
}
|
||||
|
||||
private:
|
||||
Common::VirtualBuffer<u8> buffer;
|
||||
Common::HostMemory buffer;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -150,7 +150,9 @@ void ProgramMetadata::Print() const {
|
||||
LOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.is_retail ? "YES" : "NO");
|
||||
LOG_DEBUG(Service_FS, "Title ID Min: 0x{:016X}", acid_header.title_id_min);
|
||||
LOG_DEBUG(Service_FS, "Title ID Max: 0x{:016X}", acid_header.title_id_max);
|
||||
LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", acid_file_access.permissions);
|
||||
u64_le permissions_l; // local copy to fix alignment error
|
||||
std::memcpy(&permissions_l, &acid_file_access.permissions, sizeof(permissions_l));
|
||||
LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", permissions_l);
|
||||
|
||||
// Begin ACI0 printing (actual perms, unsigned)
|
||||
LOG_DEBUG(Service_FS, "Magic: {:.4}", aci_header.magic.data());
|
||||
|
||||
@@ -53,7 +53,7 @@ ResultVal<VirtualFile> RomFSFactory::OpenPatchedRomFS(u64 title_id, ContentRecor
|
||||
|
||||
if (nca == nullptr) {
|
||||
// TODO: Find the right error code to use here
|
||||
return RESULT_UNKNOWN;
|
||||
return ResultUnknown;
|
||||
}
|
||||
|
||||
const PatchManager patch_manager{title_id, filesystem_controller, content_provider};
|
||||
@@ -74,13 +74,13 @@ ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage,
|
||||
const std::shared_ptr<NCA> res = GetEntry(title_id, storage, type);
|
||||
if (res == nullptr) {
|
||||
// TODO(DarkLordZach): Find the right error code to use here
|
||||
return RESULT_UNKNOWN;
|
||||
return ResultUnknown;
|
||||
}
|
||||
|
||||
const auto romfs = res->GetRomFS();
|
||||
if (romfs == nullptr) {
|
||||
// TODO(DarkLordZach): Find the right error code to use here
|
||||
return RESULT_UNKNOWN;
|
||||
return ResultUnknown;
|
||||
}
|
||||
|
||||
return MakeResult<VirtualFile>(romfs);
|
||||
|
||||
@@ -91,7 +91,7 @@ ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space,
|
||||
// Return an error if the save data doesn't actually exist.
|
||||
if (out == nullptr) {
|
||||
// TODO(DarkLordZach): Find out correct error code.
|
||||
return RESULT_UNKNOWN;
|
||||
return ResultUnknown;
|
||||
}
|
||||
|
||||
return MakeResult<VirtualDir>(std::move(out));
|
||||
@@ -105,14 +105,14 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space,
|
||||
|
||||
auto out = dir->GetDirectoryRelative(save_directory);
|
||||
|
||||
if (out == nullptr && ShouldSaveDataBeAutomaticallyCreated(space, meta)) {
|
||||
if (out == nullptr && (ShouldSaveDataBeAutomaticallyCreated(space, meta) && auto_create)) {
|
||||
return Create(space, meta);
|
||||
}
|
||||
|
||||
// Return an error if the save data doesn't actually exist.
|
||||
if (out == nullptr) {
|
||||
// TODO(Subv): Find out correct error code.
|
||||
return RESULT_UNKNOWN;
|
||||
return ResultUnknown;
|
||||
}
|
||||
|
||||
return MakeResult<VirtualDir>(std::move(out));
|
||||
@@ -199,4 +199,8 @@ void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 us
|
||||
size_file->WriteObject(new_value);
|
||||
}
|
||||
|
||||
void SaveDataFactory::SetAutoCreate(bool state) {
|
||||
auto_create = state;
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -104,9 +104,12 @@ public:
|
||||
void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
|
||||
SaveDataSize new_value) const;
|
||||
|
||||
void SetAutoCreate(bool state);
|
||||
|
||||
private:
|
||||
VirtualDir dir;
|
||||
Core::System& system;
|
||||
bool auto_create{true};
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "core/file_sys/mode.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#endif
|
||||
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/file_sys/vfs_libzip.h"
|
||||
#include "core/file_sys/vfs_vector.h"
|
||||
|
||||
@@ -27,6 +27,10 @@ struct AnalogProperties {
|
||||
float range;
|
||||
float threshold;
|
||||
};
|
||||
template <typename StatusType>
|
||||
struct InputCallback {
|
||||
std::function<void(StatusType)> on_change;
|
||||
};
|
||||
|
||||
/// An abstract class template for an input device (a button, an analog input, etc.).
|
||||
template <typename StatusType>
|
||||
@@ -50,6 +54,17 @@ public:
|
||||
[[maybe_unused]] f32 freq_high) const {
|
||||
return {};
|
||||
}
|
||||
void SetCallback(InputCallback<StatusType> callback_) {
|
||||
callback = std::move(callback_);
|
||||
}
|
||||
void TriggerOnChange() {
|
||||
if (callback.on_change) {
|
||||
callback.on_change(GetStatus());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
InputCallback<StatusType> callback;
|
||||
};
|
||||
|
||||
/// An abstract class template for a factory that can create input devices.
|
||||
|
||||
@@ -30,16 +30,38 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
SessionRequestHandler::SessionRequestHandler() = default;
|
||||
SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_)
|
||||
: kernel{kernel_}, service_thread{kernel.CreateServiceThread(service_name_)} {}
|
||||
|
||||
SessionRequestHandler::~SessionRequestHandler() = default;
|
||||
SessionRequestHandler::~SessionRequestHandler() {
|
||||
kernel.ReleaseServiceThread(service_thread);
|
||||
}
|
||||
|
||||
SessionRequestManager::SessionRequestManager(KernelCore& kernel_) : kernel{kernel_} {}
|
||||
|
||||
SessionRequestManager::~SessionRequestManager() = default;
|
||||
|
||||
bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& context) const {
|
||||
if (IsDomain() && context.HasDomainMessageHeader()) {
|
||||
const auto& message_header = context.GetDomainMessageHeader();
|
||||
const auto object_id = message_header.object_id;
|
||||
|
||||
if (object_id > DomainHandlerCount()) {
|
||||
LOG_CRITICAL(IPC, "object_id {} is too big!", object_id);
|
||||
return false;
|
||||
}
|
||||
return DomainHandler(object_id - 1) != nullptr;
|
||||
} else {
|
||||
return session_handler != nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SessionRequestHandler::ClientConnected(KServerSession* session) {
|
||||
session->SetSessionHandler(shared_from_this());
|
||||
session->ClientConnected(shared_from_this());
|
||||
}
|
||||
|
||||
void SessionRequestHandler::ClientDisconnected(KServerSession* session) {
|
||||
session->SetSessionHandler(nullptr);
|
||||
session->ClientDisconnected();
|
||||
}
|
||||
|
||||
HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_,
|
||||
@@ -169,12 +191,12 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const KHandleTab
|
||||
|
||||
if (command_header->IsCloseCommand()) {
|
||||
// Close does not populate the rest of the IPC header
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
std::copy_n(src_cmdbuf, IPC::COMMAND_BUFFER_LENGTH, cmd_buf.begin());
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_thread) {
|
||||
@@ -216,7 +238,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_t
|
||||
memory.WriteBlock(owner_process, requesting_thread.GetTLSAddress(), cmd_buf.data(),
|
||||
write_size * sizeof(u32));
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
|
||||
|
||||
@@ -46,6 +46,7 @@ class KThread;
|
||||
class KReadableEvent;
|
||||
class KSession;
|
||||
class KWritableEvent;
|
||||
class ServiceThread;
|
||||
|
||||
enum class ThreadWakeupReason;
|
||||
|
||||
@@ -56,7 +57,7 @@ enum class ThreadWakeupReason;
|
||||
*/
|
||||
class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> {
|
||||
public:
|
||||
SessionRequestHandler();
|
||||
SessionRequestHandler(KernelCore& kernel, const char* service_name_);
|
||||
virtual ~SessionRequestHandler();
|
||||
|
||||
/**
|
||||
@@ -83,6 +84,14 @@ public:
|
||||
* @param server_session ServerSession associated with the connection.
|
||||
*/
|
||||
void ClientDisconnected(KServerSession* session);
|
||||
|
||||
std::weak_ptr<ServiceThread> GetServiceThread() const {
|
||||
return service_thread;
|
||||
}
|
||||
|
||||
protected:
|
||||
KernelCore& kernel;
|
||||
std::weak_ptr<ServiceThread> service_thread;
|
||||
};
|
||||
|
||||
using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>;
|
||||
@@ -94,7 +103,8 @@ using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>;
|
||||
*/
|
||||
class SessionRequestManager final {
|
||||
public:
|
||||
SessionRequestManager() = default;
|
||||
explicit SessionRequestManager(KernelCore& kernel);
|
||||
~SessionRequestManager();
|
||||
|
||||
bool IsDomain() const {
|
||||
return is_domain;
|
||||
@@ -142,10 +152,19 @@ public:
|
||||
session_handler = std::move(handler);
|
||||
}
|
||||
|
||||
std::weak_ptr<ServiceThread> GetServiceThread() const {
|
||||
return session_handler->GetServiceThread();
|
||||
}
|
||||
|
||||
bool HasSessionRequestHandler(const HLERequestContext& context) const;
|
||||
|
||||
private:
|
||||
bool is_domain{};
|
||||
SessionRequestHandlerPtr session_handler;
|
||||
std::vector<SessionRequestHandlerPtr> domain_handlers;
|
||||
|
||||
private:
|
||||
KernelCore& kernel;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -97,7 +97,7 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
|
||||
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
|
||||
(it->GetAddressArbiterKey() == addr)) {
|
||||
KThread* target_thread = std::addressof(*it);
|
||||
target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
|
||||
target_thread->SetSyncedObject(nullptr, ResultSuccess);
|
||||
|
||||
ASSERT(target_thread->IsWaitingForAddressArbiter());
|
||||
target_thread->Wakeup();
|
||||
@@ -107,7 +107,7 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
|
||||
++num_waiters;
|
||||
}
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count) {
|
||||
@@ -130,7 +130,7 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32
|
||||
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
|
||||
(it->GetAddressArbiterKey() == addr)) {
|
||||
KThread* target_thread = std::addressof(*it);
|
||||
target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
|
||||
target_thread->SetSyncedObject(nullptr, ResultSuccess);
|
||||
|
||||
ASSERT(target_thread->IsWaitingForAddressArbiter());
|
||||
target_thread->Wakeup();
|
||||
@@ -140,7 +140,7 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32
|
||||
++num_waiters;
|
||||
}
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count) {
|
||||
@@ -198,7 +198,7 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
|
||||
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
|
||||
(it->GetAddressArbiterKey() == addr)) {
|
||||
KThread* target_thread = std::addressof(*it);
|
||||
target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
|
||||
target_thread->SetSyncedObject(nullptr, ResultSuccess);
|
||||
|
||||
ASSERT(target_thread->IsWaitingForAddressArbiter());
|
||||
target_thread->Wakeup();
|
||||
@@ -208,7 +208,7 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
|
||||
++num_waiters;
|
||||
}
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
return SignalAndModifyByWaitingCountIfEqual(addr, value, count);
|
||||
}
|
||||
UNREACHABLE();
|
||||
return RESULT_UNKNOWN;
|
||||
return ResultUnknown;
|
||||
}
|
||||
|
||||
[[nodiscard]] ResultCode WaitForAddress(VAddr addr, Svc::ArbitrationType type, s32 value,
|
||||
@@ -51,7 +51,7 @@ public:
|
||||
return WaitIfEqual(addr, value, timeout);
|
||||
}
|
||||
UNREACHABLE();
|
||||
return RESULT_UNKNOWN;
|
||||
return ResultUnknown;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -7,10 +7,11 @@
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
|
||||
#include <boost/intrusive/rbtree.hpp>
|
||||
|
||||
#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_class_token.h"
|
||||
|
||||
namespace Kernel {
|
||||
@@ -175,7 +176,7 @@ private:
|
||||
|
||||
class KAutoObjectWithListContainer;
|
||||
|
||||
class KAutoObjectWithList : public KAutoObject {
|
||||
class KAutoObjectWithList : public KAutoObject, public boost::intrusive::set_base_hook<> {
|
||||
public:
|
||||
explicit KAutoObjectWithList(KernelCore& kernel_) : KAutoObject(kernel_) {}
|
||||
|
||||
@@ -192,6 +193,10 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
friend bool operator<(const KAutoObjectWithList& left, const KAutoObjectWithList& right) {
|
||||
return &left < &right;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual u64 GetId() const {
|
||||
return reinterpret_cast<u64>(this);
|
||||
@@ -203,8 +208,6 @@ public:
|
||||
|
||||
private:
|
||||
friend class KAutoObjectWithListContainer;
|
||||
|
||||
Common::IntrusiveRedBlackTreeNode list_node;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
||||
@@ -9,13 +9,13 @@ namespace Kernel {
|
||||
void KAutoObjectWithListContainer::Register(KAutoObjectWithList* obj) {
|
||||
KScopedLightLock lk(m_lock);
|
||||
|
||||
m_object_list.insert(*obj);
|
||||
m_object_list.insert_unique(*obj);
|
||||
}
|
||||
|
||||
void KAutoObjectWithListContainer::Unregister(KAutoObjectWithList* obj) {
|
||||
KScopedLightLock lk(m_lock);
|
||||
|
||||
m_object_list.erase(m_object_list.iterator_to(*obj));
|
||||
m_object_list.erase(*obj);
|
||||
}
|
||||
|
||||
size_t KAutoObjectWithListContainer::GetOwnedCount(KProcess* owner) {
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include <boost/intrusive/rbtree.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
@@ -23,8 +25,7 @@ class KAutoObjectWithListContainer {
|
||||
YUZU_NON_MOVEABLE(KAutoObjectWithListContainer);
|
||||
|
||||
public:
|
||||
using ListType = Common::IntrusiveRedBlackTreeMemberTraits<
|
||||
&KAutoObjectWithList::list_node>::TreeType<KAutoObjectWithList>;
|
||||
using ListType = boost::intrusive::rbtree<KAutoObjectWithList>;
|
||||
|
||||
public:
|
||||
class ListAccessor : public KScopedLightLock {
|
||||
|
||||
@@ -84,50 +84,43 @@ static_assert(ClassToken<KTransferMemory> == ((0b10010001 << 8) | ClassToken<KAu
|
||||
// Ensure that the token hierarchy reflects the class hierarchy.
|
||||
|
||||
// Base classes.
|
||||
static_assert(!std::is_final<KSynchronizationObject>::value &&
|
||||
std::is_base_of<KAutoObject, KSynchronizationObject>::value);
|
||||
static_assert(!std::is_final<KReadableEvent>::value &&
|
||||
std::is_base_of<KSynchronizationObject, KReadableEvent>::value);
|
||||
static_assert(!std::is_final_v<KSynchronizationObject> &&
|
||||
std::is_base_of_v<KAutoObject, KSynchronizationObject>);
|
||||
static_assert(!std::is_final_v<KReadableEvent> &&
|
||||
std::is_base_of_v<KSynchronizationObject, KReadableEvent>);
|
||||
|
||||
// Final classes
|
||||
// static_assert(std::is_final<KInterruptEvent>::value &&
|
||||
// std::is_base_of<KReadableEvent, KInterruptEvent>::value);
|
||||
// static_assert(std::is_final<KDebug>::value &&
|
||||
// std::is_base_of<KSynchronizationObject, KDebug>::value);
|
||||
static_assert(std::is_final<KThread>::value &&
|
||||
std::is_base_of<KSynchronizationObject, KThread>::value);
|
||||
static_assert(std::is_final<KServerPort>::value &&
|
||||
std::is_base_of<KSynchronizationObject, KServerPort>::value);
|
||||
static_assert(std::is_final<KServerSession>::value &&
|
||||
std::is_base_of<KSynchronizationObject, KServerSession>::value);
|
||||
static_assert(std::is_final<KClientPort>::value &&
|
||||
std::is_base_of<KSynchronizationObject, KClientPort>::value);
|
||||
static_assert(std::is_final<KClientSession>::value &&
|
||||
std::is_base_of<KAutoObject, KClientSession>::value);
|
||||
static_assert(std::is_final<KProcess>::value &&
|
||||
std::is_base_of<KSynchronizationObject, KProcess>::value);
|
||||
static_assert(std::is_final<KResourceLimit>::value &&
|
||||
std::is_base_of<KAutoObject, KResourceLimit>::value);
|
||||
// static_assert(std::is_final<KLightSession>::value &&
|
||||
// std::is_base_of<KAutoObject, KLightSession>::value);
|
||||
static_assert(std::is_final<KPort>::value && std::is_base_of<KAutoObject, KPort>::value);
|
||||
static_assert(std::is_final<KSession>::value && std::is_base_of<KAutoObject, KSession>::value);
|
||||
static_assert(std::is_final<KSharedMemory>::value &&
|
||||
std::is_base_of<KAutoObject, KSharedMemory>::value);
|
||||
static_assert(std::is_final<KEvent>::value && std::is_base_of<KAutoObject, KEvent>::value);
|
||||
static_assert(std::is_final<KWritableEvent>::value &&
|
||||
std::is_base_of<KAutoObject, KWritableEvent>::value);
|
||||
// static_assert(std::is_final<KLightClientSession>::value &&
|
||||
// std::is_base_of<KAutoObject, KLightClientSession>::value);
|
||||
// static_assert(std::is_final<KLightServerSession>::value &&
|
||||
// std::is_base_of<KAutoObject, KLightServerSession>::value);
|
||||
static_assert(std::is_final<KTransferMemory>::value &&
|
||||
std::is_base_of<KAutoObject, KTransferMemory>::value);
|
||||
// static_assert(std::is_final<KDeviceAddressSpace>::value &&
|
||||
// std::is_base_of<KAutoObject, KDeviceAddressSpace>::value);
|
||||
// static_assert(std::is_final<KSessionRequest>::value &&
|
||||
// std::is_base_of<KAutoObject, KSessionRequest>::value);
|
||||
// static_assert(std::is_final<KCodeMemory>::value &&
|
||||
// std::is_base_of<KAutoObject, KCodeMemory>::value);
|
||||
// static_assert(std::is_final_v<KInterruptEvent> &&
|
||||
// std::is_base_of_v<KReadableEvent, KInterruptEvent>);
|
||||
// static_assert(std::is_final_v<KDebug> &&
|
||||
// std::is_base_of_v<KSynchronizationObject, KDebug>);
|
||||
static_assert(std::is_final_v<KThread> && std::is_base_of_v<KSynchronizationObject, KThread>);
|
||||
static_assert(std::is_final_v<KServerPort> &&
|
||||
std::is_base_of_v<KSynchronizationObject, KServerPort>);
|
||||
static_assert(std::is_final_v<KServerSession> &&
|
||||
std::is_base_of_v<KSynchronizationObject, KServerSession>);
|
||||
static_assert(std::is_final_v<KClientPort> &&
|
||||
std::is_base_of_v<KSynchronizationObject, KClientPort>);
|
||||
static_assert(std::is_final_v<KClientSession> && std::is_base_of_v<KAutoObject, KClientSession>);
|
||||
static_assert(std::is_final_v<KProcess> && std::is_base_of_v<KSynchronizationObject, KProcess>);
|
||||
static_assert(std::is_final_v<KResourceLimit> && std::is_base_of_v<KAutoObject, KResourceLimit>);
|
||||
// static_assert(std::is_final_v<KLightSession> &&
|
||||
// std::is_base_of_v<KAutoObject, KLightSession>);
|
||||
static_assert(std::is_final_v<KPort> && std::is_base_of_v<KAutoObject, KPort>);
|
||||
static_assert(std::is_final_v<KSession> && std::is_base_of_v<KAutoObject, KSession>);
|
||||
static_assert(std::is_final_v<KSharedMemory> && std::is_base_of_v<KAutoObject, KSharedMemory>);
|
||||
static_assert(std::is_final_v<KEvent> && std::is_base_of_v<KAutoObject, KEvent>);
|
||||
static_assert(std::is_final_v<KWritableEvent> && std::is_base_of_v<KAutoObject, KWritableEvent>);
|
||||
// static_assert(std::is_final_v<KLightClientSession> &&
|
||||
// std::is_base_of_v<KAutoObject, KLightClientSession>);
|
||||
// static_assert(std::is_final_v<KLightServerSession> &&
|
||||
// std::is_base_of_v<KAutoObject, KLightServerSession>);
|
||||
static_assert(std::is_final_v<KTransferMemory> && std::is_base_of_v<KAutoObject, KTransferMemory>);
|
||||
// static_assert(std::is_final_v<KDeviceAddressSpace> &&
|
||||
// std::is_base_of_v<KAutoObject, KDeviceAddressSpace>);
|
||||
// static_assert(std::is_final_v<KSessionRequest> &&
|
||||
// std::is_base_of_v<KAutoObject, KSessionRequest>);
|
||||
// static_assert(std::is_final_v<KCodeMemory> &&
|
||||
// std::is_base_of_v<KAutoObject, KCodeMemory>);
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -16,11 +16,11 @@ namespace Kernel {
|
||||
KClientPort::KClientPort(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
|
||||
KClientPort::~KClientPort() = default;
|
||||
|
||||
void KClientPort::Initialize(KPort* parent_, s32 max_sessions_, std::string&& name_) {
|
||||
void KClientPort::Initialize(KPort* parent_port_, s32 max_sessions_, std::string&& name_) {
|
||||
// Set member variables.
|
||||
num_sessions = 0;
|
||||
peak_sessions = 0;
|
||||
parent = parent_;
|
||||
parent = parent_port_;
|
||||
max_sessions = max_sessions_;
|
||||
name = std::move(name_);
|
||||
}
|
||||
@@ -28,6 +28,9 @@ void KClientPort::Initialize(KPort* parent_, s32 max_sessions_, std::string&& na
|
||||
void KClientPort::OnSessionFinalized() {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// This might happen if a session was improperly used with this port.
|
||||
ASSERT_MSG(num_sessions > 0, "num_sessions is invalid");
|
||||
|
||||
const auto prev = num_sessions--;
|
||||
if (prev == max_sessions) {
|
||||
this->NotifyAvailable();
|
||||
@@ -56,7 +59,8 @@ bool KClientPort::IsSignaled() const {
|
||||
return num_sessions < max_sessions;
|
||||
}
|
||||
|
||||
ResultCode KClientPort::CreateSession(KClientSession** out) {
|
||||
ResultCode KClientPort::CreateSession(KClientSession** out,
|
||||
std::shared_ptr<SessionRequestManager> session_manager) {
|
||||
// Reserve a new session from the resource limit.
|
||||
KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(),
|
||||
LimitableResource::Sessions);
|
||||
@@ -65,7 +69,7 @@ ResultCode KClientPort::CreateSession(KClientSession** out) {
|
||||
// Update the session counts.
|
||||
{
|
||||
// Atomically increment the number of sessions.
|
||||
s32 new_sessions;
|
||||
s32 new_sessions{};
|
||||
{
|
||||
const auto max = max_sessions;
|
||||
auto cur_sessions = num_sessions.load(std::memory_order_acquire);
|
||||
@@ -101,7 +105,7 @@ ResultCode KClientPort::CreateSession(KClientSession** out) {
|
||||
}
|
||||
|
||||
// Initialize the session.
|
||||
session->Initialize(this, parent->GetName());
|
||||
session->Initialize(this, parent->GetName(), session_manager);
|
||||
|
||||
// Commit the session reservation.
|
||||
session_reservation.Commit();
|
||||
@@ -119,7 +123,7 @@ ResultCode KClientPort::CreateSession(KClientSession** out) {
|
||||
// We succeeded, so set the output.
|
||||
session_guard.Cancel();
|
||||
*out = std::addressof(session->GetClientSession());
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace Kernel {
|
||||
class KClientSession;
|
||||
class KernelCore;
|
||||
class KPort;
|
||||
class SessionRequestManager;
|
||||
|
||||
class KClientPort final : public KSynchronizationObject {
|
||||
KERNEL_AUTOOBJECT_TRAITS(KClientPort, KSynchronizationObject);
|
||||
@@ -52,7 +53,8 @@ public:
|
||||
void Destroy() override;
|
||||
bool IsSignaled() const override;
|
||||
|
||||
ResultCode CreateSession(KClientSession** out);
|
||||
ResultCode CreateSession(KClientSession** out,
|
||||
std::shared_ptr<SessionRequestManager> session_manager = nullptr);
|
||||
|
||||
private:
|
||||
std::atomic<s32> num_sessions{};
|
||||
|
||||
@@ -36,9 +36,9 @@ public:
|
||||
explicit KClientSession(KernelCore& kernel_);
|
||||
~KClientSession() override;
|
||||
|
||||
void Initialize(KSession* parent_, std::string&& name_) {
|
||||
void Initialize(KSession* parent_session_, std::string&& name_) {
|
||||
// Set member variables.
|
||||
parent = parent_;
|
||||
parent = parent_session_;
|
||||
name = std::move(name_);
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
|
||||
next_value |= Svc::HandleWaitMask;
|
||||
}
|
||||
|
||||
next_owner_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
|
||||
next_owner_thread->SetSyncedObject(nullptr, ResultSuccess);
|
||||
next_owner_thread->Wakeup();
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
|
||||
}
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
|
||||
@@ -112,7 +112,7 @@ ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 val
|
||||
ASSERT(owner_thread.IsNull());
|
||||
{
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
cur_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
|
||||
cur_thread->SetSyncedObject(nullptr, ResultSuccess);
|
||||
|
||||
// Check if the thread should terminate.
|
||||
R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
|
||||
@@ -124,7 +124,7 @@ ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 val
|
||||
ResultInvalidCurrentMemory);
|
||||
|
||||
// If the tag isn't the handle (with wait mask), we're done.
|
||||
R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), RESULT_SUCCESS);
|
||||
R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), ResultSuccess);
|
||||
|
||||
// Get the lock owner thread.
|
||||
owner_thread =
|
||||
@@ -181,7 +181,7 @@ KThread* KConditionVariable::SignalImpl(KThread* thread) {
|
||||
if (can_access) {
|
||||
if (prev_tag == Svc::InvalidHandle) {
|
||||
// If nobody held the lock previously, we're all good.
|
||||
thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
|
||||
thread->SetSyncedObject(nullptr, ResultSuccess);
|
||||
thread->Wakeup();
|
||||
} else {
|
||||
// Get the previous owner.
|
||||
@@ -292,7 +292,7 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
|
||||
}
|
||||
|
||||
// Wake up the next owner.
|
||||
next_owner_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
|
||||
next_owner_thread->SetSyncedObject(nullptr, ResultSuccess);
|
||||
next_owner_thread->Wakeup();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ ResultCode KHandleTable::Finalize() {
|
||||
}
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
bool KHandleTable::Remove(Handle handle) {
|
||||
@@ -79,7 +79,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
|
||||
*out_handle = EncodeHandle(static_cast<u16>(index), linear_id);
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KHandleTable::Reserve(Handle* out_handle) {
|
||||
@@ -89,7 +89,7 @@ ResultCode KHandleTable::Reserve(Handle* out_handle) {
|
||||
R_UNLESS(m_count < m_table_size, ResultOutOfHandles);
|
||||
|
||||
*out_handle = EncodeHandle(static_cast<u16>(this->AllocateEntry()), this->AllocateLinearId());
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void KHandleTable::Unreserve(Handle handle) {
|
||||
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
m_free_head_index = i;
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
size_t GetTableSize() const {
|
||||
|
||||
@@ -18,41 +18,58 @@ class KernelCore;
|
||||
|
||||
class KLightConditionVariable {
|
||||
public:
|
||||
explicit KLightConditionVariable(KernelCore& kernel_)
|
||||
: thread_queue(kernel_), kernel(kernel_) {}
|
||||
explicit KLightConditionVariable(KernelCore& kernel_) : kernel{kernel_} {}
|
||||
|
||||
void Wait(KLightLock* lock, s64 timeout = -1) {
|
||||
WaitImpl(lock, timeout);
|
||||
lock->Lock();
|
||||
void Wait(KLightLock* lock, s64 timeout = -1, bool allow_terminating_thread = true) {
|
||||
WaitImpl(lock, timeout, allow_terminating_thread);
|
||||
}
|
||||
|
||||
void Broadcast() {
|
||||
KScopedSchedulerLock lk{kernel};
|
||||
while (thread_queue.WakeupFrontThread() != nullptr) {
|
||||
// We want to signal all threads, and so should continue waking up until there's nothing
|
||||
// to wake.
|
||||
|
||||
// Signal all threads.
|
||||
for (auto& thread : wait_list) {
|
||||
thread.SetState(ThreadState::Runnable);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void WaitImpl(KLightLock* lock, s64 timeout) {
|
||||
void WaitImpl(KLightLock* lock, s64 timeout, bool allow_terminating_thread) {
|
||||
KThread* owner = GetCurrentThreadPointer(kernel);
|
||||
|
||||
// Sleep the thread.
|
||||
{
|
||||
KScopedSchedulerLockAndSleep lk(kernel, owner, timeout);
|
||||
lock->Unlock();
|
||||
KScopedSchedulerLockAndSleep lk{kernel, owner, timeout};
|
||||
|
||||
if (!thread_queue.SleepThread(owner)) {
|
||||
if (!allow_terminating_thread && owner->IsTerminationRequested()) {
|
||||
lk.CancelSleep();
|
||||
return;
|
||||
}
|
||||
|
||||
lock->Unlock();
|
||||
|
||||
// Set the thread as waiting.
|
||||
GetCurrentThread(kernel).SetState(ThreadState::Waiting);
|
||||
|
||||
// Add the thread to the queue.
|
||||
wait_list.push_back(GetCurrentThread(kernel));
|
||||
}
|
||||
|
||||
// Remove the thread from the wait list.
|
||||
{
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
wait_list.erase(wait_list.iterator_to(GetCurrentThread(kernel)));
|
||||
}
|
||||
|
||||
// Cancel the task that the sleep setup.
|
||||
kernel.TimeManager().UnscheduleTimeEvent(owner);
|
||||
|
||||
// Re-acquire the lock.
|
||||
lock->Lock();
|
||||
}
|
||||
KThreadQueue thread_queue;
|
||||
|
||||
KernelCore& kernel;
|
||||
KThread::WaiterList wait_list{};
|
||||
};
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -59,11 +59,7 @@ void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
|
||||
owner_thread->AddWaiter(cur_thread);
|
||||
|
||||
// Set thread states.
|
||||
if (cur_thread->GetState() == ThreadState::Runnable) {
|
||||
cur_thread->SetState(ThreadState::Waiting);
|
||||
} else {
|
||||
KScheduler::SetSchedulerUpdateNeeded(kernel);
|
||||
}
|
||||
cur_thread->SetState(ThreadState::Waiting);
|
||||
|
||||
if (owner_thread->IsSuspended()) {
|
||||
owner_thread->ContinueIfHasKernelWaiters();
|
||||
@@ -73,10 +69,9 @@ void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
|
||||
// We're no longer waiting on the lock owner.
|
||||
{
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
KThread* owner_thread = cur_thread->GetLockOwner();
|
||||
if (owner_thread) {
|
||||
|
||||
if (KThread* owner_thread = cur_thread->GetLockOwner(); owner_thread != nullptr) {
|
||||
owner_thread->RemoveWaiter(cur_thread);
|
||||
KScheduler::SetSchedulerUpdateNeeded(kernel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,17 +90,13 @@ void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
|
||||
|
||||
// Pass the lock to the next owner.
|
||||
uintptr_t next_tag = 0;
|
||||
if (next_owner) {
|
||||
if (next_owner != nullptr) {
|
||||
next_tag = reinterpret_cast<uintptr_t>(next_owner);
|
||||
if (num_waiters > 1) {
|
||||
next_tag |= 0x1;
|
||||
}
|
||||
|
||||
if (next_owner->GetState() == ThreadState::Waiting) {
|
||||
next_owner->SetState(ThreadState::Runnable);
|
||||
} else {
|
||||
KScheduler::SetSchedulerUpdateNeeded(kernel);
|
||||
}
|
||||
next_owner->SetState(ThreadState::Runnable);
|
||||
|
||||
if (next_owner->IsSuspended()) {
|
||||
next_owner->ContinueIfHasKernelWaiters();
|
||||
|
||||
@@ -86,7 +86,7 @@ ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_
|
||||
|
||||
// Early return if we're allocating no pages
|
||||
if (num_pages == 0) {
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
// Lock the pool that we're allocating from
|
||||
@@ -146,14 +146,14 @@ ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_
|
||||
|
||||
// We succeeded!
|
||||
group_guard.Cancel();
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KMemoryManager::Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
|
||||
Direction dir) {
|
||||
// Early return if we're freeing no pages
|
||||
if (!num_pages) {
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
// Lock the pool that we're freeing from
|
||||
@@ -170,7 +170,7 @@ ResultCode KMemoryManager::Free(KPageLinkedList& page_list, std::size_t num_page
|
||||
chosen_manager.Free(it.GetAddress(), min_num_pages);
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
std::size_t KMemoryManager::Impl::CalculateManagementOverheadSize(std::size_t region_size) {
|
||||
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
|
||||
ResultCode AddBlock(u64 address, u64 num_pages) {
|
||||
if (!num_pages) {
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
if (!nodes.empty()) {
|
||||
const auto node = nodes.back();
|
||||
@@ -82,7 +82,7 @@ public:
|
||||
}
|
||||
}
|
||||
nodes.push_back({address, num_pages});
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -292,7 +292,7 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory
|
||||
|
||||
block_manager->Update(addr, num_pages, state, perm);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
|
||||
@@ -329,14 +329,14 @@ ResultCode KPageTable::MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std:
|
||||
KMemoryAttribute::Locked);
|
||||
block_manager->Update(dst_addr, num_pages, KMemoryState::AliasCode);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
|
||||
if (!size) {
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
const std::size_t num_pages{size / PageSize};
|
||||
@@ -360,7 +360,7 @@ ResultCode KPageTable::UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, st
|
||||
block_manager->Update(src_addr, num_pages, KMemoryState::Normal,
|
||||
KMemoryPermission::ReadAndWrite);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void KPageTable::MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end) {
|
||||
@@ -408,7 +408,7 @@ ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
|
||||
});
|
||||
|
||||
if (mapped_size == size) {
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
const std::size_t remaining_size{size - mapped_size};
|
||||
@@ -440,14 +440,14 @@ ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
|
||||
KMemoryAttribute::None, KMemoryState::Normal,
|
||||
KMemoryPermission::ReadAndWrite, KMemoryAttribute::None);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
|
||||
const VAddr end_addr{addr + size};
|
||||
ResultCode result{RESULT_SUCCESS};
|
||||
ResultCode result{ResultSuccess};
|
||||
std::size_t mapped_size{};
|
||||
|
||||
// Verify that the region can be unmapped
|
||||
@@ -468,7 +468,7 @@ ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
|
||||
}
|
||||
|
||||
if (!mapped_size) {
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
CASCADE_CODE(UnmapMemory(addr, size));
|
||||
@@ -477,14 +477,14 @@ ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
|
||||
process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size);
|
||||
physical_memory_usage -= mapped_size;
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::UnmapMemory(VAddr addr, std::size_t size) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
|
||||
const VAddr end_addr{addr + size};
|
||||
ResultCode result{RESULT_SUCCESS};
|
||||
ResultCode result{ResultSuccess};
|
||||
KPageLinkedList page_linked_list;
|
||||
|
||||
// Unmap each region within the range
|
||||
@@ -513,7 +513,7 @@ ResultCode KPageTable::UnmapMemory(VAddr addr, std::size_t size) {
|
||||
|
||||
block_manager->Update(addr, num_pages, KMemoryState::Free);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) {
|
||||
@@ -552,7 +552,7 @@ ResultCode KPageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) {
|
||||
block_manager->Update(dst_addr, num_pages, KMemoryState::Stack,
|
||||
KMemoryPermission::ReadAndWrite);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size) {
|
||||
@@ -594,7 +594,7 @@ ResultCode KPageTable::Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size) {
|
||||
block_manager->Update(src_addr, num_pages, src_state, KMemoryPermission::ReadAndWrite);
|
||||
block_manager->Update(dst_addr, num_pages, KMemoryState::Free);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
|
||||
@@ -616,7 +616,7 @@ ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_l
|
||||
cur_addr += node.GetNumPages() * PageSize;
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
|
||||
@@ -638,7 +638,7 @@ ResultCode KPageTable::MapPages(VAddr addr, KPageLinkedList& page_linked_list, K
|
||||
|
||||
block_manager->Update(addr, num_pages, state, perm);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) {
|
||||
@@ -655,7 +655,7 @@ ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked
|
||||
cur_addr += node.GetNumPages() * PageSize;
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
|
||||
@@ -677,7 +677,7 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
|
||||
|
||||
block_manager->Update(addr, num_pages, state, KMemoryPermission::None);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::SetCodeMemoryPermission(VAddr addr, std::size_t size,
|
||||
@@ -708,7 +708,7 @@ ResultCode KPageTable::SetCodeMemoryPermission(VAddr addr, std::size_t size,
|
||||
|
||||
// Return early if there is nothing to change
|
||||
if (state == prev_state && perm == prev_perm) {
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
if ((prev_perm & KMemoryPermission::Execute) != (perm & KMemoryPermission::Execute)) {
|
||||
@@ -725,7 +725,7 @@ ResultCode KPageTable::SetCodeMemoryPermission(VAddr addr, std::size_t size,
|
||||
|
||||
block_manager->Update(addr, num_pages, state, perm);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
KMemoryInfo KPageTable::QueryInfoImpl(VAddr addr) {
|
||||
@@ -758,7 +758,7 @@ ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemo
|
||||
|
||||
block_manager->Update(addr, size / PageSize, state, perm, attribute | KMemoryAttribute::Locked);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
|
||||
@@ -775,7 +775,7 @@ ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
|
||||
|
||||
block_manager->Update(addr, size / PageSize, state, KMemoryPermission::ReadAndWrite);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryAttribute mask,
|
||||
@@ -797,13 +797,13 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryA
|
||||
|
||||
block_manager->Update(addr, size / PageSize, state, perm, attribute);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::SetHeapCapacity(std::size_t new_heap_capacity) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
heap_capacity = new_heap_capacity;
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultVal<VAddr> KPageTable::SetHeapSize(std::size_t size) {
|
||||
@@ -911,7 +911,7 @@ ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
|
||||
},
|
||||
perm);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) {
|
||||
@@ -934,13 +934,13 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
|
||||
},
|
||||
perm);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
|
||||
block_manager = std::make_unique<KMemoryBlockManager>(start, end);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
bool KPageTable::IsRegionMapped(VAddr address, u64 size) {
|
||||
@@ -1006,7 +1006,7 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageLin
|
||||
addr += size;
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm,
|
||||
@@ -1033,7 +1033,7 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermiss
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
constexpr VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
|
||||
@@ -1164,7 +1164,7 @@ constexpr ResultCode KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemo
|
||||
return ResultInvalidCurrentMemory;
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
|
||||
@@ -1223,7 +1223,7 @@ ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermissi
|
||||
*out_attr = first_attr & static_cast<KMemoryAttribute>(~ignore_attr);
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -59,7 +59,7 @@ ResultCode KPort::EnqueueSession(KServerSession* session) {
|
||||
server.EnqueueSession(session);
|
||||
server.GetSessionRequestHandler()->ClientConnected(server.AcceptSession());
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -142,7 +142,7 @@ ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::st
|
||||
// Open a reference to the resource limit.
|
||||
process->resource_limit->Open();
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
KResourceLimit* KProcess::GetResourceLimit() const {
|
||||
@@ -201,17 +201,15 @@ bool KProcess::ReleaseUserException(KThread* thread) {
|
||||
|
||||
// Remove waiter thread.
|
||||
s32 num_waiters{};
|
||||
KThread* next = thread->RemoveWaiterByKey(
|
||||
std::addressof(num_waiters),
|
||||
reinterpret_cast<uintptr_t>(std::addressof(exception_thread)));
|
||||
if (next != nullptr) {
|
||||
if (next->GetState() == ThreadState::Waiting) {
|
||||
next->SetState(ThreadState::Runnable);
|
||||
} else {
|
||||
KScheduler::SetSchedulerUpdateNeeded(kernel);
|
||||
}
|
||||
if (KThread* next = thread->RemoveWaiterByKey(
|
||||
std::addressof(num_waiters),
|
||||
reinterpret_cast<uintptr_t>(std::addressof(exception_thread)));
|
||||
next != nullptr) {
|
||||
next->SetState(ThreadState::Runnable);
|
||||
}
|
||||
|
||||
KScheduler::SetSchedulerUpdateNeeded(kernel);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -258,7 +256,7 @@ ResultCode KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAdd
|
||||
// Open a reference to the shared memory.
|
||||
shmem->Open();
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void KProcess::RemoveSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address,
|
||||
@@ -291,7 +289,7 @@ ResultCode KProcess::Reset() {
|
||||
|
||||
// Clear signaled.
|
||||
is_signaled = false;
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
|
||||
@@ -524,7 +522,7 @@ ResultCode KProcess::AllocateMainThreadStack(std::size_t stack_size) {
|
||||
|
||||
main_thread_stack_top += main_thread_stack_size;
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -310,7 +310,7 @@ public:
|
||||
*
|
||||
* @param metadata The provided metadata to load process specific info from.
|
||||
*
|
||||
* @returns RESULT_SUCCESS if all relevant metadata was able to be
|
||||
* @returns ResultSuccess if all relevant metadata was able to be
|
||||
* loaded and parsed. Otherwise, an error code is returned.
|
||||
*/
|
||||
ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size);
|
||||
|
||||
@@ -36,13 +36,13 @@ ResultCode KReadableEvent::Signal() {
|
||||
NotifyAvailable();
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KReadableEvent::Clear() {
|
||||
Reset();
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KReadableEvent::Reset() {
|
||||
@@ -53,7 +53,7 @@ ResultCode KReadableEvent::Reset() {
|
||||
}
|
||||
|
||||
is_signaled = false;
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -21,9 +21,9 @@ public:
|
||||
explicit KReadableEvent(KernelCore& kernel_);
|
||||
~KReadableEvent() override;
|
||||
|
||||
void Initialize(KEvent* parent_, std::string&& name_) {
|
||||
void Initialize(KEvent* parent_event_, std::string&& name_) {
|
||||
is_signaled = false;
|
||||
parent = parent_;
|
||||
parent = parent_event_;
|
||||
name = std::move(name_);
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ ResultCode KResourceLimit::SetLimitValue(LimitableResource which, s64 value) {
|
||||
|
||||
limit_values[index] = value;
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
bool KResourceLimit::Reserve(LimitableResource which, s64 value) {
|
||||
@@ -117,7 +117,7 @@ bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) {
|
||||
if (current_hints[index] + value <= limit_values[index] &&
|
||||
(timeout < 0 || core_timing->GetGlobalTimeNs().count() < timeout)) {
|
||||
waiter_count++;
|
||||
cond_var.Wait(&lock, timeout);
|
||||
cond_var.Wait(&lock, timeout, false);
|
||||
waiter_count--;
|
||||
} else {
|
||||
break;
|
||||
|
||||
@@ -659,7 +659,6 @@ void KScheduler::Unload(KThread* thread) {
|
||||
|
||||
if (thread) {
|
||||
if (thread->IsCallingSvc()) {
|
||||
system.ArmInterface(core_id).ExceptionalExit();
|
||||
thread->ClearIsCallingSvc();
|
||||
}
|
||||
if (!thread->IsTerminationRequested()) {
|
||||
|
||||
@@ -17,9 +17,9 @@ namespace Kernel {
|
||||
KServerPort::KServerPort(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
|
||||
KServerPort::~KServerPort() = default;
|
||||
|
||||
void KServerPort::Initialize(KPort* parent_, std::string&& name_) {
|
||||
void KServerPort::Initialize(KPort* parent_port_, std::string&& name_) {
|
||||
// Set member variables.
|
||||
parent = parent_;
|
||||
parent = parent_port_;
|
||||
name = std::move(name_);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
explicit KServerPort(KernelCore& kernel_);
|
||||
~KServerPort() override;
|
||||
|
||||
void Initialize(KPort* parent_, std::string&& name_);
|
||||
void Initialize(KPort* parent_port_, std::string&& name_);
|
||||
|
||||
/// Whether or not this server port has an HLE handler available.
|
||||
bool HasSessionRequestHandler() const {
|
||||
|
||||
@@ -8,13 +8,16 @@
|
||||
#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"
|
||||
#include "core/hle/kernel/k_client_port.h"
|
||||
#include "core/hle/kernel/k_handle_table.h"
|
||||
#include "core/hle/kernel/k_port.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_server_port.h"
|
||||
#include "core/hle/kernel/k_server_session.h"
|
||||
#include "core/hle/kernel/k_session.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
@@ -23,18 +26,21 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
KServerSession::KServerSession(KernelCore& kernel_)
|
||||
: KSynchronizationObject{kernel_}, manager{std::make_shared<SessionRequestManager>()} {}
|
||||
KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
|
||||
|
||||
KServerSession::~KServerSession() {
|
||||
kernel.ReleaseServiceThread(service_thread);
|
||||
}
|
||||
KServerSession::~KServerSession() {}
|
||||
|
||||
void KServerSession::Initialize(KSession* parent_, std::string&& name_) {
|
||||
void KServerSession::Initialize(KSession* parent_session_, std::string&& name_,
|
||||
std::shared_ptr<SessionRequestManager> manager_) {
|
||||
// Set member variables.
|
||||
parent = parent_;
|
||||
parent = parent_session_;
|
||||
name = std::move(name_);
|
||||
service_thread = kernel.CreateServiceThread(name);
|
||||
|
||||
if (manager_) {
|
||||
manager = manager_;
|
||||
} else {
|
||||
manager = std::make_shared<SessionRequestManager>(kernel);
|
||||
}
|
||||
}
|
||||
|
||||
void KServerSession::Destroy() {
|
||||
@@ -71,7 +77,7 @@ std::size_t KServerSession::NumDomainRequestHandlers() const {
|
||||
|
||||
ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
|
||||
if (!context.HasDomainMessageHeader()) {
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
// Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
|
||||
@@ -88,7 +94,7 @@ ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& co
|
||||
"to {} needed to return a new interface!",
|
||||
object_id, name);
|
||||
UNREACHABLE();
|
||||
return RESULT_SUCCESS; // Ignore error if asserts are off
|
||||
return ResultSuccess; // Ignore error if asserts are off
|
||||
}
|
||||
return manager->DomainHandler(object_id - 1)->HandleSyncRequest(*this, context);
|
||||
|
||||
@@ -98,14 +104,14 @@ ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& co
|
||||
manager->CloseDomainHandler(object_id - 1);
|
||||
|
||||
IPC::ResponseBuilder rb{context, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
return RESULT_SUCCESS;
|
||||
rb.Push(ResultSuccess);
|
||||
return ResultSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_CRITICAL(IPC, "Unknown domain command={}", domain_message_header.command.Value());
|
||||
ASSERT(false);
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) {
|
||||
@@ -114,23 +120,46 @@ ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memor
|
||||
|
||||
context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
|
||||
|
||||
if (auto strong_ptr = service_thread.lock()) {
|
||||
strong_ptr->QueueSyncRequest(*parent, std::move(context));
|
||||
return RESULT_SUCCESS;
|
||||
// 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!");
|
||||
}
|
||||
} else {
|
||||
ASSERT_MSG(false, "handler is invalid!");
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) {
|
||||
ResultCode result = RESULT_SUCCESS;
|
||||
ResultCode result = ResultSuccess;
|
||||
|
||||
// If the session has been converted to a domain, handle the domain request
|
||||
if (IsDomain() && context.HasDomainMessageHeader()) {
|
||||
result = HandleDomainSyncRequest(context);
|
||||
// If there is no domain header, the regular session handler is used
|
||||
} else if (manager->HasSessionHandler()) {
|
||||
// If this ServerSession has an associated HLE handler, forward the request to it.
|
||||
result = manager->SessionHandler().HandleSyncRequest(*this, context);
|
||||
if (manager->HasSessionRequestHandler(context)) {
|
||||
if (IsDomain() && context.HasDomainMessageHeader()) {
|
||||
result = HandleDomainSyncRequest(context);
|
||||
// If there is no domain header, the regular session handler is used
|
||||
} else if (manager->HasSessionHandler()) {
|
||||
// If this ServerSession has an associated HLE handler, forward the request to it.
|
||||
result = manager->SessionHandler().HandleSyncRequest(*this, context);
|
||||
}
|
||||
} else {
|
||||
ASSERT_MSG(false, "Session handler is invalid, stubbing response!");
|
||||
IPC::ResponseBuilder rb(context, 2);
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
if (convert_to_domain) {
|
||||
|
||||
@@ -32,6 +32,7 @@ class HLERequestContext;
|
||||
class KernelCore;
|
||||
class KSession;
|
||||
class SessionRequestHandler;
|
||||
class SessionRequestManager;
|
||||
class KThread;
|
||||
|
||||
class KServerSession final : public KSynchronizationObject,
|
||||
@@ -46,7 +47,8 @@ public:
|
||||
|
||||
void Destroy() override;
|
||||
|
||||
void Initialize(KSession* parent_, std::string&& name_);
|
||||
void Initialize(KSession* parent_session_, std::string&& name_,
|
||||
std::shared_ptr<SessionRequestManager> manager_);
|
||||
|
||||
KSession* GetParent() {
|
||||
return parent;
|
||||
@@ -60,15 +62,14 @@ public:
|
||||
|
||||
void OnClientClosed();
|
||||
|
||||
/**
|
||||
* Sets the HLE handler for the session. This handler will be called to service IPC requests
|
||||
* instead of the regular IPC machinery. (The regular IPC machinery is currently not
|
||||
* implemented.)
|
||||
*/
|
||||
void SetSessionHandler(SessionRequestHandlerPtr handler) {
|
||||
void ClientConnected(SessionRequestHandlerPtr handler) {
|
||||
manager->SetSessionHandler(std::move(handler));
|
||||
}
|
||||
|
||||
void ClientDisconnected() {
|
||||
manager = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a sync request from the emulated application.
|
||||
*
|
||||
@@ -104,16 +105,6 @@ public:
|
||||
return manager;
|
||||
}
|
||||
|
||||
/// Gets the session request manager, which forwards requests to the underlying service
|
||||
const std::shared_ptr<SessionRequestManager>& GetSessionRequestManager() const {
|
||||
return manager;
|
||||
}
|
||||
|
||||
/// Sets the session request manager, which forwards requests to the underlying service
|
||||
void SetSessionRequestManager(std::shared_ptr<SessionRequestManager> manager_) {
|
||||
manager = std::move(manager_);
|
||||
}
|
||||
|
||||
private:
|
||||
/// Queues a sync request from the emulated application.
|
||||
ResultCode QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory);
|
||||
@@ -131,9 +122,6 @@ private:
|
||||
/// When set to True, converts the session to a domain at the end of the command
|
||||
bool convert_to_domain{};
|
||||
|
||||
/// Thread to dispatch service requests
|
||||
std::weak_ptr<ServiceThread> service_thread;
|
||||
|
||||
/// KSession that owns this KServerSession
|
||||
KSession* parent{};
|
||||
};
|
||||
|
||||
@@ -15,7 +15,8 @@ KSession::KSession(KernelCore& kernel_)
|
||||
: KAutoObjectWithSlabHeapAndContainer{kernel_}, server{kernel_}, client{kernel_} {}
|
||||
KSession::~KSession() = default;
|
||||
|
||||
void KSession::Initialize(KClientPort* port_, const std::string& name_) {
|
||||
void KSession::Initialize(KClientPort* port_, const std::string& name_,
|
||||
std::shared_ptr<SessionRequestManager> manager_) {
|
||||
// Increment reference count.
|
||||
// Because reference count is one on creation, this will result
|
||||
// in a reference count of two. Thus, when both server and client are closed
|
||||
@@ -27,7 +28,7 @@ void KSession::Initialize(KClientPort* port_, const std::string& name_) {
|
||||
KAutoObject::Create(std::addressof(client));
|
||||
|
||||
// Initialize our sub sessions.
|
||||
server.Initialize(this, name_ + ":Server");
|
||||
server.Initialize(this, name_ + ":Server", manager_);
|
||||
client.Initialize(this, name_ + ":Client");
|
||||
|
||||
// Set state and name.
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class SessionRequestManager;
|
||||
|
||||
class KSession final : public KAutoObjectWithSlabHeapAndContainer<KSession, KAutoObjectWithList> {
|
||||
KERNEL_AUTOOBJECT_TRAITS(KSession, KAutoObject);
|
||||
|
||||
@@ -20,7 +22,8 @@ public:
|
||||
explicit KSession(KernelCore& kernel_);
|
||||
~KSession() override;
|
||||
|
||||
void Initialize(KClientPort* port_, const std::string& name_);
|
||||
void Initialize(KClientPort* port_, const std::string& name_,
|
||||
std::shared_ptr<SessionRequestManager> manager_ = nullptr);
|
||||
|
||||
void Finalize() override;
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ ResultCode KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProces
|
||||
// Clear all pages in the memory.
|
||||
std::memset(device_memory_.GetPointer(physical_address_), 0, size_);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void KSharedMemory::Finalize() {
|
||||
|
||||
@@ -4,34 +4,213 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
|
||||
/// This is a placeholder class to manage slab heaps for kernel objects. For now, we just allocate
|
||||
/// these with new/delete, but this can be re-implemented later to allocate these in emulated
|
||||
/// memory.
|
||||
namespace impl {
|
||||
|
||||
class KSlabHeapImpl final : NonCopyable {
|
||||
public:
|
||||
struct Node {
|
||||
Node* next{};
|
||||
};
|
||||
|
||||
constexpr KSlabHeapImpl() = default;
|
||||
|
||||
void Initialize(std::size_t size) {
|
||||
ASSERT(head == nullptr);
|
||||
obj_size = size;
|
||||
}
|
||||
|
||||
constexpr std::size_t GetObjectSize() const {
|
||||
return obj_size;
|
||||
}
|
||||
|
||||
Node* GetHead() const {
|
||||
return head;
|
||||
}
|
||||
|
||||
void* Allocate() {
|
||||
Node* ret = head.load();
|
||||
|
||||
do {
|
||||
if (ret == nullptr) {
|
||||
break;
|
||||
}
|
||||
} while (!head.compare_exchange_weak(ret, ret->next));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Free(void* obj) {
|
||||
Node* node = static_cast<Node*>(obj);
|
||||
|
||||
Node* cur_head = head.load();
|
||||
do {
|
||||
node->next = cur_head;
|
||||
} while (!head.compare_exchange_weak(cur_head, node));
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<Node*> head{};
|
||||
std::size_t obj_size{};
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
class KSlabHeapBase : NonCopyable {
|
||||
public:
|
||||
constexpr KSlabHeapBase() = default;
|
||||
|
||||
constexpr bool Contains(uintptr_t addr) const {
|
||||
return start <= addr && addr < end;
|
||||
}
|
||||
|
||||
constexpr std::size_t GetSlabHeapSize() const {
|
||||
return (end - start) / GetObjectSize();
|
||||
}
|
||||
|
||||
constexpr std::size_t GetObjectSize() const {
|
||||
return impl.GetObjectSize();
|
||||
}
|
||||
|
||||
constexpr uintptr_t GetSlabHeapAddress() const {
|
||||
return start;
|
||||
}
|
||||
|
||||
std::size_t GetObjectIndexImpl(const void* obj) const {
|
||||
return (reinterpret_cast<uintptr_t>(obj) - start) / GetObjectSize();
|
||||
}
|
||||
|
||||
std::size_t GetPeakIndex() const {
|
||||
return GetObjectIndexImpl(reinterpret_cast<const void*>(peak));
|
||||
}
|
||||
|
||||
void* AllocateImpl() {
|
||||
return impl.Allocate();
|
||||
}
|
||||
|
||||
void FreeImpl(void* obj) {
|
||||
// Don't allow freeing an object that wasn't allocated from this heap
|
||||
ASSERT(Contains(reinterpret_cast<uintptr_t>(obj)));
|
||||
|
||||
impl.Free(obj);
|
||||
}
|
||||
|
||||
void InitializeImpl(std::size_t obj_size, void* memory, std::size_t memory_size) {
|
||||
// Ensure we don't initialize a slab using null memory
|
||||
ASSERT(memory != nullptr);
|
||||
|
||||
// Initialize the base allocator
|
||||
impl.Initialize(obj_size);
|
||||
|
||||
// Set our tracking variables
|
||||
const std::size_t num_obj = (memory_size / obj_size);
|
||||
start = reinterpret_cast<uintptr_t>(memory);
|
||||
end = start + num_obj * obj_size;
|
||||
peak = start;
|
||||
|
||||
// Free the objects
|
||||
u8* cur = reinterpret_cast<u8*>(end);
|
||||
|
||||
for (std::size_t i{}; i < num_obj; i++) {
|
||||
cur -= obj_size;
|
||||
impl.Free(cur);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
using Impl = impl::KSlabHeapImpl;
|
||||
|
||||
Impl impl;
|
||||
uintptr_t peak{};
|
||||
uintptr_t start{};
|
||||
uintptr_t end{};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class KSlabHeap final : NonCopyable {
|
||||
class KSlabHeap final : public KSlabHeapBase {
|
||||
public:
|
||||
KSlabHeap() = default;
|
||||
enum class AllocationType {
|
||||
Host,
|
||||
Guest,
|
||||
};
|
||||
|
||||
void Initialize([[maybe_unused]] void* memory, [[maybe_unused]] std::size_t memory_size) {
|
||||
// Placeholder that should initialize the backing slab heap implementation.
|
||||
explicit constexpr KSlabHeap(AllocationType allocation_type_ = AllocationType::Host)
|
||||
: KSlabHeapBase(), allocation_type{allocation_type_} {}
|
||||
|
||||
void Initialize(void* memory, std::size_t memory_size) {
|
||||
if (allocation_type == AllocationType::Guest) {
|
||||
InitializeImpl(sizeof(T), memory, memory_size);
|
||||
}
|
||||
}
|
||||
|
||||
T* Allocate() {
|
||||
return new T();
|
||||
switch (allocation_type) {
|
||||
case AllocationType::Host:
|
||||
// Fallback for cases where we do not yet support allocating guest memory from the slab
|
||||
// heap, such as for kernel memory regions.
|
||||
return new T;
|
||||
|
||||
case AllocationType::Guest:
|
||||
T* obj = static_cast<T*>(AllocateImpl());
|
||||
if (obj != nullptr) {
|
||||
new (obj) T();
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
T* AllocateWithKernel(KernelCore& kernel) {
|
||||
return new T(kernel);
|
||||
switch (allocation_type) {
|
||||
case AllocationType::Host:
|
||||
// Fallback for cases where we do not yet support allocating guest memory from the slab
|
||||
// heap, such as for kernel memory regions.
|
||||
return new T(kernel);
|
||||
|
||||
case AllocationType::Guest:
|
||||
T* obj = static_cast<T*>(AllocateImpl());
|
||||
if (obj != nullptr) {
|
||||
new (obj) T(kernel);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Free(T* obj) {
|
||||
delete obj;
|
||||
switch (allocation_type) {
|
||||
case AllocationType::Host:
|
||||
// Fallback for cases where we do not yet support allocating guest memory from the slab
|
||||
// heap, such as for kernel memory regions.
|
||||
delete obj;
|
||||
return;
|
||||
|
||||
case AllocationType::Guest:
|
||||
FreeImpl(obj);
|
||||
return;
|
||||
}
|
||||
|
||||
UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type);
|
||||
}
|
||||
|
||||
constexpr std::size_t GetObjectIndex(const T* obj) const {
|
||||
return GetObjectIndexImpl(obj);
|
||||
}
|
||||
|
||||
private:
|
||||
const AllocationType allocation_type;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -38,7 +38,7 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
|
||||
if (objects[i]->IsSignaled()) {
|
||||
*out_index = i;
|
||||
slp.CancelSleep();
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
|
||||
kernel_ctx.TimeManager().UnscheduleTimeEvent(thread);
|
||||
|
||||
// Get the wait result.
|
||||
ResultCode wait_result{RESULT_SUCCESS};
|
||||
ResultCode wait_result{ResultSuccess};
|
||||
s32 sync_index = -1;
|
||||
{
|
||||
KScopedSchedulerLock lock(kernel_ctx);
|
||||
|
||||
@@ -43,7 +43,7 @@ protected:
|
||||
|
||||
void NotifyAvailable(ResultCode result);
|
||||
void NotifyAvailable() {
|
||||
return this->NotifyAvailable(RESULT_SUCCESS);
|
||||
return this->NotifyAvailable(ResultSuccess);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -168,13 +168,13 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
|
||||
std::memset(static_cast<void*>(std::addressof(GetStackParameters())), 0,
|
||||
sizeof(StackParameters));
|
||||
|
||||
// Setup the TLS, if needed.
|
||||
if (type == ThreadType::User) {
|
||||
tls_address = owner->CreateTLSRegion();
|
||||
}
|
||||
|
||||
// Set parent, if relevant.
|
||||
if (owner != nullptr) {
|
||||
// Setup the TLS, if needed.
|
||||
if (type == ThreadType::User) {
|
||||
tls_address = owner->CreateTLSRegion();
|
||||
}
|
||||
|
||||
parent = owner;
|
||||
parent->Open();
|
||||
parent->IncrementThreadCount();
|
||||
@@ -205,7 +205,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
|
||||
}
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg,
|
||||
@@ -219,7 +219,7 @@ ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uint
|
||||
thread->host_context =
|
||||
std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KThread::InitializeDummyThread(KThread* thread) {
|
||||
@@ -460,7 +460,7 @@ ResultCode KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) {
|
||||
*out_ideal_core = virtual_ideal_core_id;
|
||||
*out_affinity_mask = virtual_affinity_mask;
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask) {
|
||||
@@ -476,7 +476,7 @@ ResultCode KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_m
|
||||
*out_affinity_mask = original_physical_affinity_mask.GetAffinityMask();
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
|
||||
@@ -599,7 +599,7 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
|
||||
}
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void KThread::SetBasePriority(s32 value) {
|
||||
@@ -778,7 +778,7 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
|
||||
}
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KThread::GetThreadContext3(std::vector<u8>& out) {
|
||||
@@ -813,7 +813,7 @@ ResultCode KThread::GetThreadContext3(std::vector<u8>& out) {
|
||||
}
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void KThread::AddWaiterImpl(KThread* thread) {
|
||||
@@ -970,7 +970,7 @@ ResultCode KThread::Run() {
|
||||
|
||||
// Set our state and finish.
|
||||
SetState(ThreadState::Runnable);
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1020,7 +1020,7 @@ ResultCode KThread::Sleep(s64 timeout) {
|
||||
// Cancel the timer.
|
||||
kernel.TimeManager().UnscheduleTimeEvent(this);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void KThread::SetState(ThreadState state) {
|
||||
|
||||
@@ -684,7 +684,7 @@ private:
|
||||
u32 address_key_value{};
|
||||
u32 suspend_request_flags{};
|
||||
u32 suspend_allowed_flags{};
|
||||
ResultCode wait_result{RESULT_SUCCESS};
|
||||
ResultCode wait_result{ResultSuccess};
|
||||
s32 base_priority{};
|
||||
s32 physical_ideal_core_id{};
|
||||
s32 virtual_ideal_core_id{};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user