Compare commits
253 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6b0d16e1a3 | |||
| 9c67334031 | |||
| 1fb33bd1e1 | |||
| 405d685101 | |||
| e5a446a0df | |||
| 0e61d711e2 | |||
| 60e0d4a177 | |||
| f426fd95fe | |||
| fa660190ff | |||
| 48b4eca28a | |||
| e94bcf03cb | |||
| 8ca02794c5 | |||
| ef5184cf1c | |||
| 9e27624a19 | |||
| cf202f3718 | |||
| 18fcc03b3c | |||
| 6fa3faec65 | |||
| 040a01a5dd | |||
| 8cc5ad8742 | |||
| 540c1696d1 | |||
| 651f6598ac | |||
| 70ea1c2000 | |||
| 08091ff3e3 | |||
| b51c1544b9 | |||
| 7dfe35eca6 | |||
| 69768ec71e | |||
| e4d55e4ee4 | |||
| c4bc7ce7e2 | |||
| 211da31b34 | |||
| c973029374 | |||
| b832942b6e | |||
| 5eb30c7827 | |||
| 33ea0fdfe8 | |||
| c9bb888adf | |||
| d05b183f21 | |||
| 83eb9cf7da | |||
| 0e84fd95e2 | |||
| 18123ff958 | |||
| ceb829cc33 | |||
| bb55d2e701 | |||
| 0c176ce828 | |||
| b34d3d5882 | |||
| 26a1d4fc37 | |||
| ea41c53ab1 | |||
| 766941f1a3 | |||
| d03afd6f4b | |||
| 4eece4d35d | |||
| 770f23db34 | |||
| cbaf642ffe | |||
| d581a4a367 | |||
| 3161b34ff6 | |||
| 71c0e20f95 | |||
| ad9e5bc5b7 | |||
| a4472b5526 | |||
| 6a0d8b2aa1 | |||
| aa55c62159 | |||
| dc520a487d | |||
| aa97f39ba8 | |||
| df38c03a09 | |||
| 4c198bbf06 | |||
| cf0f821565 | |||
| 8baf036cdc | |||
| 7283010305 | |||
| a5d8703235 | |||
| 6f6be615f3 | |||
| 862afa8514 | |||
| e6fe40428c | |||
| 85527cc7c7 | |||
| d7d7ae8219 | |||
| 717c8ded82 | |||
| 9fc1bcc7b2 | |||
| 75596c07e0 | |||
| ece22fcbc7 | |||
| 38e4382f53 | |||
| 37de88040c | |||
| 05ae0cab0e | |||
| 119315af08 | |||
| 661fe06d9d | |||
| ba21ba0c5c | |||
| 32d7faafa8 | |||
| b7b47f3099 | |||
| 6f941121e6 | |||
| 6636b81573 | |||
| 1f21fa866d | |||
| 84d130f143 | |||
| d928ba8e40 | |||
| 3aab7d4473 | |||
| 6b6c02f541 | |||
| 50bfacca88 | |||
| 0cb9bc12fc | |||
| 6257461684 | |||
| d353c45f7d | |||
| f76b4417e6 | |||
| 0897f4f96c | |||
| 6d4f411c08 | |||
| 37b17252d1 | |||
| ddd3f48736 | |||
| 46322be735 | |||
| 3794851f7f | |||
| 74275d0968 | |||
| ca0d9ef4b8 | |||
| 846b6fba82 | |||
| 75ab52f05b | |||
| 83f649240e | |||
| f325fcb131 | |||
| 5e4ab2d42c | |||
| de4afde065 | |||
| 77b74f5d95 | |||
| 633411c20f | |||
| 2228383322 | |||
| 7aa91c8d9c | |||
| 7837185f0a | |||
| 983f2b7074 | |||
| 7f0d0dd177 | |||
| b42b894785 | |||
| 4e9adae5da | |||
| f39d2cf78b | |||
| d8ff939edc | |||
| eec3184bb0 | |||
| 67e0d38152 | |||
| 808e22984f | |||
| e09756b2df | |||
| 8f00c59462 | |||
| 1cdd2d5204 | |||
| ccfdb7c1af | |||
| 6f0f7f1547 | |||
| 316a2c1715 | |||
| d867ae5ab6 | |||
| c4d91488d9 | |||
| 72bff8ba11 | |||
| 84d15c7f47 | |||
| dce242858a | |||
| 8ce6256722 | |||
| 3e6840a74c | |||
| 25d53e66d1 | |||
| 20118075c5 | |||
| 2cdfbbc07d | |||
| cdb9fe978f | |||
| 86a3a0b1b4 | |||
| f6e7cae62c | |||
| 2dd6a2352d | |||
| e0ec9ffc36 | |||
| 041eb5bf57 | |||
| 8b4d5aeb4f | |||
| d8e3380ea5 | |||
| e59bd6c335 | |||
| 77803d96be | |||
| fa9b7db76f | |||
| 0ec1801bc1 | |||
| fa913a702f | |||
| 3c38bd7cf0 | |||
| 165bce3c2d | |||
| 1a378a7769 | |||
| cbb6c24215 | |||
| 1689e0a71f | |||
| 2e782a154d | |||
| 0313ee7793 | |||
| 05f2673648 | |||
| 2d90a927c9 | |||
| 2ccbf5abdd | |||
| 120cd450e5 | |||
| f51c71e956 | |||
| bb31b0f261 | |||
| f86774c1ac | |||
| 42c4ef7373 | |||
| c7e079a5d4 | |||
| 6908ea2284 | |||
| 347432524c | |||
| b02c3f2314 | |||
| 3822e31323 | |||
| cae108404a | |||
| bad3025951 | |||
| f3c40f4a20 | |||
| e6ab1f673b | |||
| 93297d14d8 | |||
| 91c410c918 | |||
| 496695618a | |||
| 0860fffd78 | |||
| 2f90694797 | |||
| 3e0aaeba98 | |||
| 82fdfb33ac | |||
| 1f54cd4ac7 | |||
| efaedcab31 | |||
| 49682a0481 | |||
| aa9e9052a6 | |||
| 93a7058d8e | |||
| f16db300c6 | |||
| 969387a79a | |||
| 3968faec06 | |||
| 7f66050f0c | |||
| 0b181eeef4 | |||
| 6b71530fa8 | |||
| a6628e8dba | |||
| 9e16837088 | |||
| c0b1bdd237 | |||
| d4c0b7b437 | |||
| 7daf751b8d | |||
| 9524e28d20 | |||
| fca195b4fb | |||
| 3efb8eb2dc | |||
| 5ffb8b8039 | |||
| 925fb63478 | |||
| 560bca57a2 | |||
| 97879faea4 | |||
| 470e89a8ed | |||
| 7bd3930939 | |||
| b8a70c9999 | |||
| 3cb4498142 | |||
| a264b54022 | |||
| 638fa6170a | |||
| 11f85ea713 | |||
| 829e82e264 | |||
| a4d11f4427 | |||
| 1b787adbd0 | |||
| abcc009dff | |||
| 79bcb38321 | |||
| 8d4e026d05 | |||
| ff26190d42 | |||
| d00245d444 | |||
| 1baedfa12c | |||
| ed591934fb | |||
| 58eb6953d1 | |||
| 2bb41cffca | |||
| 57a77e9ff4 | |||
| d02ccfb15d | |||
| 9ec5f75f43 | |||
| 345b9e6a08 | |||
| 25dcaf1eca | |||
| 113a5ed68f | |||
| 47b8160666 | |||
| cb073f95dc | |||
| e63a5459e3 | |||
| 6e1c6297a3 | |||
| b6119a55f9 | |||
| 0cfd90004b | |||
| 2cc9d94060 | |||
| 0101ef9fb1 | |||
| 9393f90ccf | |||
| 5000d814af | |||
| 8649c46c74 | |||
| 1deb997eba | |||
| 282cd3e5fe | |||
| 40d9107b23 | |||
| 23b6569fc2 | |||
| 99507d0188 | |||
| 88ccdaf10a | |||
| bffbaddb79 | |||
| ae453ab6a8 | |||
| 20139f8a55 | |||
| 40af1111c2 | |||
| d4cb0eac87 | |||
| c864cb5772 | |||
| 9a95c7fa14 |
@@ -133,13 +133,13 @@ if (NOT ENABLE_GENERIC)
|
||||
if (MSVC)
|
||||
detect_architecture("_M_AMD64" x86_64)
|
||||
detect_architecture("_M_IX86" x86)
|
||||
detect_architecture("_M_ARM" ARM)
|
||||
detect_architecture("_M_ARM64" ARM64)
|
||||
detect_architecture("_M_ARM" arm)
|
||||
detect_architecture("_M_ARM64" arm64)
|
||||
else()
|
||||
detect_architecture("__x86_64__" x86_64)
|
||||
detect_architecture("__i386__" x86)
|
||||
detect_architecture("__arm__" ARM)
|
||||
detect_architecture("__aarch64__" ARM64)
|
||||
detect_architecture("__arm__" arm)
|
||||
detect_architecture("__aarch64__" arm64)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -218,11 +218,11 @@ if(ENABLE_QT)
|
||||
set(QT_VERSION 5.15)
|
||||
|
||||
# Check for system Qt on Linux, fallback to bundled Qt
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
if (UNIX AND NOT APPLE)
|
||||
if (NOT YUZU_USE_BUNDLED_QT)
|
||||
find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus Multimedia)
|
||||
endif()
|
||||
if (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT)
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT))
|
||||
# Check for dependencies, then enable bundled Qt download
|
||||
|
||||
# Check that the system GLIBCXX version is compatible
|
||||
@@ -323,7 +323,7 @@ if(ENABLE_QT)
|
||||
|
||||
set(YUZU_QT_NO_CMAKE_SYSTEM_PATH "NO_CMAKE_SYSTEM_PATH")
|
||||
endif()
|
||||
if ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND YUZU_USE_BUNDLED_QT)
|
||||
if (UNIX AND NOT APPLE AND YUZU_USE_BUNDLED_QT)
|
||||
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia DBus ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
|
||||
else()
|
||||
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
|
||||
@@ -541,9 +541,9 @@ add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY
|
||||
# Adjustments for MSVC + Ninja
|
||||
if (MSVC AND CMAKE_GENERATOR STREQUAL "Ninja")
|
||||
add_compile_options(
|
||||
/wd4711 # function 'function' selected for automatic inline expansion
|
||||
/wd4464 # relative include path contains '..'
|
||||
/wd4820 # 'identifier1': '4' bytes padding added after data member 'identifier2'
|
||||
/wd4711 # function 'function' selected for automatic inline expansion
|
||||
/wd4820 # 'bytes' bytes padding added after construct 'member_name'
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 528 B |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 1.9 KiB |
@@ -7,15 +7,14 @@ include(DownloadExternals)
|
||||
|
||||
# xbyak
|
||||
if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
|
||||
add_library(xbyak INTERFACE)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/xbyak/xbyak DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
|
||||
target_include_directories(xbyak SYSTEM INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
|
||||
target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
|
||||
add_subdirectory(xbyak)
|
||||
endif()
|
||||
|
||||
# Dynarmic
|
||||
if (ARCHITECTURE_x86_64)
|
||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
||||
if (ARCHITECTURE_arm64)
|
||||
set(DYNARMIC_FRONTENDS "A32")
|
||||
endif()
|
||||
set(DYNARMIC_NO_BUNDLED_FMT ON)
|
||||
set(DYNARMIC_IGNORE_ASSERTS ON CACHE BOOL "" FORCE)
|
||||
add_subdirectory(dynarmic)
|
||||
|
||||
@@ -845,8 +845,6 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
|
||||
MicroProfile& S = *MicroProfileGet();
|
||||
MP_DEBUG_DUMP_RANGE();
|
||||
int nY = nBaseY - UI.nOffsetY;
|
||||
int64_t nNumBoxes = 0;
|
||||
int64_t nNumLines = 0;
|
||||
|
||||
uint32_t nFrameNext = (S.nFrameCurrent+1) % MICROPROFILE_MAX_FRAME_HISTORY;
|
||||
MicroProfileFrameState* pFrameCurrent = &S.Frames[S.nFrameCurrent];
|
||||
@@ -1149,7 +1147,6 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
|
||||
}
|
||||
}
|
||||
#endif
|
||||
++nNumBoxes;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1165,7 +1162,6 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
|
||||
}
|
||||
nLinesDrawn[nStackPos] = nLineX;
|
||||
MicroProfileDrawLineVertical(nLineX, fYStart + 0.5f, fYEnd + 0.5f, nColor|UI.nOpacityForeground);
|
||||
++nNumLines;
|
||||
}
|
||||
}
|
||||
nStackPos--;
|
||||
|
||||
@@ -58,13 +58,11 @@ if (MSVC)
|
||||
|
||||
# Warnings
|
||||
/W3
|
||||
/we4018 # 'expression': signed/unsigned mismatch
|
||||
/WX
|
||||
|
||||
/we4062 # Enumerator 'identifier' in a switch of enum 'enumeration' is not handled
|
||||
/we4101 # 'identifier': unreferenced local variable
|
||||
/we4189 # 'identifier': local variable is initialized but not referenced
|
||||
/we4265 # 'class': class has virtual functions, but destructor is not virtual
|
||||
/we4267 # 'var': conversion from 'size_t' to 'type', possible loss of data
|
||||
/we4305 # 'context': truncation from 'type1' to 'type2'
|
||||
/we4388 # 'expression': signed/unsigned mismatch
|
||||
/we4389 # 'operator': signed/unsigned mismatch
|
||||
/we4456 # Declaration of 'identifier' hides previous local declaration
|
||||
@@ -75,10 +73,13 @@ if (MSVC)
|
||||
/we4547 # 'operator': operator before comma has no effect; expected operator with side-effect
|
||||
/we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
|
||||
/we4555 # Expression has no effect; expected expression with side-effect
|
||||
/we4715 # 'function': not all control paths return a value
|
||||
/we4834 # Discarding return value of function with 'nodiscard' attribute
|
||||
/we4826 # Conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior.
|
||||
/we5038 # data member 'member1' will be initialized after data member 'member2'
|
||||
/we5233 # explicit lambda capture 'identifier' is not used
|
||||
/we5245 # 'function': unreferenced function with internal linkage has been removed
|
||||
|
||||
/wd4100 # 'identifier': unreferenced formal parameter
|
||||
/wd4324 # 'struct_name': structure was padded due to __declspec(align())
|
||||
)
|
||||
|
||||
if (USE_CCACHE)
|
||||
@@ -99,24 +100,18 @@ if (MSVC)
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE)
|
||||
else()
|
||||
add_compile_options(
|
||||
-Wall
|
||||
-Werror=array-bounds
|
||||
-Werror=implicit-fallthrough
|
||||
-Werror=all
|
||||
-Werror=extra
|
||||
-Werror=missing-declarations
|
||||
-Werror=missing-field-initializers
|
||||
-Werror=reorder
|
||||
-Werror=shadow
|
||||
-Werror=sign-compare
|
||||
-Werror=switch
|
||||
-Werror=uninitialized
|
||||
-Werror=unused-function
|
||||
-Werror=unused-result
|
||||
-Werror=unused-variable
|
||||
-Wextra
|
||||
-Wmissing-declarations
|
||||
-Werror=unused
|
||||
|
||||
-Wno-attributes
|
||||
-Wno-invalid-offsetof
|
||||
-Wno-unused-parameter
|
||||
|
||||
$<$<CXX_COMPILER_ID:Clang>:-Wno-braced-scalar-init>
|
||||
$<$<CXX_COMPILER_ID:Clang>:-Wno-unused-private-field>
|
||||
)
|
||||
|
||||
if (ARCHITECTURE_x86_64)
|
||||
|
||||
@@ -206,27 +206,18 @@ if (MSVC)
|
||||
/we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data
|
||||
/we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch
|
||||
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
||||
/we4456 # Declaration of 'identifier' hides previous local declaration
|
||||
/we4457 # Declaration of 'identifier' hides function parameter
|
||||
/we4458 # Declaration of 'identifier' hides class member
|
||||
/we4459 # Declaration of 'identifier' hides global declaration
|
||||
/we4800 # Implicit conversion from 'type' to bool. Possible information loss
|
||||
)
|
||||
else()
|
||||
target_compile_options(audio_core PRIVATE
|
||||
-Werror=conversion
|
||||
-Werror=ignored-qualifiers
|
||||
-Werror=shadow
|
||||
-Werror=unused-variable
|
||||
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
|
||||
|
||||
-Wno-sign-conversion
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(audio_core PUBLIC common core)
|
||||
if (ARCHITECTURE_x86_64)
|
||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
||||
target_link_libraries(audio_core PRIVATE dynarmic)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ Result System::IsConfigValid(const std::string_view device_name,
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result System::Initialize(std::string& device_name, const AudioInParameter& in_params,
|
||||
Result System::Initialize(std::string device_name, const AudioInParameter& in_params,
|
||||
const u32 handle_, const u64 applet_resource_user_id_) {
|
||||
auto result{IsConfigValid(device_name, in_params)};
|
||||
if (result.IsError()) {
|
||||
|
||||
@@ -97,7 +97,7 @@ public:
|
||||
* @param applet_resource_user_id - Unused.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result Initialize(std::string& device_name, const AudioInParameter& in_params, u32 handle,
|
||||
Result Initialize(std::string device_name, const AudioInParameter& in_params, u32 handle,
|
||||
u64 applet_resource_user_id);
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,8 +49,8 @@ Result System::IsConfigValid(std::string_view device_name,
|
||||
return Service::Audio::ERR_INVALID_CHANNEL_COUNT;
|
||||
}
|
||||
|
||||
Result System::Initialize(std::string& device_name, const AudioOutParameter& in_params, u32 handle_,
|
||||
u64& applet_resource_user_id_) {
|
||||
Result System::Initialize(std::string device_name, const AudioOutParameter& in_params, u32 handle_,
|
||||
u64 applet_resource_user_id_) {
|
||||
auto result = IsConfigValid(device_name, in_params);
|
||||
if (result.IsError()) {
|
||||
return result;
|
||||
|
||||
@@ -88,8 +88,8 @@ public:
|
||||
* @param applet_resource_user_id - Unused.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result Initialize(std::string& device_name, const AudioOutParameter& in_params, u32 handle,
|
||||
u64& applet_resource_user_id);
|
||||
Result Initialize(std::string device_name, const AudioOutParameter& in_params, u32 handle,
|
||||
u64 applet_resource_user_id);
|
||||
|
||||
/**
|
||||
* Start this system.
|
||||
|
||||
@@ -91,7 +91,7 @@ Result InfoUpdater::UpdateVoices(VoiceContext& voice_context,
|
||||
voice_info.Initialize();
|
||||
|
||||
for (u32 channel = 0; channel < in_param.channel_count; channel++) {
|
||||
std::memset(voice_states[channel], 0, sizeof(VoiceState));
|
||||
*voice_states[channel] = {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ void BiquadFilterCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor
|
||||
void BiquadFilterCommand::Process(const ADSP::CommandListProcessor& processor) {
|
||||
auto state_{reinterpret_cast<VoiceState::BiquadFilterState*>(state)};
|
||||
if (needs_init) {
|
||||
std::memset(state_, 0, sizeof(VoiceState::BiquadFilterState));
|
||||
*state_ = {};
|
||||
}
|
||||
|
||||
auto input_buffer{
|
||||
|
||||
@@ -30,7 +30,7 @@ void MultiTapBiquadFilterCommand::Process(const ADSP::CommandListProcessor& proc
|
||||
for (u32 i = 0; i < filter_tap_count; i++) {
|
||||
auto state{reinterpret_cast<VoiceState::BiquadFilterState*>(states[i])};
|
||||
if (needs_init[i]) {
|
||||
std::memset(state, 0, sizeof(VoiceState::BiquadFilterState));
|
||||
*state = {};
|
||||
}
|
||||
|
||||
ApplyBiquadFilterFloat(output_buffer, input_buffer, biquads[i].b, biquads[i].a, *state,
|
||||
|
||||
@@ -26,6 +26,7 @@ void PerformanceManager::CreateImpl(const size_t version) {
|
||||
impl = std::make_unique<
|
||||
PerformanceManagerImpl<PerformanceVersion::Version1, PerformanceFrameHeaderVersion1,
|
||||
PerformanceEntryVersion1, PerformanceDetailVersion1>>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -98,9 +98,8 @@ System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_)
|
||||
: core{core_}, adsp{core.AudioCore().GetADSP()}, adsp_rendered_event{adsp_rendered_event_} {}
|
||||
|
||||
Result System::Initialize(const AudioRendererParameterInternal& params,
|
||||
Kernel::KTransferMemory* transfer_memory, const u64 transfer_memory_size,
|
||||
const u32 process_handle_, const u64 applet_resource_user_id_,
|
||||
const s32 session_id_) {
|
||||
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
|
||||
u32 process_handle_, u64 applet_resource_user_id_, s32 session_id_) {
|
||||
if (!CheckValidRevision(params.revision)) {
|
||||
return Service::Audio::ERR_INVALID_REVISION;
|
||||
}
|
||||
@@ -354,6 +353,8 @@ Result System::Initialize(const AudioRendererParameterInternal& params,
|
||||
|
||||
render_time_limit_percent = 100;
|
||||
drop_voice = params.voice_drop_enabled && params.execution_mode == ExecutionMode::Auto;
|
||||
drop_voice_param = 1.0f;
|
||||
num_voices_dropped = 0;
|
||||
|
||||
allocator.Align(0x40);
|
||||
command_workbuffer_size = allocator.GetRemainingSize();
|
||||
@@ -547,7 +548,7 @@ u32 System::GetRenderingTimeLimit() const {
|
||||
return render_time_limit_percent;
|
||||
}
|
||||
|
||||
void System::SetRenderingTimeLimit(const u32 limit) {
|
||||
void System::SetRenderingTimeLimit(u32 limit) {
|
||||
render_time_limit_percent = limit;
|
||||
}
|
||||
|
||||
@@ -635,7 +636,7 @@ void System::SendCommandToDsp() {
|
||||
}
|
||||
|
||||
u64 System::GenerateCommand(std::span<u8> in_command_buffer,
|
||||
[[maybe_unused]] const u64 command_buffer_size_) {
|
||||
[[maybe_unused]] u64 command_buffer_size_) {
|
||||
PoolMapper::ClearUseState(memory_pool_workbuffer, memory_pool_count);
|
||||
const auto start_time{core.CoreTiming().GetClockTicks()};
|
||||
|
||||
@@ -693,7 +694,8 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer,
|
||||
|
||||
voice_context.SortInfo();
|
||||
|
||||
const auto start_estimated_time{command_buffer.estimated_process_time};
|
||||
const auto start_estimated_time{drop_voice_param *
|
||||
static_cast<f32>(command_buffer.estimated_process_time)};
|
||||
|
||||
command_generator.GenerateVoiceCommands();
|
||||
command_generator.GenerateSubMixCommands();
|
||||
@@ -712,11 +714,16 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer,
|
||||
render_context.behavior->IsAudioRendererProcessingTimeLimit70PercentSupported();
|
||||
time_limit_percent = 70.0f;
|
||||
}
|
||||
|
||||
const auto end_estimated_time{drop_voice_param *
|
||||
static_cast<f32>(command_buffer.estimated_process_time)};
|
||||
const auto estimated_time{start_estimated_time - end_estimated_time};
|
||||
|
||||
const auto time_limit{static_cast<u32>(
|
||||
static_cast<f32>(start_estimated_time - command_buffer.estimated_process_time) +
|
||||
(((time_limit_percent / 100.0f) * 2'880'000.0) *
|
||||
(static_cast<f32>(render_time_limit_percent) / 100.0f)))};
|
||||
num_voices_dropped = DropVoices(command_buffer, start_estimated_time, time_limit);
|
||||
estimated_time + (((time_limit_percent / 100.0f) * 2'880'000.0) *
|
||||
(static_cast<f32>(render_time_limit_percent) / 100.0f)))};
|
||||
num_voices_dropped =
|
||||
DropVoices(command_buffer, static_cast<u32>(start_estimated_time), time_limit);
|
||||
}
|
||||
|
||||
command_list_header->buffer_size = command_buffer.size;
|
||||
@@ -737,24 +744,33 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer,
|
||||
return command_buffer.size;
|
||||
}
|
||||
|
||||
u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_process_time,
|
||||
const u32 time_limit) {
|
||||
f32 System::GetVoiceDropParameter() const {
|
||||
return drop_voice_param;
|
||||
}
|
||||
|
||||
void System::SetVoiceDropParameter(f32 voice_drop_) {
|
||||
drop_voice_param = voice_drop_;
|
||||
}
|
||||
|
||||
u32 System::DropVoices(CommandBuffer& command_buffer, u32 estimated_process_time, u32 time_limit) {
|
||||
u32 i{0};
|
||||
auto command_list{command_buffer.command_list.data() + sizeof(CommandListHeader)};
|
||||
ICommand* cmd{};
|
||||
ICommand* cmd{nullptr};
|
||||
|
||||
for (; i < command_buffer.count; i++) {
|
||||
// Find a first valid voice to drop
|
||||
while (i < command_buffer.count) {
|
||||
cmd = reinterpret_cast<ICommand*>(command_list);
|
||||
if (cmd->type != CommandId::Performance &&
|
||||
cmd->type != CommandId::DataSourcePcmInt16Version1 &&
|
||||
cmd->type != CommandId::DataSourcePcmInt16Version2 &&
|
||||
cmd->type != CommandId::DataSourcePcmFloatVersion1 &&
|
||||
cmd->type != CommandId::DataSourcePcmFloatVersion2 &&
|
||||
cmd->type != CommandId::DataSourceAdpcmVersion1 &&
|
||||
cmd->type != CommandId::DataSourceAdpcmVersion2) {
|
||||
if (cmd->type == CommandId::Performance ||
|
||||
cmd->type == CommandId::DataSourcePcmInt16Version1 ||
|
||||
cmd->type == CommandId::DataSourcePcmInt16Version2 ||
|
||||
cmd->type == CommandId::DataSourcePcmFloatVersion1 ||
|
||||
cmd->type == CommandId::DataSourcePcmFloatVersion2 ||
|
||||
cmd->type == CommandId::DataSourceAdpcmVersion1 ||
|
||||
cmd->type == CommandId::DataSourceAdpcmVersion2) {
|
||||
break;
|
||||
}
|
||||
command_list += cmd->size;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (cmd == nullptr || command_buffer.count == 0 || i >= command_buffer.count) {
|
||||
@@ -767,6 +783,7 @@ u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_proces
|
||||
const auto node_id_type{cmd->node_id >> 28};
|
||||
const auto node_id_base{cmd->node_id & 0xFFF};
|
||||
|
||||
// If the new estimated process time falls below the limit, we're done dropping.
|
||||
if (estimated_process_time <= time_limit) {
|
||||
break;
|
||||
}
|
||||
@@ -775,6 +792,7 @@ u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_proces
|
||||
break;
|
||||
}
|
||||
|
||||
// Don't drop voices marked with the highest priority.
|
||||
auto& voice_info{voice_context.GetInfo(node_id_base)};
|
||||
if (voice_info.priority == HighestVoicePriority) {
|
||||
break;
|
||||
@@ -783,18 +801,23 @@ u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_proces
|
||||
voices_dropped++;
|
||||
voice_info.voice_dropped = true;
|
||||
|
||||
if (i < command_buffer.count) {
|
||||
while (cmd->node_id == node_id) {
|
||||
if (cmd->type == CommandId::DepopPrepare) {
|
||||
cmd->enabled = true;
|
||||
} else if (cmd->type == CommandId::Performance || !cmd->enabled) {
|
||||
cmd->enabled = false;
|
||||
}
|
||||
i++;
|
||||
command_list += cmd->size;
|
||||
cmd = reinterpret_cast<ICommand*>(command_list);
|
||||
// First iteration should drop the voice, and then iterate through all of the commands tied
|
||||
// to the voice. We don't need reverb on a voice which we've just removed, for example.
|
||||
// Depops can't be removed otherwise we'll introduce audio popping, and we don't
|
||||
// remove perf commands. Lower the estimated time for each command dropped.
|
||||
while (i < command_buffer.count && cmd->node_id == node_id) {
|
||||
if (cmd->type == CommandId::DepopPrepare) {
|
||||
cmd->enabled = true;
|
||||
} else if (cmd->enabled && cmd->type != CommandId::Performance) {
|
||||
cmd->enabled = false;
|
||||
estimated_process_time -= static_cast<u32>(
|
||||
drop_voice_param * static_cast<f32>(cmd->estimated_process_time));
|
||||
}
|
||||
command_list += cmd->size;
|
||||
cmd = reinterpret_cast<ICommand*>(command_list);
|
||||
i++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return voices_dropped;
|
||||
}
|
||||
|
||||
@@ -196,6 +196,20 @@ public:
|
||||
*/
|
||||
u32 DropVoices(CommandBuffer& command_buffer, u32 estimated_process_time, u32 time_limit);
|
||||
|
||||
/**
|
||||
* Get the current voice drop parameter.
|
||||
*
|
||||
* @return The current voice drop.
|
||||
*/
|
||||
f32 GetVoiceDropParameter() const;
|
||||
|
||||
/**
|
||||
* Set the voice drop parameter.
|
||||
*
|
||||
* @param The new voice drop.
|
||||
*/
|
||||
void SetVoiceDropParameter(f32 voice_drop);
|
||||
|
||||
private:
|
||||
/// Core system
|
||||
Core::System& core;
|
||||
@@ -301,6 +315,8 @@ private:
|
||||
u32 num_voices_dropped{};
|
||||
/// Tick that rendering started
|
||||
u64 render_start_tick{};
|
||||
/// Parameter to control the threshold for dropping voices if the audio graph gets too large
|
||||
f32 drop_voice_param{1.0f};
|
||||
};
|
||||
|
||||
} // namespace AudioRenderer
|
||||
|
||||
@@ -74,8 +74,8 @@ void VoiceContext::SortInfo() {
|
||||
}
|
||||
|
||||
std::ranges::sort(sorted_voice_info, [](const VoiceInfo* a, const VoiceInfo* b) {
|
||||
return a->priority != b->priority ? a->priority < b->priority
|
||||
: a->sort_order < b->sort_order;
|
||||
return a->priority != b->priority ? a->priority > b->priority
|
||||
: a->sort_order > b->sort_order;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ add_library(common STATIC
|
||||
bit_util.h
|
||||
cityhash.cpp
|
||||
cityhash.h
|
||||
cache_management.cpp
|
||||
cache_management.h
|
||||
common_funcs.h
|
||||
common_types.h
|
||||
concepts.h
|
||||
@@ -156,12 +158,13 @@ if (MSVC)
|
||||
)
|
||||
target_compile_options(common PRIVATE
|
||||
/W4
|
||||
/WX
|
||||
|
||||
/we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data
|
||||
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
||||
/we4800 # Implicit conversion from 'type' to bool. Possible information loss
|
||||
)
|
||||
else()
|
||||
target_compile_options(common PRIVATE
|
||||
-Werror
|
||||
|
||||
$<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>
|
||||
)
|
||||
endif()
|
||||
@@ -169,7 +172,11 @@ endif()
|
||||
create_target_directory_groups(common)
|
||||
|
||||
target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads)
|
||||
target_link_libraries(common PRIVATE lz4::lz4)
|
||||
if (TARGET lz4::lz4)
|
||||
target_link_libraries(common PRIVATE lz4::lz4)
|
||||
else()
|
||||
target_link_libraries(common PRIVATE LZ4::lz4_shared)
|
||||
endif()
|
||||
if (TARGET zstd::zstd)
|
||||
target_link_libraries(common PRIVATE zstd::zstd)
|
||||
else()
|
||||
|
||||
@@ -156,6 +156,7 @@ AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN {
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -141,10 +141,6 @@ public:
|
||||
constexpr BitField(BitField&&) noexcept = default;
|
||||
constexpr BitField& operator=(BitField&&) noexcept = default;
|
||||
|
||||
[[nodiscard]] constexpr operator T() const {
|
||||
return Value();
|
||||
}
|
||||
|
||||
constexpr void Assign(const T& value) {
|
||||
#ifdef _MSC_VER
|
||||
storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value));
|
||||
@@ -162,6 +158,17 @@ public:
|
||||
return ExtractValue(storage);
|
||||
}
|
||||
|
||||
template <typename ConvertedToType>
|
||||
[[nodiscard]] constexpr ConvertedToType As() const {
|
||||
static_assert(!std::is_same_v<T, ConvertedToType>,
|
||||
"Unnecessary cast. Use Value() instead.");
|
||||
return static_cast<ConvertedToType>(Value());
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr operator T() const {
|
||||
return Value();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr explicit operator bool() const {
|
||||
return Value() != 0;
|
||||
}
|
||||
|
||||
@@ -21,11 +21,6 @@ constexpr size_t hardware_interference_size = std::hardware_destructive_interfer
|
||||
constexpr size_t hardware_interference_size = 64;
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4324)
|
||||
#endif
|
||||
|
||||
template <typename T, size_t capacity = 0x400>
|
||||
class MPSCQueue {
|
||||
public:
|
||||
@@ -160,8 +155,4 @@ private:
|
||||
static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible");
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "alignment.h"
|
||||
#include "cache_management.h"
|
||||
#include "common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
|
||||
// Most cache operations are no-ops on x86
|
||||
|
||||
void DataCacheLineCleanByVAToPoU(void* start, size_t size) {}
|
||||
void DataCacheLineCleanAndInvalidateByVAToPoC(void* start, size_t size) {}
|
||||
void DataCacheLineCleanByVAToPoC(void* start, size_t size) {}
|
||||
void DataCacheZeroByVA(void* start, size_t size) {
|
||||
std::memset(start, 0, size);
|
||||
}
|
||||
|
||||
#elif defined(ARCHITECTURE_arm64)
|
||||
|
||||
// BS/DminLine is log2(cache size in words), we want size in bytes
|
||||
#define EXTRACT_DMINLINE(ctr_el0) (1 << ((((ctr_el0) >> 16) & 0xf) + 2))
|
||||
#define EXTRACT_BS(dczid_el0) (1 << (((dczid_el0)&0xf) + 2))
|
||||
|
||||
#define DEFINE_DC_OP(op_name, function_name) \
|
||||
void function_name(void* start, size_t size) { \
|
||||
size_t ctr_el0; \
|
||||
asm volatile("mrs %[ctr_el0], ctr_el0\n\t" : [ctr_el0] "=r"(ctr_el0)); \
|
||||
size_t cacheline_size = EXTRACT_DMINLINE(ctr_el0); \
|
||||
uintptr_t va_start = reinterpret_cast<uintptr_t>(start); \
|
||||
uintptr_t va_end = va_start + size; \
|
||||
for (uintptr_t va = va_start; va < va_end; va += cacheline_size) { \
|
||||
asm volatile("dc " #op_name ", %[va]\n\t" : : [va] "r"(va) : "memory"); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define DEFINE_DC_OP_DCZID(op_name, function_name) \
|
||||
void function_name(void* start, size_t size) { \
|
||||
size_t dczid_el0; \
|
||||
asm volatile("mrs %[dczid_el0], dczid_el0\n\t" : [dczid_el0] "=r"(dczid_el0)); \
|
||||
size_t cacheline_size = EXTRACT_BS(dczid_el0); \
|
||||
uintptr_t va_start = reinterpret_cast<uintptr_t>(start); \
|
||||
uintptr_t va_end = va_start + size; \
|
||||
for (uintptr_t va = va_start; va < va_end; va += cacheline_size) { \
|
||||
asm volatile("dc " #op_name ", %[va]\n\t" : : [va] "r"(va) : "memory"); \
|
||||
} \
|
||||
}
|
||||
|
||||
DEFINE_DC_OP(cvau, DataCacheLineCleanByVAToPoU);
|
||||
DEFINE_DC_OP(civac, DataCacheLineCleanAndInvalidateByVAToPoC);
|
||||
DEFINE_DC_OP(cvac, DataCacheLineCleanByVAToPoC);
|
||||
DEFINE_DC_OP_DCZID(zva, DataCacheZeroByVA);
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace Common
|
||||
@@ -0,0 +1,27 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "stdlib.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
// Data cache instructions enabled at EL0 by SCTLR_EL1.UCI.
|
||||
// VA = virtual address
|
||||
// PoC = point of coherency
|
||||
// PoU = point of unification
|
||||
|
||||
// dc cvau
|
||||
void DataCacheLineCleanByVAToPoU(void* start, size_t size);
|
||||
|
||||
// dc civac
|
||||
void DataCacheLineCleanAndInvalidateByVAToPoC(void* start, size_t size);
|
||||
|
||||
// dc cvac
|
||||
void DataCacheLineCleanByVAToPoC(void* start, size_t size);
|
||||
|
||||
// dc zva
|
||||
void DataCacheZeroByVA(void* start, size_t size);
|
||||
|
||||
} // namespace Common
|
||||
@@ -31,8 +31,10 @@
|
||||
|
||||
#ifndef _MSC_VER
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
#define Crash() __asm__ __volatile__("int $3")
|
||||
#elif defined(ARCHITECTURE_arm64)
|
||||
#define Crash() __asm__ __volatile__("brk #0")
|
||||
#else
|
||||
#define Crash() exit(1)
|
||||
#endif
|
||||
|
||||
@@ -3,24 +3,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Common {
|
||||
|
||||
// Check if type is like an STL container
|
||||
// Check if type satisfies the ContiguousContainer named requirement.
|
||||
template <typename T>
|
||||
concept IsSTLContainer = requires(T t) {
|
||||
typename T::value_type;
|
||||
typename T::iterator;
|
||||
typename T::const_iterator;
|
||||
// TODO(ogniK): Replace below is std::same_as<void> when MSVC supports it.
|
||||
t.begin();
|
||||
t.end();
|
||||
t.cbegin();
|
||||
t.cend();
|
||||
t.data();
|
||||
t.size();
|
||||
};
|
||||
concept IsContiguousContainer = std::contiguous_iterator<typename T::iterator>;
|
||||
|
||||
// TODO: Replace with std::derived_from when the <concepts> header
|
||||
// is available on all supported platforms.
|
||||
@@ -34,4 +24,12 @@ concept DerivedFrom = requires {
|
||||
template <typename From, typename To>
|
||||
concept ConvertibleTo = std::is_convertible_v<From, To>;
|
||||
|
||||
// No equivalents in the stdlib
|
||||
|
||||
template <typename T>
|
||||
concept IsArithmetic = std::is_arithmetic_v<T>;
|
||||
|
||||
template <typename T>
|
||||
concept IsIntegral = std::is_integral_v<T>;
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -1,41 +1,133 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/fiber.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
#include "common/spin_lock.h"
|
||||
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <boost/context/detail/fcontext.hpp>
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
constexpr std::size_t default_stack_size = 512 * 1024;
|
||||
|
||||
struct Fiber::FiberImpl {
|
||||
FiberImpl() : stack{default_stack_size}, rewind_stack{default_stack_size} {}
|
||||
|
||||
VirtualBuffer<u8> stack;
|
||||
VirtualBuffer<u8> rewind_stack;
|
||||
|
||||
std::mutex guard;
|
||||
SpinLock guard{};
|
||||
std::function<void()> entry_point;
|
||||
std::function<void()> rewind_point;
|
||||
void* rewind_parameter{};
|
||||
std::shared_ptr<Fiber> previous_fiber;
|
||||
bool is_thread_fiber{};
|
||||
bool released{};
|
||||
|
||||
u8* stack_limit{};
|
||||
u8* rewind_stack_limit{};
|
||||
boost::context::detail::fcontext_t context{};
|
||||
boost::context::detail::fcontext_t rewind_context{};
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
LPVOID handle = nullptr;
|
||||
LPVOID rewind_handle = nullptr;
|
||||
#else
|
||||
alignas(64) std::array<u8, default_stack_size> stack;
|
||||
alignas(64) std::array<u8, default_stack_size> rewind_stack;
|
||||
u8* stack_limit;
|
||||
u8* rewind_stack_limit;
|
||||
boost::context::detail::fcontext_t context;
|
||||
boost::context::detail::fcontext_t rewind_context;
|
||||
#endif
|
||||
};
|
||||
|
||||
void Fiber::SetRewindPoint(std::function<void()>&& rewind_func) {
|
||||
impl->rewind_point = std::move(rewind_func);
|
||||
}
|
||||
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
|
||||
void Fiber::Start() {
|
||||
ASSERT(impl->previous_fiber != nullptr);
|
||||
impl->previous_fiber->impl->guard.unlock();
|
||||
impl->previous_fiber.reset();
|
||||
impl->entry_point();
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void Fiber::OnRewind() {
|
||||
ASSERT(impl->handle != nullptr);
|
||||
DeleteFiber(impl->handle);
|
||||
impl->handle = impl->rewind_handle;
|
||||
impl->rewind_handle = nullptr;
|
||||
impl->rewind_point();
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void Fiber::FiberStartFunc(void* fiber_parameter) {
|
||||
auto* fiber = static_cast<Fiber*>(fiber_parameter);
|
||||
fiber->Start();
|
||||
}
|
||||
|
||||
void Fiber::RewindStartFunc(void* fiber_parameter) {
|
||||
auto* fiber = static_cast<Fiber*>(fiber_parameter);
|
||||
fiber->OnRewind();
|
||||
}
|
||||
|
||||
Fiber::Fiber(std::function<void()>&& entry_point_func) : impl{std::make_unique<FiberImpl>()} {
|
||||
impl->entry_point = std::move(entry_point_func);
|
||||
impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this);
|
||||
}
|
||||
|
||||
Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
|
||||
|
||||
Fiber::~Fiber() {
|
||||
if (impl->released) {
|
||||
return;
|
||||
}
|
||||
// Make sure the Fiber is not being used
|
||||
const bool locked = impl->guard.try_lock();
|
||||
ASSERT_MSG(locked, "Destroying a fiber that's still running");
|
||||
if (locked) {
|
||||
impl->guard.unlock();
|
||||
}
|
||||
DeleteFiber(impl->handle);
|
||||
}
|
||||
|
||||
void Fiber::Exit() {
|
||||
ASSERT_MSG(impl->is_thread_fiber, "Exitting non main thread fiber");
|
||||
if (!impl->is_thread_fiber) {
|
||||
return;
|
||||
}
|
||||
ConvertFiberToThread();
|
||||
impl->guard.unlock();
|
||||
impl->released = true;
|
||||
}
|
||||
|
||||
void Fiber::Rewind() {
|
||||
ASSERT(impl->rewind_point);
|
||||
ASSERT(impl->rewind_handle == nullptr);
|
||||
impl->rewind_handle = CreateFiber(default_stack_size, &RewindStartFunc, this);
|
||||
SwitchToFiber(impl->rewind_handle);
|
||||
}
|
||||
|
||||
void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
|
||||
ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
|
||||
ASSERT_MSG(to != nullptr, "Next fiber is null!");
|
||||
to->impl->guard.lock();
|
||||
to->impl->previous_fiber = from;
|
||||
SwitchToFiber(to->impl->handle);
|
||||
ASSERT(from->impl->previous_fiber != nullptr);
|
||||
from->impl->previous_fiber->impl->guard.unlock();
|
||||
from->impl->previous_fiber.reset();
|
||||
}
|
||||
|
||||
std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
|
||||
std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
|
||||
fiber->impl->guard.lock();
|
||||
fiber->impl->handle = ConvertThreadToFiber(nullptr);
|
||||
fiber->impl->is_thread_fiber = true;
|
||||
return fiber;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void Fiber::Start(boost::context::detail::transfer_t& transfer) {
|
||||
ASSERT(impl->previous_fiber != nullptr);
|
||||
impl->previous_fiber->impl->context = transfer.fctx;
|
||||
@@ -107,22 +199,16 @@ void Fiber::Rewind() {
|
||||
boost::context::detail::jump_fcontext(impl->rewind_context, this);
|
||||
}
|
||||
|
||||
void Fiber::YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to) {
|
||||
to.impl->guard.lock();
|
||||
to.impl->previous_fiber = weak_from.lock();
|
||||
|
||||
auto transfer = boost::context::detail::jump_fcontext(to.impl->context, &to);
|
||||
|
||||
// "from" might no longer be valid if the thread was killed
|
||||
if (auto from = weak_from.lock()) {
|
||||
if (from->impl->previous_fiber == nullptr) {
|
||||
ASSERT_MSG(false, "previous_fiber is nullptr!");
|
||||
return;
|
||||
}
|
||||
from->impl->previous_fiber->impl->context = transfer.fctx;
|
||||
from->impl->previous_fiber->impl->guard.unlock();
|
||||
from->impl->previous_fiber.reset();
|
||||
}
|
||||
void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
|
||||
ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
|
||||
ASSERT_MSG(to != nullptr, "Next fiber is null!");
|
||||
to->impl->guard.lock();
|
||||
to->impl->previous_fiber = from;
|
||||
auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get());
|
||||
ASSERT(from->impl->previous_fiber != nullptr);
|
||||
from->impl->previous_fiber->impl->context = transfer.fctx;
|
||||
from->impl->previous_fiber->impl->guard.unlock();
|
||||
from->impl->previous_fiber.reset();
|
||||
}
|
||||
|
||||
std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
|
||||
@@ -132,4 +218,5 @@ std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
|
||||
return fiber;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
#endif
|
||||
} // namespace Common
|
||||
@@ -6,9 +6,11 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#if !defined(_WIN32) && !defined(WIN32)
|
||||
namespace boost::context::detail {
|
||||
struct transfer_t;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
@@ -40,7 +42,7 @@ public:
|
||||
|
||||
/// Yields control from Fiber 'from' to Fiber 'to'
|
||||
/// Fiber 'from' must be the currently running fiber.
|
||||
static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to);
|
||||
static void YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to);
|
||||
[[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
|
||||
|
||||
void SetRewindPoint(std::function<void()>&& rewind_func);
|
||||
@@ -50,16 +52,26 @@ public:
|
||||
/// Only call from main thread's fiber
|
||||
void Exit();
|
||||
|
||||
/// Changes the start parameter of the fiber. Has no effect if the fiber already started
|
||||
void SetStartParameter(void* new_parameter);
|
||||
|
||||
private:
|
||||
Fiber();
|
||||
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
void OnRewind();
|
||||
void Start();
|
||||
static void FiberStartFunc(void* fiber_parameter);
|
||||
static void RewindStartFunc(void* fiber_parameter);
|
||||
#else
|
||||
void OnRewind(boost::context::detail::transfer_t& transfer);
|
||||
void Start(boost::context::detail::transfer_t& transfer);
|
||||
static void FiberStartFunc(boost::context::detail::transfer_t transfer);
|
||||
static void RewindStartFunc(boost::context::detail::transfer_t transfer);
|
||||
#endif
|
||||
|
||||
struct FiberImpl;
|
||||
std::unique_ptr<FiberImpl> impl;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
} // namespace Common
|
||||
@@ -4,14 +4,7 @@
|
||||
// From: https://github.com/eteran/cpp-utilities/blob/master/fixed/include/cpp-utilities/fixed.h
|
||||
// See also: http://stackoverflow.com/questions/79677/whats-the-best-way-to-do-fixed-point-math
|
||||
|
||||
#ifndef FIXED_H_
|
||||
#define FIXED_H_
|
||||
|
||||
#if __cplusplus >= 201402L
|
||||
#define CONSTEXPR14 constexpr
|
||||
#else
|
||||
#define CONSTEXPR14
|
||||
#endif
|
||||
#pragma once
|
||||
|
||||
#include <cstddef> // for size_t
|
||||
#include <cstdint>
|
||||
@@ -19,6 +12,8 @@
|
||||
#include <ostream>
|
||||
#include <type_traits>
|
||||
|
||||
#include <common/concepts.h>
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <size_t I, size_t F>
|
||||
@@ -57,8 +52,8 @@ struct type_from_size<64> {
|
||||
static constexpr size_t size = 64;
|
||||
|
||||
using value_type = int64_t;
|
||||
using unsigned_type = std::make_unsigned<value_type>::type;
|
||||
using signed_type = std::make_signed<value_type>::type;
|
||||
using unsigned_type = std::make_unsigned_t<value_type>;
|
||||
using signed_type = std::make_signed_t<value_type>;
|
||||
using next_size = type_from_size<128>;
|
||||
};
|
||||
|
||||
@@ -68,8 +63,8 @@ struct type_from_size<32> {
|
||||
static constexpr size_t size = 32;
|
||||
|
||||
using value_type = int32_t;
|
||||
using unsigned_type = std::make_unsigned<value_type>::type;
|
||||
using signed_type = std::make_signed<value_type>::type;
|
||||
using unsigned_type = std::make_unsigned_t<value_type>;
|
||||
using signed_type = std::make_signed_t<value_type>;
|
||||
using next_size = type_from_size<64>;
|
||||
};
|
||||
|
||||
@@ -79,8 +74,8 @@ struct type_from_size<16> {
|
||||
static constexpr size_t size = 16;
|
||||
|
||||
using value_type = int16_t;
|
||||
using unsigned_type = std::make_unsigned<value_type>::type;
|
||||
using signed_type = std::make_signed<value_type>::type;
|
||||
using unsigned_type = std::make_unsigned_t<value_type>;
|
||||
using signed_type = std::make_signed_t<value_type>;
|
||||
using next_size = type_from_size<32>;
|
||||
};
|
||||
|
||||
@@ -90,8 +85,8 @@ struct type_from_size<8> {
|
||||
static constexpr size_t size = 8;
|
||||
|
||||
using value_type = int8_t;
|
||||
using unsigned_type = std::make_unsigned<value_type>::type;
|
||||
using signed_type = std::make_signed<value_type>::type;
|
||||
using unsigned_type = std::make_unsigned_t<value_type>;
|
||||
using signed_type = std::make_signed_t<value_type>;
|
||||
using next_size = type_from_size<16>;
|
||||
};
|
||||
|
||||
@@ -106,9 +101,9 @@ constexpr B next_to_base(N rhs) {
|
||||
struct divide_by_zero : std::exception {};
|
||||
|
||||
template <size_t I, size_t F>
|
||||
CONSTEXPR14 FixedPoint<I, F> divide(
|
||||
constexpr FixedPoint<I, F> divide(
|
||||
FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder,
|
||||
typename std::enable_if<type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) {
|
||||
std::enable_if_t<type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
|
||||
|
||||
using next_type = typename FixedPoint<I, F>::next_type;
|
||||
using base_type = typename FixedPoint<I, F>::base_type;
|
||||
@@ -126,9 +121,9 @@ CONSTEXPR14 FixedPoint<I, F> divide(
|
||||
}
|
||||
|
||||
template <size_t I, size_t F>
|
||||
CONSTEXPR14 FixedPoint<I, F> divide(
|
||||
constexpr FixedPoint<I, F> divide(
|
||||
FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder,
|
||||
typename std::enable_if<!type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) {
|
||||
std::enable_if_t<!type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
|
||||
|
||||
using unsigned_type = typename FixedPoint<I, F>::unsigned_type;
|
||||
|
||||
@@ -196,9 +191,9 @@ CONSTEXPR14 FixedPoint<I, F> divide(
|
||||
|
||||
// this is the usual implementation of multiplication
|
||||
template <size_t I, size_t F>
|
||||
CONSTEXPR14 FixedPoint<I, F> multiply(
|
||||
constexpr FixedPoint<I, F> multiply(
|
||||
FixedPoint<I, F> lhs, FixedPoint<I, F> rhs,
|
||||
typename std::enable_if<type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) {
|
||||
std::enable_if_t<type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
|
||||
|
||||
using next_type = typename FixedPoint<I, F>::next_type;
|
||||
using base_type = typename FixedPoint<I, F>::base_type;
|
||||
@@ -215,9 +210,9 @@ CONSTEXPR14 FixedPoint<I, F> multiply(
|
||||
// it is slightly slower, but is more robust since it doesn't
|
||||
// require and upgraded type
|
||||
template <size_t I, size_t F>
|
||||
CONSTEXPR14 FixedPoint<I, F> multiply(
|
||||
constexpr FixedPoint<I, F> multiply(
|
||||
FixedPoint<I, F> lhs, FixedPoint<I, F> rhs,
|
||||
typename std::enable_if<!type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) {
|
||||
std::enable_if_t<!type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
|
||||
|
||||
using base_type = typename FixedPoint<I, F>::base_type;
|
||||
|
||||
@@ -272,19 +267,20 @@ public:
|
||||
static constexpr base_type one = base_type(1) << fractional_bits;
|
||||
|
||||
public: // constructors
|
||||
FixedPoint() = default;
|
||||
FixedPoint(const FixedPoint&) = default;
|
||||
FixedPoint(FixedPoint&&) = default;
|
||||
FixedPoint& operator=(const FixedPoint&) = default;
|
||||
constexpr FixedPoint() = default;
|
||||
|
||||
template <class Number>
|
||||
constexpr FixedPoint(
|
||||
Number n, typename std::enable_if<std::is_arithmetic<Number>::value>::type* = nullptr)
|
||||
: data_(static_cast<base_type>(n * one)) {}
|
||||
constexpr FixedPoint(const FixedPoint&) = default;
|
||||
constexpr FixedPoint& operator=(const FixedPoint&) = default;
|
||||
|
||||
constexpr FixedPoint(FixedPoint&&) noexcept = default;
|
||||
constexpr FixedPoint& operator=(FixedPoint&&) noexcept = default;
|
||||
|
||||
template <IsArithmetic Number>
|
||||
constexpr FixedPoint(Number n) : data_(static_cast<base_type>(n * one)) {}
|
||||
|
||||
public: // conversion
|
||||
template <size_t I2, size_t F2>
|
||||
CONSTEXPR14 explicit FixedPoint(FixedPoint<I2, F2> other) {
|
||||
constexpr explicit FixedPoint(FixedPoint<I2, F2> other) {
|
||||
static_assert(I2 <= I && F2 <= F, "Scaling conversion can only upgrade types");
|
||||
using T = FixedPoint<I2, F2>;
|
||||
|
||||
@@ -308,36 +304,14 @@ public:
|
||||
}
|
||||
|
||||
public: // comparison operators
|
||||
constexpr bool operator==(FixedPoint rhs) const {
|
||||
return data_ == rhs.data_;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(FixedPoint rhs) const {
|
||||
return data_ != rhs.data_;
|
||||
}
|
||||
|
||||
constexpr bool operator<(FixedPoint rhs) const {
|
||||
return data_ < rhs.data_;
|
||||
}
|
||||
|
||||
constexpr bool operator>(FixedPoint rhs) const {
|
||||
return data_ > rhs.data_;
|
||||
}
|
||||
|
||||
constexpr bool operator<=(FixedPoint rhs) const {
|
||||
return data_ <= rhs.data_;
|
||||
}
|
||||
|
||||
constexpr bool operator>=(FixedPoint rhs) const {
|
||||
return data_ >= rhs.data_;
|
||||
}
|
||||
friend constexpr auto operator<=>(FixedPoint lhs, FixedPoint rhs) = default;
|
||||
|
||||
public: // unary operators
|
||||
constexpr bool operator!() const {
|
||||
[[nodiscard]] constexpr bool operator!() const {
|
||||
return !data_;
|
||||
}
|
||||
|
||||
constexpr FixedPoint operator~() const {
|
||||
[[nodiscard]] constexpr FixedPoint operator~() const {
|
||||
// NOTE(eteran): this will often appear to "just negate" the value
|
||||
// that is not an error, it is because -x == (~x+1)
|
||||
// and that "+1" is adding an infinitesimally small fraction to the
|
||||
@@ -345,89 +319,87 @@ public: // unary operators
|
||||
return FixedPoint::from_base(~data_);
|
||||
}
|
||||
|
||||
constexpr FixedPoint operator-() const {
|
||||
[[nodiscard]] constexpr FixedPoint operator-() const {
|
||||
return FixedPoint::from_base(-data_);
|
||||
}
|
||||
|
||||
constexpr FixedPoint operator+() const {
|
||||
[[nodiscard]] constexpr FixedPoint operator+() const {
|
||||
return FixedPoint::from_base(+data_);
|
||||
}
|
||||
|
||||
CONSTEXPR14 FixedPoint& operator++() {
|
||||
constexpr FixedPoint& operator++() {
|
||||
data_ += one;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CONSTEXPR14 FixedPoint& operator--() {
|
||||
constexpr FixedPoint& operator--() {
|
||||
data_ -= one;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CONSTEXPR14 FixedPoint operator++(int) {
|
||||
constexpr FixedPoint operator++(int) {
|
||||
FixedPoint tmp(*this);
|
||||
data_ += one;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
CONSTEXPR14 FixedPoint operator--(int) {
|
||||
constexpr FixedPoint operator--(int) {
|
||||
FixedPoint tmp(*this);
|
||||
data_ -= one;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
public: // basic math operators
|
||||
CONSTEXPR14 FixedPoint& operator+=(FixedPoint n) {
|
||||
constexpr FixedPoint& operator+=(FixedPoint n) {
|
||||
data_ += n.data_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CONSTEXPR14 FixedPoint& operator-=(FixedPoint n) {
|
||||
constexpr FixedPoint& operator-=(FixedPoint n) {
|
||||
data_ -= n.data_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CONSTEXPR14 FixedPoint& operator*=(FixedPoint n) {
|
||||
constexpr FixedPoint& operator*=(FixedPoint n) {
|
||||
return assign(detail::multiply(*this, n));
|
||||
}
|
||||
|
||||
CONSTEXPR14 FixedPoint& operator/=(FixedPoint n) {
|
||||
constexpr FixedPoint& operator/=(FixedPoint n) {
|
||||
FixedPoint temp;
|
||||
return assign(detail::divide(*this, n, temp));
|
||||
}
|
||||
|
||||
private:
|
||||
CONSTEXPR14 FixedPoint& assign(FixedPoint rhs) {
|
||||
constexpr FixedPoint& assign(FixedPoint rhs) {
|
||||
data_ = rhs.data_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
public: // binary math operators, effects underlying bit pattern since these
|
||||
// don't really typically make sense for non-integer values
|
||||
CONSTEXPR14 FixedPoint& operator&=(FixedPoint n) {
|
||||
constexpr FixedPoint& operator&=(FixedPoint n) {
|
||||
data_ &= n.data_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CONSTEXPR14 FixedPoint& operator|=(FixedPoint n) {
|
||||
constexpr FixedPoint& operator|=(FixedPoint n) {
|
||||
data_ |= n.data_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CONSTEXPR14 FixedPoint& operator^=(FixedPoint n) {
|
||||
constexpr FixedPoint& operator^=(FixedPoint n) {
|
||||
data_ ^= n.data_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class Integer,
|
||||
class = typename std::enable_if<std::is_integral<Integer>::value>::type>
|
||||
CONSTEXPR14 FixedPoint& operator>>=(Integer n) {
|
||||
template <IsIntegral Integer>
|
||||
constexpr FixedPoint& operator>>=(Integer n) {
|
||||
data_ >>= n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class Integer,
|
||||
class = typename std::enable_if<std::is_integral<Integer>::value>::type>
|
||||
CONSTEXPR14 FixedPoint& operator<<=(Integer n) {
|
||||
template <IsIntegral Integer>
|
||||
constexpr FixedPoint& operator<<=(Integer n) {
|
||||
data_ <<= n;
|
||||
return *this;
|
||||
}
|
||||
@@ -437,42 +409,42 @@ public: // conversion to basic types
|
||||
data_ += (data_ & fractional_mask) >> 1;
|
||||
}
|
||||
|
||||
constexpr int to_int() {
|
||||
[[nodiscard]] constexpr int to_int() {
|
||||
round_up();
|
||||
return static_cast<int>((data_ & integer_mask) >> fractional_bits);
|
||||
}
|
||||
|
||||
constexpr unsigned int to_uint() const {
|
||||
[[nodiscard]] constexpr unsigned int to_uint() {
|
||||
round_up();
|
||||
return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits);
|
||||
}
|
||||
|
||||
constexpr int64_t to_long() {
|
||||
[[nodiscard]] constexpr int64_t to_long() {
|
||||
round_up();
|
||||
return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits);
|
||||
}
|
||||
|
||||
constexpr int to_int_floor() const {
|
||||
[[nodiscard]] constexpr int to_int_floor() const {
|
||||
return static_cast<int>((data_ & integer_mask) >> fractional_bits);
|
||||
}
|
||||
|
||||
constexpr int64_t to_long_floor() {
|
||||
[[nodiscard]] constexpr int64_t to_long_floor() const {
|
||||
return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits);
|
||||
}
|
||||
|
||||
constexpr unsigned int to_uint_floor() const {
|
||||
[[nodiscard]] constexpr unsigned int to_uint_floor() const {
|
||||
return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits);
|
||||
}
|
||||
|
||||
constexpr float to_float() const {
|
||||
[[nodiscard]] constexpr float to_float() const {
|
||||
return static_cast<float>(data_) / FixedPoint::one;
|
||||
}
|
||||
|
||||
constexpr double to_double() const {
|
||||
[[nodiscard]] constexpr double to_double() const {
|
||||
return static_cast<double>(data_) / FixedPoint::one;
|
||||
}
|
||||
|
||||
constexpr base_type to_raw() const {
|
||||
[[nodiscard]] constexpr base_type to_raw() const {
|
||||
return data_;
|
||||
}
|
||||
|
||||
@@ -480,27 +452,27 @@ public: // conversion to basic types
|
||||
data_ &= fractional_mask;
|
||||
}
|
||||
|
||||
constexpr base_type get_frac() const {
|
||||
[[nodiscard]] constexpr base_type get_frac() const {
|
||||
return data_ & fractional_mask;
|
||||
}
|
||||
|
||||
public:
|
||||
CONSTEXPR14 void swap(FixedPoint& rhs) {
|
||||
constexpr void swap(FixedPoint& rhs) noexcept {
|
||||
using std::swap;
|
||||
swap(data_, rhs.data_);
|
||||
}
|
||||
|
||||
public:
|
||||
base_type data_;
|
||||
base_type data_{};
|
||||
};
|
||||
|
||||
// if we have the same fractional portion, but differing integer portions, we trivially upgrade the
|
||||
// smaller type
|
||||
template <size_t I1, size_t I2, size_t F>
|
||||
CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type
|
||||
operator+(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
|
||||
constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator+(
|
||||
FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
|
||||
|
||||
using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type;
|
||||
using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
|
||||
|
||||
const T l = T::from_base(lhs.to_raw());
|
||||
const T r = T::from_base(rhs.to_raw());
|
||||
@@ -508,10 +480,10 @@ operator+(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
|
||||
}
|
||||
|
||||
template <size_t I1, size_t I2, size_t F>
|
||||
CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type
|
||||
operator-(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
|
||||
constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator-(
|
||||
FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
|
||||
|
||||
using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type;
|
||||
using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
|
||||
|
||||
const T l = T::from_base(lhs.to_raw());
|
||||
const T r = T::from_base(rhs.to_raw());
|
||||
@@ -519,10 +491,10 @@ operator-(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
|
||||
}
|
||||
|
||||
template <size_t I1, size_t I2, size_t F>
|
||||
CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type
|
||||
operator*(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
|
||||
constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator*(
|
||||
FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
|
||||
|
||||
using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type;
|
||||
using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
|
||||
|
||||
const T l = T::from_base(lhs.to_raw());
|
||||
const T r = T::from_base(rhs.to_raw());
|
||||
@@ -530,10 +502,10 @@ operator*(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
|
||||
}
|
||||
|
||||
template <size_t I1, size_t I2, size_t F>
|
||||
CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type
|
||||
operator/(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
|
||||
constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator/(
|
||||
FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
|
||||
|
||||
using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type;
|
||||
using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
|
||||
|
||||
const T l = T::from_base(lhs.to_raw());
|
||||
const T r = T::from_base(rhs.to_raw());
|
||||
@@ -548,159 +520,133 @@ std::ostream& operator<<(std::ostream& os, FixedPoint<I, F> f) {
|
||||
|
||||
// basic math operators
|
||||
template <size_t I, size_t F>
|
||||
CONSTEXPR14 FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
|
||||
constexpr FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
|
||||
lhs += rhs;
|
||||
return lhs;
|
||||
}
|
||||
template <size_t I, size_t F>
|
||||
CONSTEXPR14 FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
|
||||
constexpr FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
|
||||
lhs -= rhs;
|
||||
return lhs;
|
||||
}
|
||||
template <size_t I, size_t F>
|
||||
CONSTEXPR14 FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
|
||||
constexpr FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
|
||||
lhs *= rhs;
|
||||
return lhs;
|
||||
}
|
||||
template <size_t I, size_t F>
|
||||
CONSTEXPR14 FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
|
||||
constexpr FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
|
||||
lhs /= rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <size_t I, size_t F, class Number,
|
||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
||||
CONSTEXPR14 FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, Number rhs) {
|
||||
template <size_t I, size_t F, IsArithmetic Number>
|
||||
constexpr FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, Number rhs) {
|
||||
lhs += FixedPoint<I, F>(rhs);
|
||||
return lhs;
|
||||
}
|
||||
template <size_t I, size_t F, class Number,
|
||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
||||
CONSTEXPR14 FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, Number rhs) {
|
||||
template <size_t I, size_t F, IsArithmetic Number>
|
||||
constexpr FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, Number rhs) {
|
||||
lhs -= FixedPoint<I, F>(rhs);
|
||||
return lhs;
|
||||
}
|
||||
template <size_t I, size_t F, class Number,
|
||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
||||
CONSTEXPR14 FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, Number rhs) {
|
||||
template <size_t I, size_t F, IsArithmetic Number>
|
||||
constexpr FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, Number rhs) {
|
||||
lhs *= FixedPoint<I, F>(rhs);
|
||||
return lhs;
|
||||
}
|
||||
template <size_t I, size_t F, class Number,
|
||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
||||
CONSTEXPR14 FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, Number rhs) {
|
||||
template <size_t I, size_t F, IsArithmetic Number>
|
||||
constexpr FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, Number rhs) {
|
||||
lhs /= FixedPoint<I, F>(rhs);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <size_t I, size_t F, class Number,
|
||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
||||
CONSTEXPR14 FixedPoint<I, F> operator+(Number lhs, FixedPoint<I, F> rhs) {
|
||||
template <size_t I, size_t F, IsArithmetic Number>
|
||||
constexpr FixedPoint<I, F> operator+(Number lhs, FixedPoint<I, F> rhs) {
|
||||
FixedPoint<I, F> tmp(lhs);
|
||||
tmp += rhs;
|
||||
return tmp;
|
||||
}
|
||||
template <size_t I, size_t F, class Number,
|
||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
||||
CONSTEXPR14 FixedPoint<I, F> operator-(Number lhs, FixedPoint<I, F> rhs) {
|
||||
template <size_t I, size_t F, IsArithmetic Number>
|
||||
constexpr FixedPoint<I, F> operator-(Number lhs, FixedPoint<I, F> rhs) {
|
||||
FixedPoint<I, F> tmp(lhs);
|
||||
tmp -= rhs;
|
||||
return tmp;
|
||||
}
|
||||
template <size_t I, size_t F, class Number,
|
||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
||||
CONSTEXPR14 FixedPoint<I, F> operator*(Number lhs, FixedPoint<I, F> rhs) {
|
||||
template <size_t I, size_t F, IsArithmetic Number>
|
||||
constexpr FixedPoint<I, F> operator*(Number lhs, FixedPoint<I, F> rhs) {
|
||||
FixedPoint<I, F> tmp(lhs);
|
||||
tmp *= rhs;
|
||||
return tmp;
|
||||
}
|
||||
template <size_t I, size_t F, class Number,
|
||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
||||
CONSTEXPR14 FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) {
|
||||
template <size_t I, size_t F, IsArithmetic Number>
|
||||
constexpr FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) {
|
||||
FixedPoint<I, F> tmp(lhs);
|
||||
tmp /= rhs;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
// shift operators
|
||||
template <size_t I, size_t F, class Integer,
|
||||
class = typename std::enable_if<std::is_integral<Integer>::value>::type>
|
||||
CONSTEXPR14 FixedPoint<I, F> operator<<(FixedPoint<I, F> lhs, Integer rhs) {
|
||||
template <size_t I, size_t F, IsIntegral Integer>
|
||||
constexpr FixedPoint<I, F> operator<<(FixedPoint<I, F> lhs, Integer rhs) {
|
||||
lhs <<= rhs;
|
||||
return lhs;
|
||||
}
|
||||
template <size_t I, size_t F, class Integer,
|
||||
class = typename std::enable_if<std::is_integral<Integer>::value>::type>
|
||||
CONSTEXPR14 FixedPoint<I, F> operator>>(FixedPoint<I, F> lhs, Integer rhs) {
|
||||
template <size_t I, size_t F, IsIntegral Integer>
|
||||
constexpr FixedPoint<I, F> operator>>(FixedPoint<I, F> lhs, Integer rhs) {
|
||||
lhs >>= rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
// comparison operators
|
||||
template <size_t I, size_t F, class Number,
|
||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
||||
template <size_t I, size_t F, IsArithmetic Number>
|
||||
constexpr bool operator>(FixedPoint<I, F> lhs, Number rhs) {
|
||||
return lhs > FixedPoint<I, F>(rhs);
|
||||
}
|
||||
template <size_t I, size_t F, class Number,
|
||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
||||
template <size_t I, size_t F, IsArithmetic Number>
|
||||
constexpr bool operator<(FixedPoint<I, F> lhs, Number rhs) {
|
||||
return lhs < FixedPoint<I, F>(rhs);
|
||||
}
|
||||
template <size_t I, size_t F, class Number,
|
||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
||||
template <size_t I, size_t F, IsArithmetic Number>
|
||||
constexpr bool operator>=(FixedPoint<I, F> lhs, Number rhs) {
|
||||
return lhs >= FixedPoint<I, F>(rhs);
|
||||
}
|
||||
template <size_t I, size_t F, class Number,
|
||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
||||
template <size_t I, size_t F, IsArithmetic Number>
|
||||
constexpr bool operator<=(FixedPoint<I, F> lhs, Number rhs) {
|
||||
return lhs <= FixedPoint<I, F>(rhs);
|
||||
}
|
||||
template <size_t I, size_t F, class Number,
|
||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
||||
template <size_t I, size_t F, IsArithmetic Number>
|
||||
constexpr bool operator==(FixedPoint<I, F> lhs, Number rhs) {
|
||||
return lhs == FixedPoint<I, F>(rhs);
|
||||
}
|
||||
template <size_t I, size_t F, class Number,
|
||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
||||
template <size_t I, size_t F, IsArithmetic Number>
|
||||
constexpr bool operator!=(FixedPoint<I, F> lhs, Number rhs) {
|
||||
return lhs != FixedPoint<I, F>(rhs);
|
||||
}
|
||||
|
||||
template <size_t I, size_t F, class Number,
|
||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
||||
template <size_t I, size_t F, IsArithmetic Number>
|
||||
constexpr bool operator>(Number lhs, FixedPoint<I, F> rhs) {
|
||||
return FixedPoint<I, F>(lhs) > rhs;
|
||||
}
|
||||
template <size_t I, size_t F, class Number,
|
||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
||||
template <size_t I, size_t F, IsArithmetic Number>
|
||||
constexpr bool operator<(Number lhs, FixedPoint<I, F> rhs) {
|
||||
return FixedPoint<I, F>(lhs) < rhs;
|
||||
}
|
||||
template <size_t I, size_t F, class Number,
|
||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
||||
template <size_t I, size_t F, IsArithmetic Number>
|
||||
constexpr bool operator>=(Number lhs, FixedPoint<I, F> rhs) {
|
||||
return FixedPoint<I, F>(lhs) >= rhs;
|
||||
}
|
||||
template <size_t I, size_t F, class Number,
|
||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
||||
template <size_t I, size_t F, IsArithmetic Number>
|
||||
constexpr bool operator<=(Number lhs, FixedPoint<I, F> rhs) {
|
||||
return FixedPoint<I, F>(lhs) <= rhs;
|
||||
}
|
||||
template <size_t I, size_t F, class Number,
|
||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
||||
template <size_t I, size_t F, IsArithmetic Number>
|
||||
constexpr bool operator==(Number lhs, FixedPoint<I, F> rhs) {
|
||||
return FixedPoint<I, F>(lhs) == rhs;
|
||||
}
|
||||
template <size_t I, size_t F, class Number,
|
||||
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
|
||||
template <size_t I, size_t F, IsArithmetic Number>
|
||||
constexpr bool operator!=(Number lhs, FixedPoint<I, F> rhs) {
|
||||
return FixedPoint<I, F>(lhs) != rhs;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
#undef CONSTEXPR14
|
||||
|
||||
#endif
|
||||
|
||||
@@ -209,8 +209,8 @@ public:
|
||||
|
||||
/**
|
||||
* Helper function which deduces the value type of a contiguous STL container used in ReadSpan.
|
||||
* If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls
|
||||
* ReadObject and T must be a trivially copyable object.
|
||||
* If T is not a contiguous container as defined by the concept IsContiguousContainer, this
|
||||
* calls ReadObject and T must be a trivially copyable object.
|
||||
*
|
||||
* See ReadSpan for more details if T is a contiguous container.
|
||||
* See ReadObject for more details if T is a trivially copyable object.
|
||||
@@ -223,7 +223,7 @@ public:
|
||||
*/
|
||||
template <typename T>
|
||||
[[nodiscard]] size_t Read(T& data) const {
|
||||
if constexpr (IsSTLContainer<T>) {
|
||||
if constexpr (IsContiguousContainer<T>) {
|
||||
using ContiguousType = typename T::value_type;
|
||||
static_assert(std::is_trivially_copyable_v<ContiguousType>,
|
||||
"Data type must be trivially copyable.");
|
||||
@@ -235,8 +235,8 @@ public:
|
||||
|
||||
/**
|
||||
* Helper function which deduces the value type of a contiguous STL container used in WriteSpan.
|
||||
* If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls
|
||||
* WriteObject and T must be a trivially copyable object.
|
||||
* If T is not a contiguous STL container as defined by the concept IsContiguousContainer, this
|
||||
* calls WriteObject and T must be a trivially copyable object.
|
||||
*
|
||||
* See WriteSpan for more details if T is a contiguous container.
|
||||
* See WriteObject for more details if T is a trivially copyable object.
|
||||
@@ -249,7 +249,7 @@ public:
|
||||
*/
|
||||
template <typename T>
|
||||
[[nodiscard]] size_t Write(const T& data) const {
|
||||
if constexpr (IsSTLContainer<T>) {
|
||||
if constexpr (IsContiguousContainer<T>) {
|
||||
using ContiguousType = typename T::value_type;
|
||||
static_assert(std::is_trivially_copyable_v<ContiguousType>,
|
||||
"Data type must be trivially copyable.");
|
||||
|
||||
@@ -359,6 +359,12 @@ public:
|
||||
}
|
||||
});
|
||||
|
||||
long page_size = sysconf(_SC_PAGESIZE);
|
||||
if (page_size != 0x1000) {
|
||||
LOG_CRITICAL(HW_Memory, "page size {:#x} is incompatible with 4K paging", page_size);
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
|
||||
// Backing memory initialization
|
||||
#if defined(__FreeBSD__) && __FreeBSD__ < 13
|
||||
// XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30
|
||||
|
||||
@@ -100,7 +100,6 @@ enum class CameraError {
|
||||
enum class VibrationAmplificationType {
|
||||
Linear,
|
||||
Exponential,
|
||||
Test,
|
||||
};
|
||||
|
||||
// Analog properties for calibration
|
||||
@@ -325,6 +324,10 @@ public:
|
||||
return VibrationError::NotSupported;
|
||||
}
|
||||
|
||||
virtual bool IsVibrationEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) {
|
||||
return PollingError::NotSupported;
|
||||
}
|
||||
|
||||
@@ -151,6 +151,7 @@ void UpdateRescalingInfo() {
|
||||
ASSERT(false);
|
||||
info.up_scale = 1;
|
||||
info.down_shift = 0;
|
||||
break;
|
||||
}
|
||||
info.up_factor = static_cast<f32>(info.up_scale) / (1U << info.down_shift);
|
||||
info.down_factor = static_cast<f32>(1U << info.down_shift) / info.up_scale;
|
||||
|
||||
@@ -4,14 +4,27 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include "common/bit_util.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/x64/cpu_detect.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
|
||||
static inline u64 xgetbv(u32 index) {
|
||||
return _xgetbv(index);
|
||||
}
|
||||
#else
|
||||
|
||||
#if defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
@@ -39,12 +52,11 @@ static inline void __cpuid(int info[4], u32 function_id) {
|
||||
}
|
||||
|
||||
#define _XCR_XFEATURE_ENABLED_MASK 0
|
||||
static inline u64 _xgetbv(u32 index) {
|
||||
static inline u64 xgetbv(u32 index) {
|
||||
u32 eax, edx;
|
||||
__asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index));
|
||||
return ((u64)edx << 32) | eax;
|
||||
}
|
||||
|
||||
#endif // _MSC_VER
|
||||
|
||||
namespace Common {
|
||||
@@ -107,7 +119,7 @@ static CPUCaps Detect() {
|
||||
// - Is the XSAVE bit set in CPUID?
|
||||
// - XGETBV result has the XCR bit set.
|
||||
if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) {
|
||||
if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
|
||||
if ((xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
|
||||
caps.avx = true;
|
||||
if (Common::Bit<12>(cpu_id[2]))
|
||||
caps.fma = true;
|
||||
@@ -192,4 +204,45 @@ const CPUCaps& GetCPUCaps() {
|
||||
return caps;
|
||||
}
|
||||
|
||||
std::optional<int> GetProcessorCount() {
|
||||
#if defined(_WIN32)
|
||||
// Get the buffer length.
|
||||
DWORD length = 0;
|
||||
GetLogicalProcessorInformation(nullptr, &length);
|
||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
||||
LOG_ERROR(Frontend, "Failed to query core count.");
|
||||
return std::nullopt;
|
||||
}
|
||||
std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> buffer(
|
||||
length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION));
|
||||
// Now query the core count.
|
||||
if (!GetLogicalProcessorInformation(buffer.data(), &length)) {
|
||||
LOG_ERROR(Frontend, "Failed to query core count.");
|
||||
return std::nullopt;
|
||||
}
|
||||
return static_cast<int>(
|
||||
std::count_if(buffer.cbegin(), buffer.cend(), [](const auto& proc_info) {
|
||||
return proc_info.Relationship == RelationProcessorCore;
|
||||
}));
|
||||
#elif defined(__unix__)
|
||||
const int thread_count = std::thread::hardware_concurrency();
|
||||
std::ifstream smt("/sys/devices/system/cpu/smt/active");
|
||||
char state = '0';
|
||||
if (smt) {
|
||||
smt.read(&state, sizeof(state));
|
||||
}
|
||||
switch (state) {
|
||||
case '0':
|
||||
return thread_count;
|
||||
case '1':
|
||||
return thread_count / 2;
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
#else
|
||||
// Shame on you
|
||||
return std::nullopt;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include "common/common_types.h"
|
||||
|
||||
@@ -74,4 +75,7 @@ struct CPUCaps {
|
||||
*/
|
||||
const CPUCaps& GetCPUCaps();
|
||||
|
||||
/// Detects CPU core count
|
||||
std::optional<int> GetProcessorCount();
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -190,8 +190,13 @@ add_library(core STATIC
|
||||
hle/kernel/k_code_memory.h
|
||||
hle/kernel/k_condition_variable.cpp
|
||||
hle/kernel/k_condition_variable.h
|
||||
hle/kernel/k_debug.h
|
||||
hle/kernel/k_dynamic_page_manager.h
|
||||
hle/kernel/k_dynamic_resource_manager.h
|
||||
hle/kernel/k_dynamic_slab_heap.h
|
||||
hle/kernel/k_event.cpp
|
||||
hle/kernel/k_event.h
|
||||
hle/kernel/k_event_info.h
|
||||
hle/kernel/k_handle_table.cpp
|
||||
hle/kernel/k_handle_table.h
|
||||
hle/kernel/k_interrupt_manager.cpp
|
||||
@@ -219,6 +224,8 @@ add_library(core STATIC
|
||||
hle/kernel/k_page_group.h
|
||||
hle/kernel/k_page_table.cpp
|
||||
hle/kernel/k_page_table.h
|
||||
hle/kernel/k_page_table_manager.h
|
||||
hle/kernel/k_page_table_slab_heap.h
|
||||
hle/kernel/k_port.cpp
|
||||
hle/kernel/k_port.h
|
||||
hle/kernel/k_priority_queue.h
|
||||
@@ -240,6 +247,8 @@ add_library(core STATIC
|
||||
hle/kernel/k_server_session.h
|
||||
hle/kernel/k_session.cpp
|
||||
hle/kernel/k_session.h
|
||||
hle/kernel/k_session_request.cpp
|
||||
hle/kernel/k_session_request.h
|
||||
hle/kernel/k_shared_memory.cpp
|
||||
hle/kernel/k_shared_memory.h
|
||||
hle/kernel/k_shared_memory_info.h
|
||||
@@ -249,6 +258,8 @@ add_library(core STATIC
|
||||
hle/kernel/k_synchronization_object.cpp
|
||||
hle/kernel/k_synchronization_object.h
|
||||
hle/kernel/k_system_control.h
|
||||
hle/kernel/k_system_resource.cpp
|
||||
hle/kernel/k_system_resource.h
|
||||
hle/kernel/k_thread.cpp
|
||||
hle/kernel/k_thread.h
|
||||
hle/kernel/k_thread_local_page.cpp
|
||||
@@ -486,10 +497,6 @@ add_library(core STATIC
|
||||
hle/service/hid/irsensor/processor_base.h
|
||||
hle/service/hid/irsensor/tera_plugin_processor.cpp
|
||||
hle/service/hid/irsensor/tera_plugin_processor.h
|
||||
hle/service/jit/jit_context.cpp
|
||||
hle/service/jit/jit_context.h
|
||||
hle/service/jit/jit.cpp
|
||||
hle/service/jit/jit.h
|
||||
hle/service/lbl/lbl.cpp
|
||||
hle/service/lbl/lbl.h
|
||||
hle/service/ldn/lan_discovery.cpp
|
||||
@@ -769,19 +776,15 @@ if (MSVC)
|
||||
/we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data
|
||||
/we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch
|
||||
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
||||
/we4800 # Implicit conversion from 'type' to bool. Possible information loss
|
||||
)
|
||||
else()
|
||||
target_compile_options(core PRIVATE
|
||||
-Werror=conversion
|
||||
-Werror=ignored-qualifiers
|
||||
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess>
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
|
||||
|
||||
$<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>
|
||||
|
||||
-Wno-sign-conversion
|
||||
|
||||
$<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -798,14 +801,18 @@ if (ENABLE_WEB_SERVICE)
|
||||
target_link_libraries(core PRIVATE web_service)
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_x86_64)
|
||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
||||
target_sources(core PRIVATE
|
||||
arm/dynarmic/arm_dynarmic_32.cpp
|
||||
arm/dynarmic/arm_dynarmic_32.h
|
||||
arm/dynarmic/arm_dynarmic_64.cpp
|
||||
arm/dynarmic/arm_dynarmic_64.h
|
||||
arm/dynarmic/arm_dynarmic_32.cpp
|
||||
arm/dynarmic/arm_dynarmic_32.h
|
||||
arm/dynarmic/arm_dynarmic_cp15.cpp
|
||||
arm/dynarmic/arm_dynarmic_cp15.h
|
||||
hle/service/jit/jit_context.cpp
|
||||
hle/service/jit/jit_context.h
|
||||
hle/service/jit/jit.cpp
|
||||
hle/service/jit/jit.h
|
||||
)
|
||||
target_link_libraries(core PRIVATE dynarmic)
|
||||
endif()
|
||||
|
||||
@@ -134,6 +134,14 @@ void ARM_Interface::Run() {
|
||||
}
|
||||
system.ExitDynarmicProfile();
|
||||
|
||||
// If the thread is scheduled for termination, exit the thread.
|
||||
if (current_thread->HasDpc()) {
|
||||
if (current_thread->IsTerminationRequested()) {
|
||||
current_thread->Exit();
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
// Notify the debugger and go to sleep if a breakpoint was hit,
|
||||
// or if the thread is unable to continue for any reason.
|
||||
if (Has(hr, breakpoint) || Has(hr, no_execute)) {
|
||||
|
||||
@@ -301,6 +301,11 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ARCHITECTURE_arm64
|
||||
// TODO: remove when fixed in dynarmic
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::BlockLinking;
|
||||
#endif
|
||||
|
||||
return std::make_unique<Dynarmic::A32::Jit>(config);
|
||||
}
|
||||
|
||||
@@ -450,7 +455,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::S
|
||||
// Frame records are two words long:
|
||||
// fp+0 : pointer to previous frame record
|
||||
// fp+4 : value of lr for frame
|
||||
while (true) {
|
||||
for (size_t i = 0; i < 256; i++) {
|
||||
out.push_back({"", 0, lr, 0, ""});
|
||||
if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) {
|
||||
break;
|
||||
|
||||
@@ -517,7 +517,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::S
|
||||
// Frame records are two words long:
|
||||
// fp+0 : pointer to previous frame record
|
||||
// fp+8 : value of lr for frame
|
||||
while (true) {
|
||||
for (size_t i = 0; i < 256; i++) {
|
||||
out.push_back({"", 0, lr, 0, ""});
|
||||
if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) {
|
||||
break;
|
||||
|
||||
@@ -52,12 +52,16 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1
|
||||
case 4:
|
||||
// CP15_DATA_SYNC_BARRIER
|
||||
return Callback{
|
||||
[](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
|
||||
#ifdef _MSC_VER
|
||||
[](void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
|
||||
#if defined(_MSC_VER) && defined(ARCHITECTURE_x86_64)
|
||||
_mm_mfence();
|
||||
_mm_lfence();
|
||||
#else
|
||||
#elif defined(ARCHITECTURE_x86_64)
|
||||
asm volatile("mfence\n\tlfence\n\t" : : : "memory");
|
||||
#elif defined(ARCHITECTURE_arm64)
|
||||
asm volatile("dsb sy\n\t" : : : "memory");
|
||||
#else
|
||||
#error Unsupported architecture
|
||||
#endif
|
||||
return 0;
|
||||
},
|
||||
@@ -66,11 +70,15 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1
|
||||
case 5:
|
||||
// CP15_DATA_MEMORY_BARRIER
|
||||
return Callback{
|
||||
[](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
|
||||
#ifdef _MSC_VER
|
||||
[](void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
|
||||
#if defined(_MSC_VER) && defined(ARCHITECTURE_x86_64)
|
||||
_mm_mfence();
|
||||
#else
|
||||
#elif defined(ARCHITECTURE_x86_64)
|
||||
asm volatile("mfence\n\t" : : : "memory");
|
||||
#elif defined(ARCHITECTURE_arm64)
|
||||
asm volatile("dmb sy\n\t" : : : "memory");
|
||||
#else
|
||||
#error Unsupported architecture
|
||||
#endif
|
||||
return 0;
|
||||
},
|
||||
@@ -115,7 +123,7 @@ CallbackOrAccessOneWord DynarmicCP15::CompileGetOneWord(bool two, unsigned opc1,
|
||||
CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) {
|
||||
if (!two && opc == 0 && CRm == CoprocReg::C14) {
|
||||
// CNTPCT
|
||||
const auto callback = [](Dynarmic::A32::Jit*, void* arg, u32, u32) -> u64 {
|
||||
const auto callback = [](void* arg, u32, u32) -> u64 {
|
||||
const auto& parent_arg = *static_cast<ARM_Dynarmic_32*>(arg);
|
||||
return parent_arg.system.CoreTiming().GetClockTicks();
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
|
||||
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
|
||||
#endif
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
@@ -13,7 +13,7 @@ ExclusiveMonitor::~ExclusiveMonitor() = default;
|
||||
|
||||
std::unique_ptr<Core::ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory,
|
||||
std::size_t num_cores) {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
|
||||
return std::make_unique<Core::DynarmicExclusiveMonitor>(memory, num_cores);
|
||||
#else
|
||||
// TODO(merry): Passthrough exclusive monitor
|
||||
|
||||
@@ -133,6 +133,56 @@ struct System::Impl {
|
||||
: kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{},
|
||||
cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {}
|
||||
|
||||
void Initialize(System& system) {
|
||||
device_memory = std::make_unique<Core::DeviceMemory>();
|
||||
|
||||
is_multicore = Settings::values.use_multi_core.GetValue();
|
||||
extended_memory_layout = Settings::values.use_extended_memory_layout.GetValue();
|
||||
|
||||
core_timing.SetMulticore(is_multicore);
|
||||
core_timing.Initialize([&system]() { system.RegisterHostThread(); });
|
||||
|
||||
const auto posix_time = std::chrono::system_clock::now().time_since_epoch();
|
||||
const auto current_time =
|
||||
std::chrono::duration_cast<std::chrono::seconds>(posix_time).count();
|
||||
Settings::values.custom_rtc_differential =
|
||||
Settings::values.custom_rtc.value_or(current_time) - current_time;
|
||||
|
||||
// Create a default fs if one doesn't already exist.
|
||||
if (virtual_filesystem == nullptr) {
|
||||
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
|
||||
}
|
||||
if (content_provider == nullptr) {
|
||||
content_provider = std::make_unique<FileSys::ContentProviderUnion>();
|
||||
}
|
||||
|
||||
// Create default implementations of applets if one is not provided.
|
||||
applet_manager.SetDefaultAppletsIfMissing();
|
||||
|
||||
is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue();
|
||||
|
||||
kernel.SetMulticore(is_multicore);
|
||||
cpu_manager.SetMulticore(is_multicore);
|
||||
cpu_manager.SetAsyncGpu(is_async_gpu);
|
||||
}
|
||||
|
||||
void ReinitializeIfNecessary(System& system) {
|
||||
const bool must_reinitialize =
|
||||
is_multicore != Settings::values.use_multi_core.GetValue() ||
|
||||
extended_memory_layout != Settings::values.use_extended_memory_layout.GetValue();
|
||||
|
||||
if (!must_reinitialize) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Kernel, "Re-initializing");
|
||||
|
||||
is_multicore = Settings::values.use_multi_core.GetValue();
|
||||
extended_memory_layout = Settings::values.use_extended_memory_layout.GetValue();
|
||||
|
||||
Initialize(system);
|
||||
}
|
||||
|
||||
SystemResultStatus Run() {
|
||||
std::unique_lock<std::mutex> lk(suspend_guard);
|
||||
status = SystemResultStatus::Success;
|
||||
@@ -178,37 +228,14 @@ struct System::Impl {
|
||||
debugger = std::make_unique<Debugger>(system, port);
|
||||
}
|
||||
|
||||
SystemResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
|
||||
SystemResultStatus SetupForMainProcess(System& system, Frontend::EmuWindow& emu_window) {
|
||||
LOG_DEBUG(Core, "initialized OK");
|
||||
|
||||
device_memory = std::make_unique<Core::DeviceMemory>();
|
||||
|
||||
is_multicore = Settings::values.use_multi_core.GetValue();
|
||||
is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue();
|
||||
|
||||
kernel.SetMulticore(is_multicore);
|
||||
cpu_manager.SetMulticore(is_multicore);
|
||||
cpu_manager.SetAsyncGpu(is_async_gpu);
|
||||
core_timing.SetMulticore(is_multicore);
|
||||
// Setting changes may require a full system reinitialization (e.g., disabling multicore).
|
||||
ReinitializeIfNecessary(system);
|
||||
|
||||
kernel.Initialize();
|
||||
cpu_manager.Initialize();
|
||||
core_timing.Initialize([&system]() { system.RegisterHostThread(); });
|
||||
|
||||
const auto posix_time = std::chrono::system_clock::now().time_since_epoch();
|
||||
const auto current_time =
|
||||
std::chrono::duration_cast<std::chrono::seconds>(posix_time).count();
|
||||
Settings::values.custom_rtc_differential =
|
||||
Settings::values.custom_rtc.value_or(current_time) - current_time;
|
||||
|
||||
// Create a default fs if one doesn't already exist.
|
||||
if (virtual_filesystem == nullptr)
|
||||
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
|
||||
if (content_provider == nullptr)
|
||||
content_provider = std::make_unique<FileSys::ContentProviderUnion>();
|
||||
|
||||
/// Create default implementations of applets if one is not provided.
|
||||
applet_manager.SetDefaultAppletsIfMissing();
|
||||
|
||||
/// Reset all glue registrations
|
||||
arp_manager.ResetAll();
|
||||
@@ -253,11 +280,11 @@ struct System::Impl {
|
||||
return SystemResultStatus::ErrorGetLoader;
|
||||
}
|
||||
|
||||
SystemResultStatus init_result{Init(system, emu_window)};
|
||||
SystemResultStatus init_result{SetupForMainProcess(system, emu_window)};
|
||||
if (init_result != SystemResultStatus::Success) {
|
||||
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
|
||||
static_cast<int>(init_result));
|
||||
Shutdown();
|
||||
ShutdownMainProcess();
|
||||
return init_result;
|
||||
}
|
||||
|
||||
@@ -276,7 +303,7 @@ struct System::Impl {
|
||||
const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
|
||||
if (load_result != Loader::ResultStatus::Success) {
|
||||
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", load_result);
|
||||
Shutdown();
|
||||
ShutdownMainProcess();
|
||||
|
||||
return static_cast<SystemResultStatus>(
|
||||
static_cast<u32>(SystemResultStatus::ErrorLoader) + static_cast<u32>(load_result));
|
||||
@@ -335,7 +362,7 @@ struct System::Impl {
|
||||
return status;
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
void ShutdownMainProcess() {
|
||||
SetShuttingDown(true);
|
||||
|
||||
// Log last frame performance stats if game was loded
|
||||
@@ -363,13 +390,14 @@ struct System::Impl {
|
||||
kernel.ShutdownCores();
|
||||
cpu_manager.Shutdown();
|
||||
debugger.reset();
|
||||
services->KillNVNFlinger();
|
||||
kernel.CloseServices();
|
||||
services.reset();
|
||||
service_manager.reset();
|
||||
cheat_engine.reset();
|
||||
telemetry_session.reset();
|
||||
time_manager.Shutdown();
|
||||
core_timing.Shutdown();
|
||||
core_timing.ClearPendingEvents();
|
||||
app_loader.reset();
|
||||
audio_core.reset();
|
||||
gpu_core.reset();
|
||||
@@ -377,7 +405,6 @@ struct System::Impl {
|
||||
perf_stats.reset();
|
||||
kernel.Shutdown();
|
||||
memory.Reset();
|
||||
applet_manager.ClearAll();
|
||||
|
||||
if (auto room_member = room_network.GetRoomMember().lock()) {
|
||||
Network::GameInfo game_info{};
|
||||
@@ -500,6 +527,7 @@ struct System::Impl {
|
||||
|
||||
bool is_multicore{};
|
||||
bool is_async_gpu{};
|
||||
bool extended_memory_layout{};
|
||||
|
||||
ExecuteProgramCallback execute_program_callback;
|
||||
ExitCallback exit_callback;
|
||||
@@ -520,6 +548,10 @@ const CpuManager& System::GetCpuManager() const {
|
||||
return impl->cpu_manager;
|
||||
}
|
||||
|
||||
void System::Initialize() {
|
||||
impl->Initialize(*this);
|
||||
}
|
||||
|
||||
SystemResultStatus System::Run() {
|
||||
return impl->Run();
|
||||
}
|
||||
@@ -540,8 +572,8 @@ void System::InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size) {
|
||||
impl->kernel.InvalidateCpuInstructionCacheRange(addr, size);
|
||||
}
|
||||
|
||||
void System::Shutdown() {
|
||||
impl->Shutdown();
|
||||
void System::ShutdownMainProcess() {
|
||||
impl->ShutdownMainProcess();
|
||||
}
|
||||
|
||||
bool System::IsShuttingDown() const {
|
||||
|
||||
@@ -142,6 +142,12 @@ public:
|
||||
System(System&&) = delete;
|
||||
System& operator=(System&&) = delete;
|
||||
|
||||
/**
|
||||
* Initializes the system
|
||||
* This function will initialize core functionaility used for system emulation
|
||||
*/
|
||||
void Initialize();
|
||||
|
||||
/**
|
||||
* Run the OS and Application
|
||||
* This function will start emulation and run the relevant devices
|
||||
@@ -166,8 +172,8 @@ public:
|
||||
|
||||
void InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size);
|
||||
|
||||
/// Shutdown the emulated system.
|
||||
void Shutdown();
|
||||
/// Shutdown the main emulated process.
|
||||
void ShutdownMainProcess();
|
||||
|
||||
/// Check if the core is shutting down.
|
||||
[[nodiscard]] bool IsShuttingDown() const;
|
||||
|
||||
@@ -40,7 +40,9 @@ struct CoreTiming::Event {
|
||||
CoreTiming::CoreTiming()
|
||||
: clock{Common::CreateBestMatchingClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)} {}
|
||||
|
||||
CoreTiming::~CoreTiming() = default;
|
||||
CoreTiming::~CoreTiming() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
void CoreTiming::ThreadEntry(CoreTiming& instance) {
|
||||
constexpr char name[] = "HostTiming";
|
||||
@@ -53,6 +55,7 @@ void CoreTiming::ThreadEntry(CoreTiming& instance) {
|
||||
}
|
||||
|
||||
void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
|
||||
Reset();
|
||||
on_thread_init = std::move(on_thread_init_);
|
||||
event_fifo_id = 0;
|
||||
shutting_down = false;
|
||||
@@ -65,17 +68,8 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
|
||||
}
|
||||
}
|
||||
|
||||
void CoreTiming::Shutdown() {
|
||||
paused = true;
|
||||
shutting_down = true;
|
||||
pause_event.Set();
|
||||
event.Set();
|
||||
if (timer_thread) {
|
||||
timer_thread->join();
|
||||
}
|
||||
ClearPendingEvents();
|
||||
timer_thread.reset();
|
||||
has_started = false;
|
||||
void CoreTiming::ClearPendingEvents() {
|
||||
event_queue.clear();
|
||||
}
|
||||
|
||||
void CoreTiming::Pause(bool is_paused) {
|
||||
@@ -196,10 +190,6 @@ u64 CoreTiming::GetClockTicks() const {
|
||||
return CpuCyclesToClockCycles(ticks);
|
||||
}
|
||||
|
||||
void CoreTiming::ClearPendingEvents() {
|
||||
event_queue.clear();
|
||||
}
|
||||
|
||||
void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
|
||||
std::scoped_lock lock{basic_lock};
|
||||
|
||||
@@ -307,6 +297,18 @@ void CoreTiming::ThreadLoop() {
|
||||
}
|
||||
}
|
||||
|
||||
void CoreTiming::Reset() {
|
||||
paused = true;
|
||||
shutting_down = true;
|
||||
pause_event.Set();
|
||||
event.Set();
|
||||
if (timer_thread) {
|
||||
timer_thread->join();
|
||||
}
|
||||
timer_thread.reset();
|
||||
has_started = false;
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const {
|
||||
if (is_multicore) {
|
||||
return clock->GetTimeNS();
|
||||
|
||||
@@ -61,19 +61,14 @@ public:
|
||||
/// required to end slice - 1 and start slice 0 before the first cycle of code is executed.
|
||||
void Initialize(std::function<void()>&& on_thread_init_);
|
||||
|
||||
/// Tears down all timing related functionality.
|
||||
void Shutdown();
|
||||
/// Clear all pending events. This should ONLY be done on exit.
|
||||
void ClearPendingEvents();
|
||||
|
||||
/// Sets if emulation is multicore or single core, must be set before Initialize
|
||||
void SetMulticore(bool is_multicore_) {
|
||||
is_multicore = is_multicore_;
|
||||
}
|
||||
|
||||
/// Check if it's using host timing.
|
||||
bool IsHostTiming() const {
|
||||
return is_multicore;
|
||||
}
|
||||
|
||||
/// Pauses/Unpauses the execution of the timer thread.
|
||||
void Pause(bool is_paused);
|
||||
|
||||
@@ -136,12 +131,11 @@ public:
|
||||
private:
|
||||
struct Event;
|
||||
|
||||
/// Clear all pending events. This should ONLY be done on exit.
|
||||
void ClearPendingEvents();
|
||||
|
||||
static void ThreadEntry(CoreTiming& instance);
|
||||
void ThreadLoop();
|
||||
|
||||
void Reset();
|
||||
|
||||
std::unique_ptr<Common::WallClock> clock;
|
||||
|
||||
s64 global_timer = 0;
|
||||
|
||||
@@ -180,7 +180,7 @@ void CpuManager::ShutdownThread() {
|
||||
auto* thread = kernel.GetCurrentEmuThread();
|
||||
auto core = is_multicore ? kernel.CurrentPhysicalCoreIndex() : 0;
|
||||
|
||||
Common::Fiber::YieldTo(thread->GetHostContext(), *core_data[core].host_context);
|
||||
Common::Fiber::YieldTo(thread->GetHostContext(), core_data[core].host_context);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
@@ -217,7 +217,7 @@ void CpuManager::RunThread(std::size_t core) {
|
||||
auto* thread = scheduler.GetSchedulerCurrentThread();
|
||||
Kernel::SetCurrentThread(kernel, thread);
|
||||
|
||||
Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext());
|
||||
Common::Fiber::YieldTo(data.host_context, thread->GetHostContext());
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -27,12 +27,21 @@ static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
|
||||
const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
|
||||
std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
|
||||
c(received_data);
|
||||
AsyncReceiveInto(r, buffer, c);
|
||||
}
|
||||
|
||||
AsyncReceiveInto(r, buffer, c);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Callback>
|
||||
static void AsyncAccept(boost::asio::ip::tcp::acceptor& acceptor, Callback&& c) {
|
||||
acceptor.async_accept([&, c](const boost::system::error_code& error, auto&& peer_socket) {
|
||||
if (!error.failed()) {
|
||||
c(peer_socket);
|
||||
AsyncAccept(acceptor, c);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Readable, typename Buffer>
|
||||
static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
|
||||
static_assert(std::is_trivial_v<Buffer>);
|
||||
@@ -59,9 +68,7 @@ namespace Core {
|
||||
|
||||
class DebuggerImpl : public DebuggerBackend {
|
||||
public:
|
||||
explicit DebuggerImpl(Core::System& system_, u16 port)
|
||||
: system{system_}, signal_pipe{io_context}, client_socket{io_context} {
|
||||
frontend = std::make_unique<GDBStub>(*this, system);
|
||||
explicit DebuggerImpl(Core::System& system_, u16 port) : system{system_} {
|
||||
InitializeServer(port);
|
||||
}
|
||||
|
||||
@@ -70,39 +77,42 @@ public:
|
||||
}
|
||||
|
||||
bool SignalDebugger(SignalInfo signal_info) {
|
||||
{
|
||||
std::scoped_lock lk{connection_lock};
|
||||
std::scoped_lock lk{connection_lock};
|
||||
|
||||
if (stopped) {
|
||||
// Do not notify the debugger about another event.
|
||||
// It should be ignored.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set up the state.
|
||||
stopped = true;
|
||||
info = signal_info;
|
||||
if (stopped || !state) {
|
||||
// Do not notify the debugger about another event.
|
||||
// It should be ignored.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set up the state.
|
||||
stopped = true;
|
||||
state->info = signal_info;
|
||||
|
||||
// Write a single byte into the pipe to wake up the debug interface.
|
||||
boost::asio::write(signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped)));
|
||||
boost::asio::write(state->signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// These functions are callbacks from the frontend, and the lock will be held.
|
||||
// There is no need to relock it.
|
||||
|
||||
std::span<const u8> ReadFromClient() override {
|
||||
return ReceiveInto(client_socket, client_data);
|
||||
return ReceiveInto(state->client_socket, state->client_data);
|
||||
}
|
||||
|
||||
void WriteToClient(std::span<const u8> data) override {
|
||||
boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes()));
|
||||
boost::asio::write(state->client_socket,
|
||||
boost::asio::buffer(data.data(), data.size_bytes()));
|
||||
}
|
||||
|
||||
void SetActiveThread(Kernel::KThread* thread) override {
|
||||
active_thread = thread;
|
||||
state->active_thread = thread;
|
||||
}
|
||||
|
||||
Kernel::KThread* GetActiveThread() override {
|
||||
return active_thread;
|
||||
return state->active_thread;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -113,65 +123,78 @@ private:
|
||||
|
||||
// Run the connection thread.
|
||||
connection_thread = std::jthread([&, port](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("Debugger");
|
||||
|
||||
try {
|
||||
// Initialize the listening socket and accept a new client.
|
||||
tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port};
|
||||
tcp::acceptor acceptor{io_context, endpoint};
|
||||
|
||||
acceptor.async_accept(client_socket, [](const auto&) {});
|
||||
io_context.run_one();
|
||||
io_context.restart();
|
||||
AsyncAccept(acceptor, [&](auto&& peer) { AcceptConnection(std::move(peer)); });
|
||||
|
||||
if (stop_token.stop_requested()) {
|
||||
return;
|
||||
while (!stop_token.stop_requested() && io_context.run()) {
|
||||
}
|
||||
|
||||
ThreadLoop(stop_token);
|
||||
} catch (const std::exception& ex) {
|
||||
LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void AcceptConnection(boost::asio::ip::tcp::socket&& peer) {
|
||||
LOG_INFO(Debug_GDBStub, "Accepting new peer connection");
|
||||
|
||||
std::scoped_lock lk{connection_lock};
|
||||
|
||||
// Ensure everything is stopped.
|
||||
PauseEmulation();
|
||||
|
||||
// Set up the new frontend.
|
||||
frontend = std::make_unique<GDBStub>(*this, system);
|
||||
|
||||
// Set the new state. This will tear down any existing state.
|
||||
state = ConnectionState{
|
||||
.client_socket{std::move(peer)},
|
||||
.signal_pipe{io_context},
|
||||
.info{},
|
||||
.active_thread{},
|
||||
.client_data{},
|
||||
.pipe_data{},
|
||||
};
|
||||
|
||||
// Set up the client signals for new data.
|
||||
AsyncReceiveInto(state->signal_pipe, state->pipe_data, [&](auto d) { PipeData(d); });
|
||||
AsyncReceiveInto(state->client_socket, state->client_data, [&](auto d) { ClientData(d); });
|
||||
|
||||
// Set the active thread.
|
||||
UpdateActiveThread();
|
||||
|
||||
// Set up the frontend.
|
||||
frontend->Connected();
|
||||
}
|
||||
|
||||
void ShutdownServer() {
|
||||
connection_thread.request_stop();
|
||||
io_context.stop();
|
||||
connection_thread.join();
|
||||
}
|
||||
|
||||
void ThreadLoop(std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("Debugger");
|
||||
|
||||
// Set up the client signals for new data.
|
||||
AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); });
|
||||
AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); });
|
||||
|
||||
// Set the active thread.
|
||||
UpdateActiveThread();
|
||||
|
||||
// Set up the frontend.
|
||||
frontend->Connected();
|
||||
|
||||
// Main event loop.
|
||||
while (!stop_token.stop_requested() && io_context.run()) {
|
||||
}
|
||||
}
|
||||
|
||||
void PipeData(std::span<const u8> data) {
|
||||
switch (info.type) {
|
||||
std::scoped_lock lk{connection_lock};
|
||||
|
||||
switch (state->info.type) {
|
||||
case SignalType::Stopped:
|
||||
case SignalType::Watchpoint:
|
||||
// Stop emulation.
|
||||
PauseEmulation();
|
||||
|
||||
// Notify the client.
|
||||
active_thread = info.thread;
|
||||
state->active_thread = state->info.thread;
|
||||
UpdateActiveThread();
|
||||
|
||||
if (info.type == SignalType::Watchpoint) {
|
||||
frontend->Watchpoint(active_thread, *info.watchpoint);
|
||||
if (state->info.type == SignalType::Watchpoint) {
|
||||
frontend->Watchpoint(state->active_thread, *state->info.watchpoint);
|
||||
} else {
|
||||
frontend->Stopped(active_thread);
|
||||
frontend->Stopped(state->active_thread);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -179,8 +202,8 @@ private:
|
||||
frontend->ShuttingDown();
|
||||
|
||||
// Wait for emulation to shut down gracefully now.
|
||||
signal_pipe.close();
|
||||
client_socket.shutdown(boost::asio::socket_base::shutdown_both);
|
||||
state->signal_pipe.close();
|
||||
state->client_socket.shutdown(boost::asio::socket_base::shutdown_both);
|
||||
LOG_INFO(Debug_GDBStub, "Shut down server");
|
||||
|
||||
break;
|
||||
@@ -188,17 +211,16 @@ private:
|
||||
}
|
||||
|
||||
void ClientData(std::span<const u8> data) {
|
||||
std::scoped_lock lk{connection_lock};
|
||||
|
||||
const auto actions{frontend->ClientData(data)};
|
||||
for (const auto action : actions) {
|
||||
switch (action) {
|
||||
case DebuggerAction::Interrupt: {
|
||||
{
|
||||
std::scoped_lock lk{connection_lock};
|
||||
stopped = true;
|
||||
}
|
||||
stopped = true;
|
||||
PauseEmulation();
|
||||
UpdateActiveThread();
|
||||
frontend->Stopped(active_thread);
|
||||
frontend->Stopped(state->active_thread);
|
||||
break;
|
||||
}
|
||||
case DebuggerAction::Continue:
|
||||
@@ -206,15 +228,15 @@ private:
|
||||
break;
|
||||
case DebuggerAction::StepThreadUnlocked:
|
||||
MarkResumed([&] {
|
||||
active_thread->SetStepState(Kernel::StepState::StepPending);
|
||||
active_thread->Resume(Kernel::SuspendType::Debug);
|
||||
ResumeEmulation(active_thread);
|
||||
state->active_thread->SetStepState(Kernel::StepState::StepPending);
|
||||
state->active_thread->Resume(Kernel::SuspendType::Debug);
|
||||
ResumeEmulation(state->active_thread);
|
||||
});
|
||||
break;
|
||||
case DebuggerAction::StepThreadLocked: {
|
||||
MarkResumed([&] {
|
||||
active_thread->SetStepState(Kernel::StepState::StepPending);
|
||||
active_thread->Resume(Kernel::SuspendType::Debug);
|
||||
state->active_thread->SetStepState(Kernel::StepState::StepPending);
|
||||
state->active_thread->Resume(Kernel::SuspendType::Debug);
|
||||
});
|
||||
break;
|
||||
}
|
||||
@@ -254,15 +276,14 @@ private:
|
||||
template <typename Callback>
|
||||
void MarkResumed(Callback&& cb) {
|
||||
Kernel::KScopedSchedulerLock sl{system.Kernel()};
|
||||
std::scoped_lock cl{connection_lock};
|
||||
stopped = false;
|
||||
cb();
|
||||
}
|
||||
|
||||
void UpdateActiveThread() {
|
||||
const auto& threads{ThreadList()};
|
||||
if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) {
|
||||
active_thread = threads[0];
|
||||
if (std::find(threads.begin(), threads.end(), state->active_thread) == threads.end()) {
|
||||
state->active_thread = threads[0];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,18 +295,22 @@ private:
|
||||
System& system;
|
||||
std::unique_ptr<DebuggerFrontend> frontend;
|
||||
|
||||
boost::asio::io_context io_context;
|
||||
std::jthread connection_thread;
|
||||
std::mutex connection_lock;
|
||||
boost::asio::io_context io_context;
|
||||
boost::process::async_pipe signal_pipe;
|
||||
boost::asio::ip::tcp::socket client_socket;
|
||||
|
||||
SignalInfo info;
|
||||
Kernel::KThread* active_thread;
|
||||
bool pipe_data;
|
||||
bool stopped;
|
||||
struct ConnectionState {
|
||||
boost::asio::ip::tcp::socket client_socket;
|
||||
boost::process::async_pipe signal_pipe;
|
||||
|
||||
std::array<u8, 4096> client_data;
|
||||
SignalInfo info;
|
||||
Kernel::KThread* active_thread;
|
||||
std::array<u8, 4096> client_data;
|
||||
bool pipe_data;
|
||||
};
|
||||
|
||||
std::optional<ConnectionState> state{};
|
||||
bool stopped{};
|
||||
};
|
||||
|
||||
Debugger::Debugger(Core::System& system, u16 port) {
|
||||
|
||||
@@ -606,6 +606,8 @@ void GDBStub::HandleQuery(std::string_view command) {
|
||||
} else if (command.starts_with("StartNoAckMode")) {
|
||||
no_ack = true;
|
||||
SendReply(GDB_STUB_REPLY_OK);
|
||||
} else if (command.starts_with("Rcmd,")) {
|
||||
HandleRcmd(Common::HexStringToVector(command.substr(5), false));
|
||||
} else {
|
||||
SendReply(GDB_STUB_REPLY_EMPTY);
|
||||
}
|
||||
@@ -645,6 +647,155 @@ void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>&
|
||||
}
|
||||
}
|
||||
|
||||
constexpr std::array<std::pair<const char*, Kernel::Svc::MemoryState>, 22> MemoryStateNames{{
|
||||
{"----- Free -----", Kernel::Svc::MemoryState::Free},
|
||||
{"Io ", Kernel::Svc::MemoryState::Io},
|
||||
{"Static ", Kernel::Svc::MemoryState::Static},
|
||||
{"Code ", Kernel::Svc::MemoryState::Code},
|
||||
{"CodeData ", Kernel::Svc::MemoryState::CodeData},
|
||||
{"Normal ", Kernel::Svc::MemoryState::Normal},
|
||||
{"Shared ", Kernel::Svc::MemoryState::Shared},
|
||||
{"AliasCode ", Kernel::Svc::MemoryState::AliasCode},
|
||||
{"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData},
|
||||
{"Ipc ", Kernel::Svc::MemoryState::Ipc},
|
||||
{"Stack ", Kernel::Svc::MemoryState::Stack},
|
||||
{"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal},
|
||||
{"Transfered ", Kernel::Svc::MemoryState::Transfered},
|
||||
{"SharedTransfered", Kernel::Svc::MemoryState::SharedTransfered},
|
||||
{"SharedCode ", Kernel::Svc::MemoryState::SharedCode},
|
||||
{"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible},
|
||||
{"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc},
|
||||
{"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc},
|
||||
{"Kernel ", Kernel::Svc::MemoryState::Kernel},
|
||||
{"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode},
|
||||
{"CodeOut ", Kernel::Svc::MemoryState::CodeOut},
|
||||
{"Coverage ", Kernel::Svc::MemoryState::Coverage},
|
||||
}};
|
||||
|
||||
static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) {
|
||||
for (size_t i = 0; i < MemoryStateNames.size(); i++) {
|
||||
if (std::get<1>(MemoryStateNames[i]) == state) {
|
||||
return std::get<0>(MemoryStateNames[i]);
|
||||
}
|
||||
}
|
||||
return "Unknown ";
|
||||
}
|
||||
|
||||
static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::MemoryInfo& info) {
|
||||
if (info.state == Kernel::Svc::MemoryState::Free) {
|
||||
return " ";
|
||||
}
|
||||
|
||||
switch (info.permission) {
|
||||
case Kernel::Svc::MemoryPermission::ReadExecute:
|
||||
return "r-x";
|
||||
case Kernel::Svc::MemoryPermission::Read:
|
||||
return "r--";
|
||||
case Kernel::Svc::MemoryPermission::ReadWrite:
|
||||
return "rw-";
|
||||
default:
|
||||
return "---";
|
||||
}
|
||||
}
|
||||
|
||||
static VAddr GetModuleEnd(Kernel::KPageTable& page_table, VAddr base) {
|
||||
Kernel::Svc::MemoryInfo mem_info;
|
||||
VAddr cur_addr{base};
|
||||
|
||||
// Expect: r-x Code (.text)
|
||||
mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
|
||||
cur_addr = mem_info.base_address + mem_info.size;
|
||||
if (mem_info.state != Kernel::Svc::MemoryState::Code ||
|
||||
mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) {
|
||||
return cur_addr - 1;
|
||||
}
|
||||
|
||||
// Expect: r-- Code (.rodata)
|
||||
mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
|
||||
cur_addr = mem_info.base_address + mem_info.size;
|
||||
if (mem_info.state != Kernel::Svc::MemoryState::Code ||
|
||||
mem_info.permission != Kernel::Svc::MemoryPermission::Read) {
|
||||
return cur_addr - 1;
|
||||
}
|
||||
|
||||
// Expect: rw- CodeData (.data)
|
||||
mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
|
||||
cur_addr = mem_info.base_address + mem_info.size;
|
||||
return cur_addr - 1;
|
||||
}
|
||||
|
||||
void GDBStub::HandleRcmd(const std::vector<u8>& command) {
|
||||
std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
|
||||
std::string reply;
|
||||
|
||||
auto* process = system.CurrentProcess();
|
||||
auto& page_table = process->PageTable();
|
||||
|
||||
if (command_str == "get info") {
|
||||
Loader::AppLoader::Modules modules;
|
||||
system.GetAppLoader().ReadNSOModules(modules);
|
||||
|
||||
reply = fmt::format("Process: {:#x} ({})\n"
|
||||
"Program Id: {:#018x}\n",
|
||||
process->GetProcessID(), process->GetName(), process->GetProgramID());
|
||||
reply +=
|
||||
fmt::format("Layout:\n"
|
||||
" Alias: {:#012x} - {:#012x}\n"
|
||||
" Heap: {:#012x} - {:#012x}\n"
|
||||
" Aslr: {:#012x} - {:#012x}\n"
|
||||
" Stack: {:#012x} - {:#012x}\n"
|
||||
"Modules:\n",
|
||||
page_table.GetAliasRegionStart(), page_table.GetAliasRegionEnd(),
|
||||
page_table.GetHeapRegionStart(), page_table.GetHeapRegionEnd(),
|
||||
page_table.GetAliasCodeRegionStart(), page_table.GetAliasCodeRegionEnd(),
|
||||
page_table.GetStackRegionStart(), page_table.GetStackRegionEnd());
|
||||
|
||||
for (const auto& [vaddr, name] : modules) {
|
||||
reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr,
|
||||
GetModuleEnd(page_table, vaddr), name);
|
||||
}
|
||||
} else if (command_str == "get mappings") {
|
||||
reply = "Mappings:\n";
|
||||
VAddr cur_addr = 0;
|
||||
|
||||
while (true) {
|
||||
using MemoryAttribute = Kernel::Svc::MemoryAttribute;
|
||||
|
||||
auto mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
|
||||
|
||||
if (mem_info.state != Kernel::Svc::MemoryState::Inaccessible ||
|
||||
mem_info.base_address + mem_info.size - 1 != std::numeric_limits<u64>::max()) {
|
||||
const char* state = GetMemoryStateName(mem_info.state);
|
||||
const char* perm = GetMemoryPermissionString(mem_info);
|
||||
|
||||
const char l = True(mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-';
|
||||
const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
|
||||
const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
|
||||
const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
|
||||
|
||||
reply +=
|
||||
fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{} [{}, {}]\n",
|
||||
mem_info.base_address, mem_info.base_address + mem_info.size - 1,
|
||||
perm, state, l, i, d, u, mem_info.ipc_count, mem_info.device_count);
|
||||
}
|
||||
|
||||
const uintptr_t next_address = mem_info.base_address + mem_info.size;
|
||||
if (next_address <= cur_addr) {
|
||||
break;
|
||||
}
|
||||
|
||||
cur_addr = next_address;
|
||||
}
|
||||
} else if (command_str == "help") {
|
||||
reply = "Commands:\n get info\n get mappings\n";
|
||||
} else {
|
||||
reply = "Unknown command.\nCommands:\n get info\n get mappings\n";
|
||||
}
|
||||
|
||||
std::span<const u8> reply_span{reinterpret_cast<u8*>(&reply.front()), reply.size()};
|
||||
SendReply(Common::HexToString(reply_span, false));
|
||||
}
|
||||
|
||||
Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
|
||||
const auto& threads{system.GlobalSchedulerContext().GetThreadList()};
|
||||
for (auto* thread : threads) {
|
||||
|
||||
@@ -32,6 +32,7 @@ private:
|
||||
void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions);
|
||||
void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions);
|
||||
void HandleQuery(std::string_view command);
|
||||
void HandleRcmd(const std::vector<u8>& command);
|
||||
void HandleBreakpointInsert(std::string_view command);
|
||||
void HandleBreakpointRemove(std::string_view command);
|
||||
std::vector<char>::const_iterator CommandEnd() const;
|
||||
|
||||
@@ -31,12 +31,14 @@ public:
|
||||
DramMemoryMap::Base;
|
||||
}
|
||||
|
||||
u8* GetPointer(PAddr addr) {
|
||||
return buffer.BackingBasePointer() + (addr - DramMemoryMap::Base);
|
||||
template <typename T>
|
||||
T* GetPointer(PAddr addr) {
|
||||
return reinterpret_cast<T*>(buffer.BackingBasePointer() + (addr - DramMemoryMap::Base));
|
||||
}
|
||||
|
||||
const u8* GetPointer(PAddr addr) const {
|
||||
return buffer.BackingBasePointer() + (addr - DramMemoryMap::Base);
|
||||
template <typename T>
|
||||
const T* GetPointer(PAddr addr) const {
|
||||
return reinterpret_cast<T*>(buffer.BackingBasePointer() + (addr - DramMemoryMap::Base));
|
||||
}
|
||||
|
||||
Common::HostMemory buffer;
|
||||
|
||||
@@ -232,8 +232,8 @@ const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const {
|
||||
|
||||
std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const {
|
||||
const auto program_id = secure_partition->GetProgramTitleID();
|
||||
const auto iter = std::find_if(
|
||||
ncas.begin(), ncas.end(), [this, type, program_id](const std::shared_ptr<NCA>& nca) {
|
||||
const auto iter =
|
||||
std::find_if(ncas.begin(), ncas.end(), [type, program_id](const std::shared_ptr<NCA>& nca) {
|
||||
return nca->GetType() == type && nca->GetTitleId() == program_id;
|
||||
});
|
||||
return iter == ncas.end() ? nullptr : *iter;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
@@ -37,6 +38,27 @@ std::string LanguageEntry::GetDeveloperName() const {
|
||||
developer_name.size());
|
||||
}
|
||||
|
||||
constexpr std::array<Language, 18> language_to_codes = {{
|
||||
Language::Japanese,
|
||||
Language::AmericanEnglish,
|
||||
Language::French,
|
||||
Language::German,
|
||||
Language::Italian,
|
||||
Language::Spanish,
|
||||
Language::Chinese,
|
||||
Language::Korean,
|
||||
Language::Dutch,
|
||||
Language::Portuguese,
|
||||
Language::Russian,
|
||||
Language::Taiwanese,
|
||||
Language::BritishEnglish,
|
||||
Language::CanadianFrench,
|
||||
Language::LatinAmericanSpanish,
|
||||
Language::Chinese,
|
||||
Language::Taiwanese,
|
||||
Language::BrazilianPortuguese,
|
||||
}};
|
||||
|
||||
NACP::NACP() = default;
|
||||
|
||||
NACP::NACP(VirtualFile file) {
|
||||
@@ -45,9 +67,13 @@ NACP::NACP(VirtualFile file) {
|
||||
|
||||
NACP::~NACP() = default;
|
||||
|
||||
const LanguageEntry& NACP::GetLanguageEntry(Language language) const {
|
||||
if (language != Language::Default) {
|
||||
return raw.language_entries.at(static_cast<u8>(language));
|
||||
const LanguageEntry& NACP::GetLanguageEntry() const {
|
||||
Language language = language_to_codes[Settings::values.language_index.GetValue()];
|
||||
|
||||
{
|
||||
const auto& language_entry = raw.language_entries.at(static_cast<u8>(language));
|
||||
if (!language_entry.GetApplicationName().empty())
|
||||
return language_entry;
|
||||
}
|
||||
|
||||
for (const auto& language_entry : raw.language_entries) {
|
||||
@@ -55,16 +81,15 @@ const LanguageEntry& NACP::GetLanguageEntry(Language language) const {
|
||||
return language_entry;
|
||||
}
|
||||
|
||||
// Fallback to English
|
||||
return GetLanguageEntry(Language::AmericanEnglish);
|
||||
return raw.language_entries.at(static_cast<u8>(Language::AmericanEnglish));
|
||||
}
|
||||
|
||||
std::string NACP::GetApplicationName(Language language) const {
|
||||
return GetLanguageEntry(language).GetApplicationName();
|
||||
std::string NACP::GetApplicationName() const {
|
||||
return GetLanguageEntry().GetApplicationName();
|
||||
}
|
||||
|
||||
std::string NACP::GetDeveloperName(Language language) const {
|
||||
return GetLanguageEntry(language).GetDeveloperName();
|
||||
std::string NACP::GetDeveloperName() const {
|
||||
return GetLanguageEntry().GetDeveloperName();
|
||||
}
|
||||
|
||||
u64 NACP::GetTitleId() const {
|
||||
|
||||
@@ -101,9 +101,9 @@ public:
|
||||
explicit NACP(VirtualFile file);
|
||||
~NACP();
|
||||
|
||||
const LanguageEntry& GetLanguageEntry(Language language = Language::Default) const;
|
||||
std::string GetApplicationName(Language language = Language::Default) const;
|
||||
std::string GetDeveloperName(Language language = Language::Default) const;
|
||||
const LanguageEntry& GetLanguageEntry() const;
|
||||
std::string GetApplicationName() const;
|
||||
std::string GetDeveloperName() const;
|
||||
u64 GetTitleId() const;
|
||||
u64 GetDLCBaseTitleId() const;
|
||||
std::string GetVersionString() const;
|
||||
|
||||
@@ -127,7 +127,7 @@ void ProgramMetadata::LoadManual(bool is_64_bit, ProgramAddressSpaceType address
|
||||
}
|
||||
|
||||
bool ProgramMetadata::Is64BitProgram() const {
|
||||
return npdm_header.has_64_bit_instructions;
|
||||
return npdm_header.has_64_bit_instructions.As<bool>();
|
||||
}
|
||||
|
||||
ProgramAddressSpaceType ProgramMetadata::GetAddressSpaceType() const {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/uuid.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
@@ -59,6 +60,36 @@ bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataA
|
||||
attr.title_id == 0 && attr.save_id == 0);
|
||||
}
|
||||
|
||||
std::string GetFutureSaveDataPath(SaveDataSpaceId space_id, SaveDataType type, u64 title_id,
|
||||
u128 user_id) {
|
||||
// Only detect nand user saves.
|
||||
const auto space_id_path = [space_id]() -> std::string_view {
|
||||
switch (space_id) {
|
||||
case SaveDataSpaceId::NandUser:
|
||||
return "/user/save";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}();
|
||||
|
||||
if (space_id_path.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
Common::UUID uuid;
|
||||
std::memcpy(uuid.uuid.data(), user_id.data(), sizeof(Common::UUID));
|
||||
|
||||
// Only detect account/device saves from the future location.
|
||||
switch (type) {
|
||||
case SaveDataType::SaveData:
|
||||
return fmt::format("{}/account/{}/{:016X}/1", space_id_path, uuid.RawString(), title_id);
|
||||
case SaveDataType::DeviceSaveData:
|
||||
return fmt::format("{}/device/{:016X}/1", space_id_path, title_id);
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
std::string SaveDataAttribute::DebugInfo() const {
|
||||
@@ -82,7 +113,7 @@ ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space,
|
||||
PrintSaveDataAttributeWarnings(meta);
|
||||
|
||||
const auto save_directory =
|
||||
GetFullPath(system, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
|
||||
GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
|
||||
|
||||
auto out = dir->CreateDirectoryRelative(save_directory);
|
||||
|
||||
@@ -99,7 +130,7 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space,
|
||||
const SaveDataAttribute& meta) const {
|
||||
|
||||
const auto save_directory =
|
||||
GetFullPath(system, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
|
||||
GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
|
||||
|
||||
auto out = dir->GetDirectoryRelative(save_directory);
|
||||
|
||||
@@ -134,9 +165,9 @@ std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
|
||||
}
|
||||
}
|
||||
|
||||
std::string SaveDataFactory::GetFullPath(Core::System& system, SaveDataSpaceId space,
|
||||
SaveDataType type, u64 title_id, u128 user_id,
|
||||
u64 save_id) {
|
||||
std::string SaveDataFactory::GetFullPath(Core::System& system, VirtualDir dir,
|
||||
SaveDataSpaceId space, SaveDataType type, u64 title_id,
|
||||
u128 user_id, u64 save_id) {
|
||||
// According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
|
||||
// be interpreted as the title id of the current process.
|
||||
if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) {
|
||||
@@ -145,6 +176,17 @@ std::string SaveDataFactory::GetFullPath(Core::System& system, SaveDataSpaceId s
|
||||
}
|
||||
}
|
||||
|
||||
// For compat with a future impl.
|
||||
if (std::string future_path =
|
||||
GetFutureSaveDataPath(space, type, title_id & ~(0xFFULL), user_id);
|
||||
!future_path.empty()) {
|
||||
// Check if this location exists, and prefer it over the old.
|
||||
if (const auto future_dir = dir->GetDirectoryRelative(future_path); future_dir != nullptr) {
|
||||
LOG_INFO(Service_FS, "Using save at new location: {}", future_path);
|
||||
return future_path;
|
||||
}
|
||||
}
|
||||
|
||||
std::string out = GetSaveDataSpaceIdPath(space);
|
||||
|
||||
switch (type) {
|
||||
@@ -167,7 +209,8 @@ std::string SaveDataFactory::GetFullPath(Core::System& system, SaveDataSpaceId s
|
||||
|
||||
SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
|
||||
u128 user_id) const {
|
||||
const auto path = GetFullPath(system, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
|
||||
const auto path =
|
||||
GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
|
||||
const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
|
||||
|
||||
const auto size_file = relative_dir->GetFile(SAVE_DATA_SIZE_FILENAME);
|
||||
@@ -185,7 +228,8 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
|
||||
|
||||
void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
|
||||
SaveDataSize new_value) const {
|
||||
const auto path = GetFullPath(system, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
|
||||
const auto path =
|
||||
GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
|
||||
const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
|
||||
|
||||
const auto size_file = relative_dir->CreateFile(SAVE_DATA_SIZE_FILENAME);
|
||||
|
||||
@@ -95,8 +95,8 @@ public:
|
||||
VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
|
||||
|
||||
static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space);
|
||||
static std::string GetFullPath(Core::System& system, SaveDataSpaceId space, SaveDataType type,
|
||||
u64 title_id, u128 user_id, u64 save_id);
|
||||
static std::string GetFullPath(Core::System& system, VirtualDir dir, SaveDataSpaceId space,
|
||||
SaveDataType type, u64 title_id, u128 user_id, u64 save_id);
|
||||
|
||||
SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const;
|
||||
void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
|
||||
|
||||
@@ -970,14 +970,7 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
|
||||
Common::Input::VibrationError::None;
|
||||
}
|
||||
|
||||
bool EmulatedController::TestVibration(std::size_t device_index) {
|
||||
if (device_index >= output_devices.size()) {
|
||||
return false;
|
||||
}
|
||||
if (!output_devices[device_index]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
|
||||
const auto player_index = NpadIdTypeToIndex(npad_id_type);
|
||||
const auto& player = Settings::values.players.GetValue()[player_index];
|
||||
|
||||
@@ -985,31 +978,15 @@ bool EmulatedController::TestVibration(std::size_t device_index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Common::Input::VibrationStatus test_vibration = {
|
||||
.low_amplitude = 0.001f,
|
||||
.low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency,
|
||||
.high_amplitude = 0.001f,
|
||||
.high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency,
|
||||
.type = Common::Input::VibrationAmplificationType::Test,
|
||||
};
|
||||
if (device_index >= output_devices.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Common::Input::VibrationStatus zero_vibration = {
|
||||
.low_amplitude = DEFAULT_VIBRATION_VALUE.low_amplitude,
|
||||
.low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency,
|
||||
.high_amplitude = DEFAULT_VIBRATION_VALUE.high_amplitude,
|
||||
.high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency,
|
||||
.type = Common::Input::VibrationAmplificationType::Test,
|
||||
};
|
||||
if (!output_devices[device_index]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send a slight vibration to test for rumble support
|
||||
output_devices[device_index]->SetVibration(test_vibration);
|
||||
|
||||
// Wait for about 15ms to ensure the controller is ready for the stop command
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(15));
|
||||
|
||||
// Stop any vibration and return the result
|
||||
return output_devices[device_index]->SetVibration(zero_vibration) ==
|
||||
Common::Input::VibrationError::None;
|
||||
return output_devices[device_index]->IsVibrationEnabled();
|
||||
}
|
||||
|
||||
bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) {
|
||||
@@ -1048,6 +1025,7 @@ bool EmulatedController::HasNfc() const {
|
||||
case NpadStyleIndex::JoyconRight:
|
||||
case NpadStyleIndex::JoyconDual:
|
||||
case NpadStyleIndex::ProController:
|
||||
case NpadStyleIndex::Handheld:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
@@ -1158,27 +1136,27 @@ bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
|
||||
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
|
||||
switch (type) {
|
||||
case NpadStyleIndex::ProController:
|
||||
return supported_style_tag.fullkey;
|
||||
return supported_style_tag.fullkey.As<bool>();
|
||||
case NpadStyleIndex::Handheld:
|
||||
return supported_style_tag.handheld;
|
||||
return supported_style_tag.handheld.As<bool>();
|
||||
case NpadStyleIndex::JoyconDual:
|
||||
return supported_style_tag.joycon_dual;
|
||||
return supported_style_tag.joycon_dual.As<bool>();
|
||||
case NpadStyleIndex::JoyconLeft:
|
||||
return supported_style_tag.joycon_left;
|
||||
return supported_style_tag.joycon_left.As<bool>();
|
||||
case NpadStyleIndex::JoyconRight:
|
||||
return supported_style_tag.joycon_right;
|
||||
return supported_style_tag.joycon_right.As<bool>();
|
||||
case NpadStyleIndex::GameCube:
|
||||
return supported_style_tag.gamecube;
|
||||
return supported_style_tag.gamecube.As<bool>();
|
||||
case NpadStyleIndex::Pokeball:
|
||||
return supported_style_tag.palma;
|
||||
return supported_style_tag.palma.As<bool>();
|
||||
case NpadStyleIndex::NES:
|
||||
return supported_style_tag.lark;
|
||||
return supported_style_tag.lark.As<bool>();
|
||||
case NpadStyleIndex::SNES:
|
||||
return supported_style_tag.lucia;
|
||||
return supported_style_tag.lucia.As<bool>();
|
||||
case NpadStyleIndex::N64:
|
||||
return supported_style_tag.lagoon;
|
||||
return supported_style_tag.lagoon.As<bool>();
|
||||
case NpadStyleIndex::SegaGenesis:
|
||||
return supported_style_tag.lager;
|
||||
return supported_style_tag.lager.As<bool>();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -1234,12 +1212,6 @@ bool EmulatedController::IsConnected(bool get_temporary_value) const {
|
||||
return is_connected;
|
||||
}
|
||||
|
||||
bool EmulatedController::IsVibrationEnabled() const {
|
||||
const auto player_index = NpadIdTypeToIndex(npad_id_type);
|
||||
const auto& player = Settings::values.players.GetValue()[player_index];
|
||||
return player.vibration_enabled;
|
||||
}
|
||||
|
||||
NpadIdType EmulatedController::GetNpadIdType() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
return npad_id_type;
|
||||
|
||||
@@ -206,9 +206,6 @@ public:
|
||||
*/
|
||||
bool IsConnected(bool get_temporary_value = false) const;
|
||||
|
||||
/// Returns true if vibration is enabled
|
||||
bool IsVibrationEnabled() const;
|
||||
|
||||
/// Removes all callbacks created from input devices
|
||||
void UnloadInput();
|
||||
|
||||
@@ -339,7 +336,7 @@ public:
|
||||
* Sends a small vibration to the output device
|
||||
* @return true if SetVibration was successfull
|
||||
*/
|
||||
bool TestVibration(std::size_t device_index);
|
||||
bool IsVibrationEnabled(std::size_t device_index);
|
||||
|
||||
/**
|
||||
* Sets the desired data to be polled from a controller
|
||||
|
||||
@@ -86,13 +86,13 @@ public:
|
||||
u32 num_domain_objects{};
|
||||
const bool always_move_handles{
|
||||
(static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0};
|
||||
if (!ctx.Session()->IsDomain() || always_move_handles) {
|
||||
if (!ctx.GetManager()->IsDomain() || always_move_handles) {
|
||||
num_handles_to_move = num_objects_to_move;
|
||||
} else {
|
||||
num_domain_objects = num_objects_to_move;
|
||||
}
|
||||
|
||||
if (ctx.Session()->IsDomain()) {
|
||||
if (ctx.GetManager()->IsDomain()) {
|
||||
raw_data_size +=
|
||||
static_cast<u32>(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects);
|
||||
ctx.write_size += num_domain_objects;
|
||||
@@ -125,7 +125,7 @@ public:
|
||||
if (!ctx.IsTipc()) {
|
||||
AlignWithPadding();
|
||||
|
||||
if (ctx.Session()->IsDomain() && ctx.HasDomainMessageHeader()) {
|
||||
if (ctx.GetManager()->IsDomain() && ctx.HasDomainMessageHeader()) {
|
||||
IPC::DomainMessageHeader domain_header{};
|
||||
domain_header.num_objects = num_domain_objects;
|
||||
PushRaw(domain_header);
|
||||
@@ -145,18 +145,18 @@ public:
|
||||
|
||||
template <class T>
|
||||
void PushIpcInterface(std::shared_ptr<T> iface) {
|
||||
if (context->Session()->IsDomain()) {
|
||||
if (context->GetManager()->IsDomain()) {
|
||||
context->AddDomainObject(std::move(iface));
|
||||
} else {
|
||||
kernel.CurrentProcess()->GetResourceLimit()->Reserve(
|
||||
Kernel::LimitableResource::Sessions, 1);
|
||||
Kernel::LimitableResource::SessionCountMax, 1);
|
||||
|
||||
auto* session = Kernel::KSession::Create(kernel);
|
||||
session->Initialize(nullptr, iface->GetServiceName(),
|
||||
std::make_shared<Kernel::SessionRequestManager>(kernel));
|
||||
session->Initialize(nullptr, iface->GetServiceName());
|
||||
iface->RegisterSession(&session->GetServerSession(),
|
||||
std::make_shared<Kernel::SessionRequestManager>(kernel));
|
||||
|
||||
context->AddMoveObject(&session->GetClientSession());
|
||||
iface->ClientConnected(&session->GetServerSession());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,7 +386,7 @@ public:
|
||||
|
||||
template <class T>
|
||||
std::weak_ptr<T> PopIpcInterface() {
|
||||
ASSERT(context->Session()->IsDomain());
|
||||
ASSERT(context->GetManager()->IsDomain());
|
||||
ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
|
||||
return context->GetDomainHandler<T>(Pop<u32>() - 1);
|
||||
}
|
||||
@@ -405,7 +405,7 @@ inline s32 RequestParser::Pop() {
|
||||
}
|
||||
|
||||
// Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects.
|
||||
#if defined(__GNUC__)
|
||||
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wclass-memaccess"
|
||||
#endif
|
||||
@@ -416,7 +416,7 @@ void RequestParser::PopRaw(T& value) {
|
||||
std::memcpy(&value, cmdbuf + index, sizeof(T));
|
||||
index += (sizeof(T) + 3) / 4; // round up to word length
|
||||
}
|
||||
#if defined(__GNUC__)
|
||||
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
namespace Kernel::Board::Nintendo::Nx {
|
||||
|
||||
class KSystemControl {
|
||||
public:
|
||||
// This can be overridden as needed.
|
||||
static constexpr size_t SecureAppletMemorySize = 4 * 1024 * 1024; // 4_MB
|
||||
|
||||
public:
|
||||
class Init {
|
||||
public:
|
||||
|
||||
@@ -49,4 +49,26 @@ bool GlobalSchedulerContext::IsLocked() const {
|
||||
return scheduler_lock.IsLockedByCurrentThread();
|
||||
}
|
||||
|
||||
void GlobalSchedulerContext::RegisterDummyThreadForWakeup(KThread* thread) {
|
||||
ASSERT(IsLocked());
|
||||
|
||||
woken_dummy_threads.insert(thread);
|
||||
}
|
||||
|
||||
void GlobalSchedulerContext::UnregisterDummyThreadForWakeup(KThread* thread) {
|
||||
ASSERT(IsLocked());
|
||||
|
||||
woken_dummy_threads.erase(thread);
|
||||
}
|
||||
|
||||
void GlobalSchedulerContext::WakeupWaitingDummyThreads() {
|
||||
ASSERT(IsLocked());
|
||||
|
||||
for (auto* thread : woken_dummy_threads) {
|
||||
thread->DummyThreadEndWait();
|
||||
}
|
||||
|
||||
woken_dummy_threads.clear();
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
@@ -58,6 +59,10 @@ public:
|
||||
/// Returns true if the global scheduler lock is acquired
|
||||
bool IsLocked() const;
|
||||
|
||||
void UnregisterDummyThreadForWakeup(KThread* thread);
|
||||
void RegisterDummyThreadForWakeup(KThread* thread);
|
||||
void WakeupWaitingDummyThreads();
|
||||
|
||||
[[nodiscard]] LockType& SchedulerLock() {
|
||||
return scheduler_lock;
|
||||
}
|
||||
@@ -76,6 +81,9 @@ private:
|
||||
KSchedulerPriorityQueue priority_queue;
|
||||
LockType scheduler_lock;
|
||||
|
||||
/// Lists dummy threads pending wakeup on lock release
|
||||
std::set<KThread*> woken_dummy_threads;
|
||||
|
||||
/// Lists all thread ids that aren't deleted/etc.
|
||||
std::vector<KThread*> thread_list;
|
||||
std::mutex global_list_guard;
|
||||
|
||||
@@ -16,27 +16,39 @@
|
||||
#include "core/hle/kernel/k_auto_object.h"
|
||||
#include "core/hle/kernel/k_handle_table.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_server_port.h"
|
||||
#include "core/hle/kernel/k_server_session.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/service_thread.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_,
|
||||
ServiceThreadType thread_type)
|
||||
: kernel{kernel_} {
|
||||
if (thread_type == ServiceThreadType::CreateNew) {
|
||||
service_thread = kernel.CreateServiceThread(service_name_);
|
||||
} else {
|
||||
service_thread = kernel.GetDefaultServiceThread();
|
||||
}
|
||||
}
|
||||
: kernel{kernel_}, service_thread{thread_type == ServiceThreadType::CreateNew
|
||||
? kernel.CreateServiceThread(service_name_)
|
||||
: kernel.GetDefaultServiceThread()} {}
|
||||
|
||||
SessionRequestHandler::~SessionRequestHandler() {
|
||||
kernel.ReleaseServiceThread(service_thread);
|
||||
}
|
||||
|
||||
void SessionRequestHandler::AcceptSession(KServerPort* server_port) {
|
||||
auto* server_session = server_port->AcceptSession();
|
||||
ASSERT(server_session != nullptr);
|
||||
|
||||
RegisterSession(server_session, std::make_shared<SessionRequestManager>(kernel));
|
||||
}
|
||||
|
||||
void SessionRequestHandler::RegisterSession(KServerSession* server_session,
|
||||
std::shared_ptr<SessionRequestManager> manager) {
|
||||
manager->SetSessionHandler(shared_from_this());
|
||||
service_thread.RegisterServerSession(server_session, manager);
|
||||
server_session->Close();
|
||||
}
|
||||
|
||||
SessionRequestManager::SessionRequestManager(KernelCore& kernel_) : kernel{kernel_} {}
|
||||
|
||||
SessionRequestManager::~SessionRequestManager() = default;
|
||||
@@ -56,15 +68,77 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co
|
||||
}
|
||||
}
|
||||
|
||||
void SessionRequestHandler::ClientConnected(KServerSession* session) {
|
||||
session->ClientConnected(shared_from_this());
|
||||
Result SessionRequestManager::CompleteSyncRequest(KServerSession* server_session,
|
||||
HLERequestContext& context) {
|
||||
Result result = ResultSuccess;
|
||||
|
||||
// Ensure our server session is tracked globally.
|
||||
kernel.RegisterServerObject(session);
|
||||
// If the session has been converted to a domain, handle the domain request
|
||||
if (this->HasSessionRequestHandler(context)) {
|
||||
if (IsDomain() && context.HasDomainMessageHeader()) {
|
||||
result = HandleDomainSyncRequest(server_session, context);
|
||||
// If there is no domain header, the regular session handler is used
|
||||
} else if (this->HasSessionHandler()) {
|
||||
// If this manager has an associated HLE handler, forward the request to it.
|
||||
result = this->SessionHandler().HandleSyncRequest(*server_session, context);
|
||||
}
|
||||
} else {
|
||||
ASSERT_MSG(false, "Session handler is invalid, stubbing response!");
|
||||
IPC::ResponseBuilder rb(context, 2);
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
if (convert_to_domain) {
|
||||
ASSERT_MSG(!IsDomain(), "ServerSession is already a domain instance.");
|
||||
this->ConvertToDomain();
|
||||
convert_to_domain = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void SessionRequestHandler::ClientDisconnected(KServerSession* session) {
|
||||
session->ClientDisconnected();
|
||||
Result SessionRequestManager::HandleDomainSyncRequest(KServerSession* server_session,
|
||||
HLERequestContext& context) {
|
||||
if (!context.HasDomainMessageHeader()) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
// Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
|
||||
ASSERT(context.GetManager().get() == this);
|
||||
|
||||
// If there is a DomainMessageHeader, then this is CommandType "Request"
|
||||
const auto& domain_message_header = context.GetDomainMessageHeader();
|
||||
const u32 object_id{domain_message_header.object_id};
|
||||
switch (domain_message_header.command) {
|
||||
case IPC::DomainMessageHeader::CommandType::SendMessage:
|
||||
if (object_id > this->DomainHandlerCount()) {
|
||||
LOG_CRITICAL(IPC,
|
||||
"object_id {} is too big! This probably means a recent service call "
|
||||
"needed to return a new interface!",
|
||||
object_id);
|
||||
ASSERT(false);
|
||||
return ResultSuccess; // Ignore error if asserts are off
|
||||
}
|
||||
if (auto strong_ptr = this->DomainHandler(object_id - 1).lock()) {
|
||||
return strong_ptr->HandleSyncRequest(*server_session, context);
|
||||
} else {
|
||||
ASSERT(false);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
|
||||
LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id);
|
||||
|
||||
this->CloseDomainHandler(object_id - 1);
|
||||
|
||||
IPC::ResponseBuilder rb{context, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
return ResultSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_CRITICAL(IPC, "Unknown domain command={}", domain_message_header.command.Value());
|
||||
ASSERT(false);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_,
|
||||
@@ -126,7 +200,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
|
||||
// Padding to align to 16 bytes
|
||||
rp.AlignWithPadding();
|
||||
|
||||
if (Session()->IsDomain() &&
|
||||
if (GetManager()->IsDomain() &&
|
||||
((command_header->type == IPC::CommandType::Request ||
|
||||
command_header->type == IPC::CommandType::RequestWithContext) ||
|
||||
!incoming)) {
|
||||
@@ -135,7 +209,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
|
||||
if (incoming || domain_message_header) {
|
||||
domain_message_header = rp.PopRaw<IPC::DomainMessageHeader>();
|
||||
} else {
|
||||
if (Session()->IsDomain()) {
|
||||
if (GetManager()->IsDomain()) {
|
||||
LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!");
|
||||
}
|
||||
}
|
||||
@@ -228,12 +302,11 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_threa
|
||||
// Write the domain objects to the command buffer, these go after the raw untranslated data.
|
||||
// TODO(Subv): This completely ignores C buffers.
|
||||
|
||||
if (Session()->IsDomain()) {
|
||||
if (GetManager()->IsDomain()) {
|
||||
current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size());
|
||||
for (const auto& object : outgoing_domain_objects) {
|
||||
server_session->AppendDomainHandler(object);
|
||||
cmd_buf[current_offset++] =
|
||||
static_cast<u32_le>(server_session->NumDomainRequestHandlers());
|
||||
for (auto& object : outgoing_domain_objects) {
|
||||
GetManager()->AppendDomainHandler(std::move(object));
|
||||
cmd_buf[current_offset++] = static_cast<u32_le>(GetManager()->DomainHandlerCount());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,11 +45,13 @@ class KAutoObject;
|
||||
class KernelCore;
|
||||
class KEvent;
|
||||
class KHandleTable;
|
||||
class KServerPort;
|
||||
class KProcess;
|
||||
class KServerSession;
|
||||
class KThread;
|
||||
class KReadableEvent;
|
||||
class KSession;
|
||||
class SessionRequestManager;
|
||||
class ServiceThread;
|
||||
|
||||
enum class ThreadWakeupReason;
|
||||
@@ -76,27 +78,17 @@ public:
|
||||
virtual Result HandleSyncRequest(Kernel::KServerSession& session,
|
||||
Kernel::HLERequestContext& context) = 0;
|
||||
|
||||
/**
|
||||
* Signals that a client has just connected to this HLE handler and keeps the
|
||||
* associated ServerSession alive for the duration of the connection.
|
||||
* @param server_session Owning pointer to the ServerSession associated with the connection.
|
||||
*/
|
||||
void ClientConnected(KServerSession* session);
|
||||
void AcceptSession(KServerPort* server_port);
|
||||
void RegisterSession(KServerSession* server_session,
|
||||
std::shared_ptr<SessionRequestManager> manager);
|
||||
|
||||
/**
|
||||
* Signals that a client has just disconnected from this HLE handler and releases the
|
||||
* associated ServerSession.
|
||||
* @param server_session ServerSession associated with the connection.
|
||||
*/
|
||||
void ClientDisconnected(KServerSession* session);
|
||||
|
||||
std::weak_ptr<ServiceThread> GetServiceThread() const {
|
||||
ServiceThread& GetServiceThread() const {
|
||||
return service_thread;
|
||||
}
|
||||
|
||||
protected:
|
||||
KernelCore& kernel;
|
||||
std::weak_ptr<ServiceThread> service_thread;
|
||||
ServiceThread& service_thread;
|
||||
};
|
||||
|
||||
using SessionRequestHandlerWeakPtr = std::weak_ptr<SessionRequestHandler>;
|
||||
@@ -121,6 +113,10 @@ public:
|
||||
is_domain = true;
|
||||
}
|
||||
|
||||
void ConvertToDomainOnRequestEnd() {
|
||||
convert_to_domain = true;
|
||||
}
|
||||
|
||||
std::size_t DomainHandlerCount() const {
|
||||
return domain_handlers.size();
|
||||
}
|
||||
@@ -158,13 +154,17 @@ public:
|
||||
session_handler = std::move(handler);
|
||||
}
|
||||
|
||||
std::weak_ptr<ServiceThread> GetServiceThread() const {
|
||||
ServiceThread& GetServiceThread() const {
|
||||
return session_handler->GetServiceThread();
|
||||
}
|
||||
|
||||
bool HasSessionRequestHandler(const HLERequestContext& context) const;
|
||||
|
||||
Result HandleDomainSyncRequest(KServerSession* server_session, HLERequestContext& context);
|
||||
Result CompleteSyncRequest(KServerSession* server_session, HLERequestContext& context);
|
||||
|
||||
private:
|
||||
bool convert_to_domain{};
|
||||
bool is_domain{};
|
||||
SessionRequestHandlerPtr session_handler;
|
||||
std::vector<SessionRequestHandlerPtr> domain_handlers;
|
||||
@@ -295,7 +295,7 @@ public:
|
||||
*/
|
||||
template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>>
|
||||
std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const {
|
||||
if constexpr (Common::IsSTLContainer<T>) {
|
||||
if constexpr (Common::IsContiguousContainer<T>) {
|
||||
using ContiguousType = typename T::value_type;
|
||||
static_assert(std::is_trivially_copyable_v<ContiguousType>,
|
||||
"Container to WriteBuffer must contain trivially copyable objects");
|
||||
@@ -341,11 +341,11 @@ public:
|
||||
|
||||
template <typename T>
|
||||
std::shared_ptr<T> GetDomainHandler(std::size_t index) const {
|
||||
return std::static_pointer_cast<T>(manager.lock()->DomainHandler(index).lock());
|
||||
return std::static_pointer_cast<T>(GetManager()->DomainHandler(index).lock());
|
||||
}
|
||||
|
||||
void SetSessionRequestManager(std::weak_ptr<SessionRequestManager> manager_) {
|
||||
manager = std::move(manager_);
|
||||
manager = manager_;
|
||||
}
|
||||
|
||||
std::string Description() const;
|
||||
@@ -354,6 +354,10 @@ public:
|
||||
return *thread;
|
||||
}
|
||||
|
||||
std::shared_ptr<SessionRequestManager> GetManager() const {
|
||||
return manager.lock();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class IPC::ResponseBuilder;
|
||||
|
||||
@@ -387,7 +391,7 @@ private:
|
||||
u32 handles_offset{};
|
||||
u32 domain_offset{};
|
||||
|
||||
std::weak_ptr<SessionRequestManager> manager;
|
||||
std::weak_ptr<SessionRequestManager> manager{};
|
||||
|
||||
KernelCore& kernel;
|
||||
Core::Memory::Memory& memory;
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
#include "core/hardware_properties.h"
|
||||
#include "core/hle/kernel/init/init_slab_setup.h"
|
||||
#include "core/hle/kernel/k_code_memory.h"
|
||||
#include "core/hle/kernel/k_debug.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_event_info.h"
|
||||
#include "core/hle/kernel/k_memory_layout.h"
|
||||
#include "core/hle/kernel/k_memory_manager.h"
|
||||
#include "core/hle/kernel/k_page_buffer.h"
|
||||
@@ -18,9 +20,11 @@
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_resource_limit.h"
|
||||
#include "core/hle/kernel/k_session.h"
|
||||
#include "core/hle/kernel/k_session_request.h"
|
||||
#include "core/hle/kernel/k_shared_memory.h"
|
||||
#include "core/hle/kernel/k_shared_memory_info.h"
|
||||
#include "core/hle/kernel/k_system_control.h"
|
||||
#include "core/hle/kernel/k_system_resource.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_thread_local_page.h"
|
||||
#include "core/hle/kernel/k_transfer_memory.h"
|
||||
@@ -34,6 +38,7 @@ namespace Kernel::Init {
|
||||
HANDLER(KThread, (SLAB_COUNT(KThread)), ##__VA_ARGS__) \
|
||||
HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \
|
||||
HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \
|
||||
HANDLER(KSessionRequest, (SLAB_COUNT(KSession) * 2), ##__VA_ARGS__) \
|
||||
HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \
|
||||
HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \
|
||||
HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \
|
||||
@@ -42,7 +47,10 @@ namespace Kernel::Init {
|
||||
HANDLER(KThreadLocalPage, \
|
||||
(SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), \
|
||||
##__VA_ARGS__) \
|
||||
HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__)
|
||||
HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__) \
|
||||
HANDLER(KEventInfo, (SLAB_COUNT(KThread) + SLAB_COUNT(KDebug)), ##__VA_ARGS__) \
|
||||
HANDLER(KDebug, (SLAB_COUNT(KDebug)), ##__VA_ARGS__) \
|
||||
HANDLER(KSecureSystemResource, (SLAB_COUNT(KProcess)), ##__VA_ARGS__)
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -71,8 +79,20 @@ constexpr size_t SlabCountKResourceLimit = 5;
|
||||
constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES;
|
||||
constexpr size_t SlabCountKIoPool = 1;
|
||||
constexpr size_t SlabCountKIoRegion = 6;
|
||||
constexpr size_t SlabcountKSessionRequestMappings = 40;
|
||||
|
||||
constexpr size_t SlabCountExtraKThread = 160;
|
||||
constexpr size_t SlabCountExtraKThread = (1024 + 256 + 256) - SlabCountKThread;
|
||||
|
||||
namespace test {
|
||||
|
||||
static_assert(KernelPageBufferHeapSize ==
|
||||
2 * PageSize + (SlabCountKProcess + SlabCountKThread +
|
||||
(SlabCountKProcess + SlabCountKThread) / 8) *
|
||||
PageSize);
|
||||
static_assert(KernelPageBufferAdditionalSize ==
|
||||
(SlabCountExtraKThread + (SlabCountExtraKThread / 8)) * PageSize);
|
||||
|
||||
} // namespace test
|
||||
|
||||
/// Helper function to translate from the slab virtual address to the reserved location in physical
|
||||
/// memory.
|
||||
@@ -94,8 +114,8 @@ VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAd
|
||||
// TODO(bunnei): Fix this once we support the kernel virtual memory layout.
|
||||
|
||||
if (size > 0) {
|
||||
void* backing_kernel_memory{
|
||||
system.DeviceMemory().GetPointer(TranslateSlabAddrToPhysical(memory_layout, start))};
|
||||
void* backing_kernel_memory{system.DeviceMemory().GetPointer<void>(
|
||||
TranslateSlabAddrToPhysical(memory_layout, start))};
|
||||
|
||||
const KMemoryRegion* region = memory_layout.FindVirtual(start + size - 1);
|
||||
ASSERT(region != nullptr);
|
||||
@@ -107,7 +127,7 @@ VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAd
|
||||
}
|
||||
|
||||
size_t CalculateSlabHeapGapSize() {
|
||||
constexpr size_t KernelSlabHeapGapSize = 2_MiB - 296_KiB;
|
||||
constexpr size_t KernelSlabHeapGapSize = 2_MiB - 320_KiB;
|
||||
static_assert(KernelSlabHeapGapSize <= KernelSlabHeapGapsSizeMax);
|
||||
return KernelSlabHeapGapSize;
|
||||
}
|
||||
@@ -132,6 +152,7 @@ KSlabResourceCounts KSlabResourceCounts::CreateDefault() {
|
||||
.num_KDebug = SlabCountKDebug,
|
||||
.num_KIoPool = SlabCountKIoPool,
|
||||
.num_KIoRegion = SlabCountKIoRegion,
|
||||
.num_KSessionRequestMappings = SlabcountKSessionRequestMappings,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -162,29 +183,6 @@ size_t CalculateTotalSlabHeapSize(const KernelCore& kernel) {
|
||||
return size;
|
||||
}
|
||||
|
||||
void InitializeKPageBufferSlabHeap(Core::System& system) {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
const auto& counts = kernel.SlabResourceCounts();
|
||||
const size_t num_pages =
|
||||
counts.num_KProcess + counts.num_KThread + (counts.num_KProcess + counts.num_KThread) / 8;
|
||||
const size_t slab_size = num_pages * PageSize;
|
||||
|
||||
// Reserve memory from the system resource limit.
|
||||
ASSERT(kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemory, slab_size));
|
||||
|
||||
// Allocate memory for the slab.
|
||||
constexpr auto AllocateOption = KMemoryManager::EncodeOption(
|
||||
KMemoryManager::Pool::System, KMemoryManager::Direction::FromFront);
|
||||
const PAddr slab_address =
|
||||
kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, AllocateOption);
|
||||
ASSERT(slab_address != 0);
|
||||
|
||||
// Initialize the slabheap.
|
||||
KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer(slab_address),
|
||||
slab_size);
|
||||
}
|
||||
|
||||
void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
@@ -245,6 +243,7 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
|
||||
// If we somehow get an invalid type, abort.
|
||||
default:
|
||||
ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
// If we've hit the end of a gap, free it.
|
||||
@@ -256,3 +255,30 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
|
||||
}
|
||||
|
||||
} // namespace Kernel::Init
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
void KPageBufferSlabHeap::Initialize(Core::System& system) {
|
||||
auto& kernel = system.Kernel();
|
||||
const auto& counts = kernel.SlabResourceCounts();
|
||||
const size_t num_pages =
|
||||
counts.num_KProcess + counts.num_KThread + (counts.num_KProcess + counts.num_KThread) / 8;
|
||||
const size_t slab_size = num_pages * PageSize;
|
||||
|
||||
// Reserve memory from the system resource limit.
|
||||
ASSERT(
|
||||
kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemoryMax, slab_size));
|
||||
|
||||
// Allocate memory for the slab.
|
||||
constexpr auto AllocateOption = KMemoryManager::EncodeOption(
|
||||
KMemoryManager::Pool::System, KMemoryManager::Direction::FromFront);
|
||||
const PAddr slab_address =
|
||||
kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, AllocateOption);
|
||||
ASSERT(slab_address != 0);
|
||||
|
||||
// Initialize the slabheap.
|
||||
KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer<void>(slab_address),
|
||||
slab_size);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -33,11 +33,11 @@ struct KSlabResourceCounts {
|
||||
size_t num_KDebug;
|
||||
size_t num_KIoPool;
|
||||
size_t num_KIoRegion;
|
||||
size_t num_KSessionRequestMappings;
|
||||
};
|
||||
|
||||
void InitializeSlabResourceCounts(KernelCore& kernel);
|
||||
size_t CalculateTotalSlabHeapSize(const KernelCore& kernel);
|
||||
void InitializeKPageBufferSlabHeap(Core::System& system);
|
||||
void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout);
|
||||
|
||||
} // namespace Kernel::Init
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "core/hle/kernel/k_session.h"
|
||||
#include "core/hle/kernel/k_shared_memory.h"
|
||||
#include "core/hle/kernel/k_synchronization_object.h"
|
||||
#include "core/hle/kernel/k_system_resource.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_transfer_memory.h"
|
||||
|
||||
@@ -119,4 +120,6 @@ static_assert(std::is_final_v<KTransferMemory> && std::is_base_of_v<KAutoObject,
|
||||
// static_assert(std::is_final_v<KCodeMemory> &&
|
||||
// std::is_base_of_v<KAutoObject, KCodeMemory>);
|
||||
|
||||
static_assert(std::is_base_of_v<KAutoObject, KSystemResource>);
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace Kernel {
|
||||
|
||||
class KAutoObject;
|
||||
|
||||
class KSystemResource;
|
||||
|
||||
class KClassTokenGenerator {
|
||||
public:
|
||||
using TokenBaseType = u16;
|
||||
@@ -58,7 +60,7 @@ private:
|
||||
if constexpr (std::is_same<T, KAutoObject>::value) {
|
||||
static_assert(T::ObjectType == ObjectType::KAutoObject);
|
||||
return 0;
|
||||
} else if constexpr (!std::is_final<T>::value) {
|
||||
} else if constexpr (!std::is_final<T>::value && !std::same_as<T, KSystemResource>) {
|
||||
static_assert(ObjectType::BaseClassesStart <= T::ObjectType &&
|
||||
T::ObjectType < ObjectType::BaseClassesEnd);
|
||||
constexpr auto ClassIndex = static_cast<TokenBaseType>(T::ObjectType) -
|
||||
@@ -108,6 +110,8 @@ public:
|
||||
KSessionRequest,
|
||||
KCodeMemory,
|
||||
|
||||
KSystemResource,
|
||||
|
||||
// NOTE: True order for these has not been determined yet.
|
||||
KAlpha,
|
||||
KBeta,
|
||||
|
||||
@@ -58,11 +58,10 @@ bool KClientPort::IsSignaled() const {
|
||||
return num_sessions < max_sessions;
|
||||
}
|
||||
|
||||
Result KClientPort::CreateSession(KClientSession** out,
|
||||
std::shared_ptr<SessionRequestManager> session_manager) {
|
||||
Result KClientPort::CreateSession(KClientSession** out) {
|
||||
// Reserve a new session from the resource limit.
|
||||
KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(),
|
||||
LimitableResource::Sessions);
|
||||
LimitableResource::SessionCountMax);
|
||||
R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
|
||||
|
||||
// Update the session counts.
|
||||
@@ -104,7 +103,7 @@ Result KClientPort::CreateSession(KClientSession** out,
|
||||
}
|
||||
|
||||
// Initialize the session.
|
||||
session->Initialize(this, parent->GetName(), session_manager);
|
||||
session->Initialize(this, parent->GetName());
|
||||
|
||||
// Commit the session reservation.
|
||||
session_reservation.Commit();
|
||||
|
||||
@@ -52,8 +52,7 @@ public:
|
||||
void Destroy() override;
|
||||
bool IsSignaled() const override;
|
||||
|
||||
Result CreateSession(KClientSession** out,
|
||||
std::shared_ptr<SessionRequestManager> session_manager = nullptr);
|
||||
Result CreateSession(KClientSession** out);
|
||||
|
||||
private:
|
||||
std::atomic<s32> num_sessions{};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/k_client_session.h"
|
||||
#include "core/hle/kernel/k_server_session.h"
|
||||
@@ -10,6 +11,8 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
static constexpr u32 MessageBufferSize = 0x100;
|
||||
|
||||
KClientSession::KClientSession(KernelCore& kernel_)
|
||||
: KAutoObjectWithSlabHeapAndContainer{kernel_} {}
|
||||
KClientSession::~KClientSession() = default;
|
||||
@@ -22,8 +25,16 @@ void KClientSession::Destroy() {
|
||||
void KClientSession::OnServerClosed() {}
|
||||
|
||||
Result KClientSession::SendSyncRequest() {
|
||||
// Signal the server session that new data is available
|
||||
return parent->GetServerSession().OnRequest();
|
||||
// Create a session request.
|
||||
KSessionRequest* request = KSessionRequest::Create(kernel);
|
||||
R_UNLESS(request != nullptr, ResultOutOfResource);
|
||||
SCOPE_EXIT({ request->Close(); });
|
||||
|
||||
// Initialize the request.
|
||||
request->Initialize(nullptr, GetCurrentThread(kernel).GetTLSAddress(), MessageBufferSize);
|
||||
|
||||
// Send the request.
|
||||
return parent->GetServerSession().OnRequest(request);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -34,7 +34,7 @@ Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, si
|
||||
|
||||
// Clear the memory.
|
||||
for (const auto& block : m_page_group.Nodes()) {
|
||||
std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
|
||||
std::memset(device_memory.GetPointer<void>(block.GetAddress()), 0xFF, block.GetSize());
|
||||
}
|
||||
|
||||
// Set remaining tracking members.
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/kernel/k_auto_object.h"
|
||||
#include "core/hle/kernel/slab_helpers.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KDebug final : public KAutoObjectWithSlabHeapAndContainer<KDebug, KAutoObjectWithList> {
|
||||
KERNEL_AUTOOBJECT_TRAITS(KDebug, KAutoObject);
|
||||
|
||||
public:
|
||||
explicit KDebug(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {}
|
||||
|
||||
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -0,0 +1,169 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/k_page_bitmap.h"
|
||||
#include "core/hle/kernel/k_spin_lock.h"
|
||||
#include "core/hle/kernel/memory_types.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KDynamicPageManager {
|
||||
public:
|
||||
class PageBuffer {
|
||||
private:
|
||||
u8 m_buffer[PageSize];
|
||||
};
|
||||
static_assert(sizeof(PageBuffer) == PageSize);
|
||||
|
||||
public:
|
||||
KDynamicPageManager() = default;
|
||||
|
||||
template <typename T>
|
||||
T* GetPointer(VAddr addr) {
|
||||
return reinterpret_cast<T*>(m_backing_memory.data() + (addr - m_address));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T* GetPointer(VAddr addr) const {
|
||||
return reinterpret_cast<T*>(m_backing_memory.data() + (addr - m_address));
|
||||
}
|
||||
|
||||
Result Initialize(VAddr memory, size_t size, size_t align) {
|
||||
// We need to have positive size.
|
||||
R_UNLESS(size > 0, ResultOutOfMemory);
|
||||
m_backing_memory.resize(size);
|
||||
|
||||
// Set addresses.
|
||||
m_address = memory;
|
||||
m_aligned_address = Common::AlignDown(memory, align);
|
||||
|
||||
// Calculate extents.
|
||||
const size_t managed_size = m_address + size - m_aligned_address;
|
||||
const size_t overhead_size = Common::AlignUp(
|
||||
KPageBitmap::CalculateManagementOverheadSize(managed_size / sizeof(PageBuffer)),
|
||||
sizeof(PageBuffer));
|
||||
R_UNLESS(overhead_size < size, ResultOutOfMemory);
|
||||
|
||||
// Set tracking fields.
|
||||
m_size = Common::AlignDown(size - overhead_size, sizeof(PageBuffer));
|
||||
m_count = m_size / sizeof(PageBuffer);
|
||||
|
||||
// Clear the management region.
|
||||
u64* management_ptr = GetPointer<u64>(m_address + size - overhead_size);
|
||||
std::memset(management_ptr, 0, overhead_size);
|
||||
|
||||
// Initialize the bitmap.
|
||||
const size_t allocatable_region_size =
|
||||
(m_address + size - overhead_size) - m_aligned_address;
|
||||
ASSERT(allocatable_region_size >= sizeof(PageBuffer));
|
||||
|
||||
m_page_bitmap.Initialize(management_ptr, allocatable_region_size / sizeof(PageBuffer));
|
||||
|
||||
// Free the pages to the bitmap.
|
||||
for (size_t i = 0; i < m_count; i++) {
|
||||
// Ensure the freed page is all-zero.
|
||||
std::memset(GetPointer<PageBuffer>(m_address) + i, 0, PageSize);
|
||||
|
||||
// Set the bit for the free page.
|
||||
m_page_bitmap.SetBit((m_address + (i * sizeof(PageBuffer)) - m_aligned_address) /
|
||||
sizeof(PageBuffer));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
VAddr GetAddress() const {
|
||||
return m_address;
|
||||
}
|
||||
size_t GetSize() const {
|
||||
return m_size;
|
||||
}
|
||||
size_t GetUsed() const {
|
||||
return m_used;
|
||||
}
|
||||
size_t GetPeak() const {
|
||||
return m_peak;
|
||||
}
|
||||
size_t GetCount() const {
|
||||
return m_count;
|
||||
}
|
||||
|
||||
PageBuffer* Allocate() {
|
||||
// Take the lock.
|
||||
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Find a random free block.
|
||||
s64 soffset = m_page_bitmap.FindFreeBlock(true);
|
||||
if (soffset < 0) [[unlikely]] {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const size_t offset = static_cast<size_t>(soffset);
|
||||
|
||||
// Update our tracking.
|
||||
m_page_bitmap.ClearBit(offset);
|
||||
m_peak = std::max(m_peak, (++m_used));
|
||||
|
||||
return GetPointer<PageBuffer>(m_aligned_address) + offset;
|
||||
}
|
||||
|
||||
PageBuffer* Allocate(size_t count) {
|
||||
// Take the lock.
|
||||
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Find a random free block.
|
||||
s64 soffset = m_page_bitmap.FindFreeRange(count);
|
||||
if (soffset < 0) [[likely]] {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const size_t offset = static_cast<size_t>(soffset);
|
||||
|
||||
// Update our tracking.
|
||||
m_page_bitmap.ClearRange(offset, count);
|
||||
m_used += count;
|
||||
m_peak = std::max(m_peak, m_used);
|
||||
|
||||
return GetPointer<PageBuffer>(m_aligned_address) + offset;
|
||||
}
|
||||
|
||||
void Free(PageBuffer* pb) {
|
||||
// Ensure all pages in the heap are zero.
|
||||
std::memset(pb, 0, PageSize);
|
||||
|
||||
// Take the lock.
|
||||
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Set the bit for the free page.
|
||||
size_t offset = (reinterpret_cast<uintptr_t>(pb) - m_aligned_address) / sizeof(PageBuffer);
|
||||
m_page_bitmap.SetBit(offset);
|
||||
|
||||
// Decrement our used count.
|
||||
--m_used;
|
||||
}
|
||||
|
||||
private:
|
||||
KSpinLock m_lock;
|
||||
KPageBitmap m_page_bitmap;
|
||||
size_t m_used{};
|
||||
size_t m_peak{};
|
||||
size_t m_count{};
|
||||
VAddr m_address{};
|
||||
VAddr m_aligned_address{};
|
||||
size_t m_size{};
|
||||
|
||||
// TODO(bunnei): Back by host memory until we emulate kernel virtual address space.
|
||||
std::vector<u8> m_backing_memory;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||