Compare commits

...

119 Commits

Author SHA1 Message Date
liushuyu
dd72e4dce4 CI: fix CI on Linux 2021-12-13 22:31:19 -07:00
liushuyu
a2d73eaa10 video_core/codecs: skip decoders that use hw frames ...
... this would resolve some edge-cases where multiple devices are
present and ffmpeg is unable to auto-supply the hw surfaces
2021-12-13 22:29:19 -07:00
liushuyu
37f1c76613 CI: fix MinGW installation step 2021-12-13 17:48:05 -07:00
liushuyu
f91b6fbbcb ffmpeg: move the whole tree into externals/ffmpeg/ffmpeg ...
* this resolves the todo items in the CMakeLists.txt
* a version requirement check for ffmpeg is added to catch issues early
* for future-proof reasons, nasm/yasm is now only required when build on
  x86/AMD64 systems
2021-12-13 17:48:05 -07:00
liushuyu
ccc0a1e621 cmake: refactor ffmpeg searching and handling logic on Linux 2021-12-13 17:48:05 -07:00
bunnei
7276aaf907 Merge pull request #7576 from lioncash/tasenum
tas_input: Minor cleanup
2021-12-13 14:47:24 -08:00
Lioncash
54ca48e8b7 tas_input: Avoid minor copies in Read/WriteCommandButtons()
We don't need to copy the whole pair
2021-12-13 11:45:19 -05:00
Lioncash
734fb180bb tas_input: Remove unnecessary semicolon
Resolves a -Wextra-semi warning
2021-12-13 11:45:19 -05:00
Lioncash
ddda6ae776 tas_input: Execute clear() even if empty
clear() when empty is simply a no-op, so we can get rid of the check
here and let the stdlib do it for us.
2021-12-13 11:45:18 -05:00
Lioncash
db9320e754 tas_input: Remove unnecessary includes
Gets rid of indirect includes and includes only what the interface
needs.
2021-12-13 11:45:18 -05:00
Lioncash
26ef76213c tas_input: std::move strings into vector
While we're in the same area, we can also avoid performing std::stoi in
a loop when it only needs to be performed once.
2021-12-13 11:45:15 -05:00
Lioncash
a515ede2af tas_input: Use istringstream over stringstream
This is only using the input facilities, so we don't need to use the
fully-fleged stringstream.
2021-12-13 10:57:53 -05:00
Lioncash
6be730bdcd tas_input: Use u8string_view instead of u8string
Same behavior, but without the potential for extra allocations.
2021-12-13 10:54:43 -05:00
Lioncash
37a8e2a67e tas_input: Remove unused std::smatch variable
This also means we can get rid of the dependency on <regex>
2021-12-13 10:50:24 -05:00
Lioncash
d52ad96ce3 tas_input: Amend -Wdocumentation warnings
Parameters shouldn't have the colon by their name.
2021-12-13 10:49:11 -05:00
Lioncash
c126b0718c tas_input: Make TasAxes enum an enum class
Prevents these values from potentially clashing with anything in other
headers.
2021-12-13 10:41:32 -05:00
Mai M
3592628302 Merge pull request #7574 from v1993/patch-1
Remove erroneous #pragma once
2021-12-13 08:50:08 -05:00
Valeri
04301e1a8a Remove erroneous #pragma once 2021-12-13 16:49:01 +03:00
Mai M
4e2fb9e51a Merge pull request #7554 from Tachi107/build-remove-unicorn
build: remove remaining bits of Unicorn
2021-12-13 08:24:47 -05:00
bunnei
a2f842ce54 Merge pull request #7527 from Tachi107/cubeb-result_of
build: update cubeb and remove the result_of comment
2021-12-12 22:44:05 -08:00
bunnei
280c779898 Merge pull request #7462 from bunnei/kernel-improve-scheduling
Kernel: Improve threading & scheduling V3
2021-12-12 22:43:25 -08:00
Andrea Pappacoda
eec9aace60 build: remove remaining bits of Unicorn
Unicorn has been removed in fc6db97a09
2021-12-10 12:25:04 +01:00
Morph
429320aee8 Merge pull request #7495 from FernandoS27/text-blit-fix-again
Texture Cache: Fix mismatching image/views on blits
2021-12-09 05:26:21 -05:00
bunnei
46366c6dca Merge pull request #7519 from itsmeft24/master
kernel: svc: Implement ProcessMemory and CodeMemory SVCs
2021-12-09 00:29:09 -08:00
bunnei
25298d1c02 Merge pull request #7545 from Morph1984/qt-deprecated-warn
profiler: Use QWheelEvent position().toPoint()
2021-12-08 22:01:35 -08:00
Morph
9ba812485a profiler: Use QWheelEvent position().toPoint()
QWheelEvent::pos() is deprecated. Make use of position().toPoint() instead.
2021-12-08 15:25:46 -05:00
bunnei
7dd2764f2a Merge pull request #7544 from Morph1984/r16g16
renderer_vulkan: Add R16G16_UINT
2021-12-08 11:47:05 -08:00
Morph
47a724780f renderer_vulkan: Add R16G16_UINT
- Used by Immortals Fenyx Rising
2021-12-08 10:55:11 -05:00
bunnei
5f7e73c74a Merge pull request #7525 from german77/notifa
service/notif: Add notif:a and stub ListAlarmSettings, Initialize
2021-12-07 23:31:44 -08:00
bunnei
f6e8e61e3e Merge pull request #7521 from german77/dual_single_joycons
service/hid: Implement SetNpadJoyAssignmentMode
2021-12-07 21:03:42 -08:00
bunnei
815189eaf3 Merge pull request #7488 from vonchenplus/support_multiple_videos_playing
Support multiple videos playing
2021-12-07 18:38:14 -08:00
Mai M
5b2cb22a04 Merge pull request #7506 from heinermann/focus_crash
Fixed #7502
2021-12-07 18:28:16 -05:00
Mai M
edbde7a220 Merge pull request #7522 from ameerj/shader-recompiler-filenames
shader_recompiler/backend: Minor organization and refactoring to reduce compile time overhead
2021-12-07 18:27:50 -05:00
Mai M
00f65af8b2 Merge pull request #7526 from Void48/patch-1
(README) changed the color of Discord badge to use the new color
2021-12-07 18:20:52 -05:00
itsmeft24
e05c86aa3c Update k_code_memory.h 2021-12-07 16:58:23 -05:00
itsmeft24
d197246880 make KCodeMemory::GetSourceAddress const
Co-authored-by: Mai M. <mathew1800@gmail.com>
2021-12-07 07:58:33 -05:00
bunnei
204d198d16 Merge pull request #7531 from Morph1984/zm-msvc
CMakeLists: Specify /Zm200 when compiling in MSVC
2021-12-06 19:52:27 -08:00
bunnei
257d3c9ecf hle: kernel k_scheduler: EnableScheduling: Remove redundant GetCurrentThreadPointer calls. 2021-12-06 16:39:18 -08:00
bunnei
9a9e7dd78b hle: kernel k_process: Remove unnecessary .at usage with thread pinning methods. 2021-12-06 16:39:18 -08:00
bunnei
834c25f4d9 hle: kernel: Remove unnecessary virtual specifier on NotifyAvailable. 2021-12-06 16:39:18 -08:00
bunnei
a63af9860b hle: kernel: Remove unnecessary virtual specifier on EndWait. 2021-12-06 16:39:18 -08:00
bunnei
3f8eb44e7d hle: kernel: k_light_condition_variable: Revert unnecessary license comment changes. 2021-12-06 16:39:18 -08:00
bunnei
2e8d737a96 hle: kernel: k_condition_variable: Revert unnecessary style changes. 2021-12-06 16:39:18 -08:00
bunnei
0d1bdfc1d4 hle: kernel: Remove unnecessary virtual specifier on CancelWait. 2021-12-06 16:39:18 -08:00
bunnei
d7f6d516ce hle: kernel: service_thread: Force stop threads on destruction. 2021-12-06 16:39:18 -08:00
bunnei
e596fac6ee hle: kernel: k_light_lock: Implement CancelWait.
- Fixes a crash in Megadimension Neptunia VII.
2021-12-06 16:39:18 -08:00
bunnei
efb5de1c5f hle: kernel: service_thread: Use std::jthread.
- Fixes a potential deadlock on service thread shutdown.
2021-12-06 16:39:18 -08:00
bunnei
a2384a18fa hle: kernel: k_thread: Skip reschedule on DisableDispatch with SC. 2021-12-06 16:39:18 -08:00
bunnei
42697527ba hle: kernel: k_thread: Rename sleeping_queue -> wait_queue. 2021-12-06 16:39:18 -08:00
bunnei
e3d156ab0e hle: kernel: svc: Fix deadlock that can occur with single core. 2021-12-06 16:39:18 -08:00
bunnei
0d9afdedc4 hle: kernel: k_thread: Treat dummy threads as a special type. 2021-12-06 16:39:18 -08:00
FernandoS27
894ed14ebc hle: kernel: fix timing on thread preemption 2021-12-06 16:39:18 -08:00
FernandoS27
3c2a451f47 hle: kernel: fix scheduling ops from HLE host thread. 2021-12-06 16:39:17 -08:00
bunnei
abbea575cf hle: kernel: Add a flag for indicating that the kernel is currently shutting down. 2021-12-06 16:39:17 -08:00
bunnei
2c49a65d2b hle: kernel: KSynchronizationObject: Fix variable shadowing. 2021-12-06 16:39:17 -08:00
bunnei
8f4ff06c4c hle: kernel: Cleanup to match coding style. 2021-12-06 16:39:17 -08:00
bunnei
316a2dd22a hle: kernel: KProcess: Improvements for thread pinning. 2021-12-06 16:39:17 -08:00
bunnei
4c74761155 hle: kernel: KThreadQueue: Remove deprecated code. 2021-12-06 16:39:17 -08:00
bunnei
f3d6e31e78 hle: kernel: KConditionVariable: Various updates & simplifications. 2021-12-06 16:39:17 -08:00
bunnei
f62c7091a2 hle: kernel: KThread: Migrate to updated KThreadQueue (part 2). 2021-12-06 16:39:17 -08:00
bunnei
b0671c7cfa hle: kernel: KThread: Migrate to updated KThreadQueue (part 1). 2021-12-06 16:39:17 -08:00
bunnei
beb55cb90e hle: kernel: KConditionVariable: Migrate to updated KThreadQueue. 2021-12-06 16:39:17 -08:00
bunnei
e942d97540 hle: kernel: KServerSession: Migrate to updated KThreadQueue. 2021-12-06 16:39:17 -08:00
bunnei
5dff28290f hle: kernel: KLightConditionVariable: Migrate to updated KThreadQueue. 2021-12-06 16:39:17 -08:00
bunnei
423acf53b7 hle: kernel: KLightLock: Migrate to updated KThreadQueue. 2021-12-06 16:39:17 -08:00
bunnei
15c721b909 hle: kernel: KAddressArbiter: Migrate to updated KThreadQueue. 2021-12-06 16:39:17 -08:00
bunnei
2f89456041 hle: kernel: KThread: Remove tracking of sync object from threads. 2021-12-06 16:39:17 -08:00
bunnei
bc1399204b hle: kernel: Update KThreadQueue and migrate KSynchronizationObject. 2021-12-06 16:39:17 -08:00
bunnei
3dc803a430 core: hle: kernel: Disable dispatch count tracking on single core.
- This would have limited value, and would be a mess to handle properly.
2021-12-06 16:39:17 -08:00
bunnei
d14b8fc747 core: hle: kernel: k_thread: Mark KScopedDisableDispatch as nodiscard. 2021-12-06 16:39:17 -08:00
bunnei
08c63d5c75 core: cpu_manager: Use invalid core_id on init and simplify shutdown. 2021-12-06 16:39:17 -08:00
bunnei
07690572f7 core: hle: kernel: k_auto_object: Add GetName method.
- Useful purely for debugging.
2021-12-06 16:39:16 -08:00
bunnei
3239442de6 core: hle: kernel: DisableDispatch on suspend threads. 2021-12-06 16:39:16 -08:00
bunnei
284015dfd7 core: hle: kernel: k_scheduler: Improve DisableScheduling and EnableScheduling. 2021-12-06 16:39:16 -08:00
bunnei
d604edfedf core: cpu_manager: Use KScopedDisableDispatch. 2021-12-06 16:39:16 -08:00
bunnei
178584e56f core: hle: kernel: Use CurrentPhysicalCoreIndex as appropriate. 2021-12-06 16:39:16 -08:00
bunnei
629f9274ac core: hle: kernel: k_scheduler: Remove unnecessary MakeCurrentProcess. 2021-12-06 16:39:16 -08:00
bunnei
13c82d042f core: hle: kernel: k_scheduler: Improve ScheduleImpl. 2021-12-06 16:39:16 -08:00
bunnei
f412d2027a core: hle: kernel: k_scheduler: Improve Unload. 2021-12-06 16:39:16 -08:00
bunnei
f13fce3953 core: hle: kernel: k_process: DisableDispatch on main thread. 2021-12-06 16:39:16 -08:00
bunnei
4c18a207a4 core: hle: kernel: k_handle_table: Use KScopedDisableDispatch as necessary. 2021-12-06 16:39:16 -08:00
bunnei
04daefa488 core: hle: kernel: k_thread: Add KScopedDisableDispatch. 2021-12-06 16:39:16 -08:00
bunnei
3bd5d4b6f8 core: hle: kernel: Ensure idle threads are closed before destroying scheduler. 2021-12-06 16:39:16 -08:00
bunnei
669a2d2c67 core: hle: kernel: Reflect non-emulated threads as core 3. 2021-12-06 16:39:16 -08:00
Morph
c61857286d CMakeLists: Specify /Zm200 when compiling in MSVC
This increases the memory heap size for constructing precompiled headers to 2x the default.
2021-12-06 19:31:26 -05:00
bunnei
77b11b927c Merge pull request #7529 from german77/sdl2.0.18
input_common: Update SDL to 2.0.18
2021-12-06 15:09:30 -08:00
bunnei
bafee97589 Merge pull request #7524 from german77/hid_stub
service/hid: Stub SetNpadCaptureButtonAssignment and ClearNpadCaptureButtonAssignment
2021-12-06 13:06:18 -08:00
Narr the Reg
133504b74b input_common: Update SDL to 2.0.18 2021-12-06 14:48:36 -06:00
Andrea Pappacoda
f4587c596f build: update cubeb and remove the result_of comment
Cubeb doesn't use result_of anymore, it has been dropped in commit
mozilla/cubeb@75d9d125ee
2021-12-06 18:56:43 +01:00
german77
ac1bfe228f service/notif: Add notif:a and stub ListAlarmSettings,Initialize
Used by ring fit adventure 1.2.0
2021-12-06 10:36:37 -06:00
itsmeft24
8ed2748820 fix formatting 2021-12-06 11:02:33 -05:00
itsmeft24
e10903cab9 move private members below public members 2021-12-06 10:37:13 -05:00
itsmeft24
4bdacdedc1 fix formatting 2021-12-06 10:37:09 -05:00
itsmeft24
32854a2992 fix formatting
Co-authored-by: Mai M. <mathew1800@gmail.com>
2021-12-06 07:58:28 -05:00
Void48
6d25fe6c9a Update README.md 2021-12-06 11:39:55 +01:00
german77
189741b521 service/hid: Stub SetNpadCaptureButtonAssignment and ClearNpadCaptureButtonAssignment
Used by ring fit adventure 1.2.0
2021-12-05 22:44:31 -06:00
Morph
df47cfc32c Merge pull request #7523 from jam1garner/support-all-subsdks
Add support for subsdk8 and subsdk9
2021-12-05 23:28:41 -05:00
jam1garner
29559930e9 loader: Support loading subsdk{8,9} 2021-12-05 23:07:50 -05:00
itsmeft24
14c03b9748 fix formatting 2021-12-05 19:00:29 -05:00
itsmeft24
8254f238b9 Remove unnecessary includes 2021-12-05 18:49:40 -05:00
ameerj
7105204a7e emit_spirv: Reduce emit_spirv.h include overhead
emit_spirv.h is included in video_core, which was propagating further includes that video_core did not depend on.
2021-12-05 18:11:19 -05:00
ameerj
1e1f7b3234 glasm: Move implemented instructions from not_implemented.cpp 2021-12-05 18:11:19 -05:00
ameerj
f32b2bcd20 shader_recompiler: Adjust emit_context includes 2021-12-05 18:11:19 -05:00
Morph
975931e8fa Merge pull request #7520 from ameerj/missing-copyright
general: Add missing copyright notices
2021-12-05 18:08:08 -05:00
german77
d6ae9c68f8 service/hid: Implement SetNpadJoyAssignmentMode 2021-12-05 16:18:23 -06:00
itsmeft24
b7d80c127f Add copyright notice 2021-12-05 16:49:52 -05:00
ameerj
5286a7bc4c shader_recompiler: Rename backend emit_context files 2021-12-05 16:33:44 -05:00
ameerj
41aec2773f general: Add missing copyright notices 2021-12-05 16:18:53 -05:00
itsmeft24
36350d3f78 Add KCodeMemory to CMakeLists.txt 2021-12-05 15:56:44 -05:00
Morph
fae07919af Merge pull request #7518 from german77/is_npad_valid
core/hid: Add missing controller type
2021-12-05 15:11:14 -05:00
itsmeft24
8aef8f39d8 kernel: svc: Implement Map/UnmapProcessMemory and Create/ControlCodeMemory
Used by Skyline modding framework
2021-12-05 15:04:08 -05:00
german77
7347cdb651 core/hid: Add missing controller type 2021-12-05 13:57:59 -06:00
Fernando S
f1f91ad468 Merge pull request #7494 from Morph1984/no-time-to-wait
native_clock: Wait for less time in EstimateRDTSCFrequency
2021-12-05 18:56:38 +01:00
Adam Heinermann
7220056974 Fixed #7502 2021-12-04 23:22:39 -08:00
bunnei
60e923046e Merge pull request #7503 from german77/is_npad_valid
core/hid: Ensure only valid npad are connected
2021-12-04 23:08:43 -08:00
german77
7fe455e42e core/hid: Ensure only valid npad are connected 2021-12-04 23:20:18 -06:00
Feng Chen
5462485cc3 Address feedback 2021-12-05 00:06:14 +08:00
Morph
f919498f8f native_clock: Wait for less time in EstimateRDTSCFrequency
In my testing, waiting for 200ms provided the same level of precision as the previous implementation when estimating the RDTSC frequency.
This significantly improves the yuzu executable launch times since we reduced the wait time from 3 seconds to 200 milliseconds.
2021-12-03 19:55:59 -05:00
Feng Chen
2c47f8aa18 Support multiple videos playing 2021-12-02 12:48:42 +08:00
155 changed files with 2648 additions and 1384 deletions

View File

@@ -41,12 +41,11 @@ for i in package/*.exe; do
done
pip3 install pefile
python3 .ci/scripts/windows/scan_dll.py package/*.exe "package/"
python3 .ci/scripts/windows/scan_dll.py package/imageformats/*.dll "package/"
python3 .ci/scripts/windows/scan_dll.py package/*.exe package/imageformats/*.dll "package/"
# copy FFmpeg libraries
EXTERNALS_PATH="$(pwd)/build/externals"
FFMPEG_DLL_PATH="$(find ${EXTERNALS_PATH} -maxdepth 1 -type d | grep ffmpeg)/bin"
FFMPEG_DLL_PATH="$(find "${EXTERNALS_PATH}" -maxdepth 1 -type d | grep 'ffmpeg-')/bin"
find ${FFMPEG_DLL_PATH} -type f -regex ".*\.dll" -exec cp -v {} package/ ';'
# copy libraries from yuzu.exe path

6
.gitmodules vendored
View File

@@ -34,12 +34,12 @@
[submodule "opus"]
path = externals/opus/opus
url = https://github.com/xiph/opus.git
[submodule "ffmpeg"]
path = externals/ffmpeg
url = https://git.ffmpeg.org/ffmpeg.git
[submodule "SDL"]
path = externals/SDL
url = https://github.com/libsdl-org/SDL.git
[submodule "externals/cpp-httplib"]
path = externals/cpp-httplib
url = https://github.com/yhirose/cpp-httplib.git
[submodule "externals/ffmpeg/ffmpeg"]
path = externals/ffmpeg/ffmpeg
url = https://git.ffmpeg.org/ffmpeg.git

View File

@@ -131,7 +131,7 @@ add_definitions(-DBOOST_ASIO_DISABLE_CONCEPTS)
if (MSVC)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:/std:c++latest>)
# cubeb and boost still make use of deprecated result_of.
# boost still makes use of deprecated result_of.
add_definitions(-D_HAS_DEPRECATED_RESULT_OF)
else()
set(CMAKE_CXX_STANDARD 20)
@@ -370,7 +370,7 @@ if (ENABLE_SDL2)
if (YUZU_USE_BUNDLED_SDL2)
# Detect toolchain and platform
if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64)
set(SDL2_VER "SDL2-2.0.16")
set(SDL2_VER "SDL2-2.0.18")
else()
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.")
endif()
@@ -390,7 +390,7 @@ if (ENABLE_SDL2)
elseif (YUZU_USE_EXTERNAL_SDL2)
message(STATUS "Using SDL2 from externals.")
else()
find_package(SDL2 2.0.16 REQUIRED)
find_package(SDL2 2.0.18 REQUIRED)
# Some installations don't set SDL2_LIBRARIES
if("${SDL2_LIBRARIES}" STREQUAL "")
@@ -514,7 +514,7 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
endif()
if (NOT YUZU_USE_BUNDLED_FFMPEG)
# Use system installed FFmpeg
find_package(FFmpeg QUIET COMPONENTS ${FFmpeg_COMPONENTS})
find_package(FFmpeg 4.3 QUIET COMPONENTS ${FFmpeg_COMPONENTS})
if (FFmpeg_FOUND)
# Overwrite aggregate defines from FFmpeg module to avoid over-linking libraries.
@@ -527,225 +527,11 @@ if (NOT YUZU_USE_BUNDLED_FFMPEG)
set(FFmpeg_INCLUDE_DIR ${FFmpeg_INCLUDE_DIR} ${FFmpeg_INCLUDE_${COMPONENT}} CACHE PATH "Path to FFmpeg headers" FORCE)
endforeach()
else()
message(WARNING "FFmpeg not found, falling back to externals")
message(WARNING "FFmpeg not found or too old, falling back to externals")
set(YUZU_USE_BUNDLED_FFMPEG ON)
endif()
endif()
if (YUZU_USE_BUNDLED_FFMPEG)
if (NOT WIN32)
# TODO(lat9nq): Move this to externals/ffmpeg/CMakeLists.txt (and move externals/ffmpeg to
# externals/ffmpeg/ffmpeg)
# Build FFmpeg from externals
message(STATUS "Using FFmpeg from externals")
# FFmpeg has source that requires one of nasm or yasm to assemble it.
# REQUIRED throws an error if not found here during configuration rather than during compilation.
find_program(ASSEMBLER NAMES nasm yasm)
if ("${ASSEMBLER}" STREQUAL "ASSEMBLER-NOTFOUND")
message(FATAL_ERROR "One of either `nasm` or `yasm` not found but is required.")
endif()
find_program(AUTOCONF autoconf)
if ("${AUTOCONF}" STREQUAL "AUTOCONF-NOTFOUND")
message(FATAL_ERROR "Required program `autoconf` not found.")
endif()
set(FFmpeg_PREFIX ${PROJECT_SOURCE_DIR}/externals/ffmpeg)
set(FFmpeg_BUILD_DIR ${PROJECT_BINARY_DIR}/externals/ffmpeg)
set(FFmpeg_MAKEFILE ${FFmpeg_BUILD_DIR}/Makefile)
make_directory(${FFmpeg_BUILD_DIR})
# Read version string from external
file(READ ${FFmpeg_PREFIX}/RELEASE FFmpeg_VERSION)
set(FFmpeg_FOUND NO)
if (NOT FFmpeg_VERSION STREQUAL "")
set(FFmpeg_FOUND YES)
endif()
unset(FFmpeg_LIBRARIES CACHE)
foreach(COMPONENT ${FFmpeg_COMPONENTS})
set(FFmpeg_${COMPONENT}_PREFIX "${FFmpeg_BUILD_DIR}/lib${COMPONENT}")
set(FFmpeg_${COMPONENT}_LIB_NAME "lib${COMPONENT}.a")
set(FFmpeg_${COMPONENT}_LIBRARY "${FFmpeg_${COMPONENT}_PREFIX}/${FFmpeg_${COMPONENT}_LIB_NAME}")
set(FFmpeg_LIBRARIES
${FFmpeg_LIBRARIES}
${FFmpeg_${COMPONENT}_LIBRARY}
CACHE PATH "Paths to FFmpeg libraries" FORCE)
endforeach()
Include(FindPkgConfig REQUIRED)
pkg_check_modules(LIBVA libva)
pkg_check_modules(CUDA cuda)
pkg_check_modules(FFNVCODEC ffnvcodec)
pkg_check_modules(VDPAU vdpau)
set(FFmpeg_HWACCEL_LIBRARIES)
set(FFmpeg_HWACCEL_FLAGS)
set(FFmpeg_HWACCEL_INCLUDE_DIRS)
set(FFmpeg_HWACCEL_LDFLAGS)
if(LIBVA_FOUND)
pkg_check_modules(LIBDRM libdrm REQUIRED)
find_package(X11 REQUIRED)
pkg_check_modules(LIBVA-DRM libva-drm REQUIRED)
pkg_check_modules(LIBVA-X11 libva-x11 REQUIRED)
list(APPEND FFmpeg_HWACCEL_LIBRARIES
${LIBDRM_LIBRARIES}
${X11_LIBRARIES}
${LIBVA-DRM_LIBRARIES}
${LIBVA-X11_LIBRARIES}
${LIBVA_LIBRARIES})
set(FFmpeg_HWACCEL_FLAGS
--enable-hwaccel=h264_vaapi
--enable-hwaccel=vp8_vaapi
--enable-hwaccel=vp9_vaapi
--enable-libdrm)
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
${LIBDRM_INCLUDE_DIRS}
${X11_INCLUDE_DIRS}
${LIBVA-DRM_INCLUDE_DIRS}
${LIBVA-X11_INCLUDE_DIRS}
${LIBVA_INCLUDE_DIRS}
)
message(STATUS "VA-API found")
else()
set(FFmpeg_HWACCEL_FLAGS --disable-vaapi)
endif()
if (FFNVCODEC_FOUND AND CUDA_FOUND)
list(APPEND FFmpeg_HWACCEL_FLAGS
--enable-cuvid
--enable-ffnvcodec
--enable-nvdec
--enable-hwaccel=h264_nvdec
--enable-hwaccel=vp8_nvdec
--enable-hwaccel=vp9_nvdec
--extra-cflags=-I${CUDA_INCLUDE_DIRS}
)
list(APPEND FFmpeg_HWACCEL_LIBRARIES
${FFNVCODEC_LIBRARIES}
${CUDA_LIBRARIES}
)
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
${FFNVCODEC_INCLUDE_DIRS}
${CUDA_INCLUDE_DIRS}
)
list(APPEND FFmpeg_HWACCEL_LDFLAGS
${FFNVCODEC_LDFLAGS}
${CUDA_LDFLAGS}
)
message(STATUS "ffnvcodec libraries version ${FFNVCODEC_VERSION} found")
endif()
if (VDPAU_FOUND)
list(APPEND FFmpeg_HWACCEL_FLAGS
--enable-vdpau
--enable-hwaccel=h264_vdpau
--enable-hwaccel=vp9_vdpau
)
list(APPEND FFmpeg_HWACCEL_LIBRARIES ${VDPAU_LIBRARIES})
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${VDPAU_INCLUDE_DIRS})
list(APPEND FFmpeg_HWACCEL_LDFLAGS ${VDPAU_LDFLAGS})
message(STATUS "vdpau libraries version ${VDPAU_VERSION} found")
else()
list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vdpau)
endif()
# `configure` parameters builds only exactly what yuzu needs from FFmpeg
# `--disable-vdpau` is needed to avoid linking issues
add_custom_command(
OUTPUT
${FFmpeg_MAKEFILE}
COMMAND
/bin/bash ${FFmpeg_PREFIX}/configure
--disable-avdevice
--disable-avfilter
--disable-avformat
--disable-doc
--disable-everything
--disable-ffmpeg
--disable-ffprobe
--disable-network
--disable-postproc
--disable-swresample
--enable-decoder=h264
--enable-decoder=vp8
--enable-decoder=vp9
--cc="${CMAKE_C_COMPILER}"
--cxx="${CMAKE_CXX_COMPILER}"
${FFmpeg_HWACCEL_FLAGS}
WORKING_DIRECTORY
${FFmpeg_BUILD_DIR}
)
unset(FFmpeg_HWACCEL_FLAGS)
# Workaround for Ubuntu 18.04's older version of make not being able to call make as a child
# with context of the jobserver. Also helps ninja users.
execute_process(
COMMAND
nproc
OUTPUT_VARIABLE
SYSTEM_THREADS)
set(FFmpeg_BUILD_LIBRARIES ${FFmpeg_LIBRARIES})
add_custom_command(
OUTPUT
${FFmpeg_BUILD_LIBRARIES}
COMMAND
make -j${SYSTEM_THREADS}
WORKING_DIRECTORY
${FFmpeg_BUILD_DIR}
)
set(FFmpeg_INCLUDE_DIR
"${FFmpeg_PREFIX};${FFmpeg_BUILD_DIR};${FFmpeg_HWACCEL_INCLUDE_DIRS}"
CACHE PATH "Path to FFmpeg headers" FORCE)
set(FFmpeg_LDFLAGS
"${FFmpeg_HWACCEL_LDFLAGS}"
CACHE STRING "FFmpeg linker flags" FORCE)
# ALL makes this custom target build every time
# but it won't actually build if the DEPENDS parameter is up to date
add_custom_target(ffmpeg-configure ALL DEPENDS ${FFmpeg_MAKEFILE})
add_custom_target(ffmpeg-build ALL DEPENDS ${FFmpeg_BUILD_LIBRARIES} ffmpeg-configure)
link_libraries(${FFmpeg_LIBVA_LIBRARIES})
set(FFmpeg_LIBRARIES ${FFmpeg_BUILD_LIBRARIES} ${FFmpeg_HWACCEL_LIBRARIES}
CACHE PATH "Paths to FFmpeg libraries" FORCE)
unset(FFmpeg_BUILD_LIBRARIES)
unset(FFmpeg_HWACCEL_FLAGS)
unset(FFmpeg_HWACCEL_INCLUDE_DIRS)
unset(FFmpeg_HWACCEL_LDFLAGS)
unset(FFmpeg_HWACCEL_LIBRARIES)
if (FFmpeg_FOUND)
message(STATUS "Found FFmpeg version ${FFmpeg_VERSION}")
else()
message(FATAL_ERROR "FFmpeg not found")
endif()
else() # WIN32
# Use yuzu FFmpeg binaries
set(FFmpeg_EXT_NAME "ffmpeg-4.4")
set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}")
download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "")
set(FFmpeg_FOUND YES)
set(FFmpeg_INCLUDE_DIR "${FFmpeg_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE)
set(FFmpeg_LIBRARY_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg library directory" FORCE)
set(FFmpeg_LDFLAGS "" CACHE STRING "FFmpeg linker flags" FORCE)
set(FFmpeg_DLL_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg dll's" FORCE)
set(FFmpeg_LIBRARIES
${FFmpeg_LIBRARY_DIR}/swscale.lib
${FFmpeg_LIBRARY_DIR}/avcodec.lib
${FFmpeg_LIBRARY_DIR}/avutil.lib
CACHE PATH "Paths to FFmpeg libraries" FORCE)
endif()
endif()
unset(FFmpeg_COMPONENTS)
# Prefer the -pthread flag on Linux.
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)

View File

@@ -17,7 +17,7 @@ It is written in C++ with portability in mind, and we actively maintain builds f
alt="Azure Mainline CI Build Status">
</a>
<a href="https://discord.com/invite/u77vRWY">
<img src="https://img.shields.io/discord/398318088170242053?color=%237289DA&label=yuzu&logo=discord&logoColor=white"
<img src="https://img.shields.io/discord/398318088170242053?color=5865F2&label=yuzu&logo=discord&logoColor=white"
alt="Discord">
</a>
</p>

View File

@@ -44,10 +44,6 @@ target_include_directories(mbedtls PUBLIC ./mbedtls/include)
add_library(microprofile INTERFACE)
target_include_directories(microprofile INTERFACE ./microprofile)
# Unicorn
add_library(unicorn-headers INTERFACE)
target_include_directories(unicorn-headers INTERFACE ./unicorn/include)
# libusb
if (NOT LIBUSB_FOUND OR YUZU_USE_BUNDLED_LIBUSB)
add_subdirectory(libusb)
@@ -125,3 +121,8 @@ if (NOT opus_FOUND)
message(STATUS "opus 1.3 or newer not found, falling back to externals")
add_subdirectory(opus EXCLUDE_FROM_ALL)
endif()
# FFMpeg
if (YUZU_USE_BUNDLED_FFMPEG)
add_subdirectory(ffmpeg)
endif()

2
externals/SDL vendored

1
externals/ffmpeg vendored

Submodule externals/ffmpeg deleted from 79e8d17024

209
externals/ffmpeg/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,209 @@
if (NOT WIN32)
# Build FFmpeg from externals
message(STATUS "Using FFmpeg from externals")
if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86_64|amd64)")
# FFmpeg has source that requires one of nasm or yasm to assemble it.
# REQUIRED throws an error if not found here during configuration rather than during compilation.
find_program(ASSEMBLER NAMES nasm yasm)
if ("${ASSEMBLER}" STREQUAL "ASSEMBLER-NOTFOUND")
message(FATAL_ERROR "One of either `nasm` or `yasm` not found but is required.")
endif()
endif()
find_program(AUTOCONF autoconf)
if ("${AUTOCONF}" STREQUAL "AUTOCONF-NOTFOUND")
message(FATAL_ERROR "Required program `autoconf` not found.")
endif()
set(FFmpeg_PREFIX ${PROJECT_SOURCE_DIR}/externals/ffmpeg/ffmpeg)
set(FFmpeg_BUILD_DIR ${PROJECT_BINARY_DIR}/externals/ffmpeg-build)
set(FFmpeg_MAKEFILE ${FFmpeg_BUILD_DIR}/Makefile)
make_directory(${FFmpeg_BUILD_DIR})
# Read version string from external
file(READ ${FFmpeg_PREFIX}/RELEASE FFmpeg_VERSION)
set(FFmpeg_FOUND NO)
if (NOT FFmpeg_VERSION STREQUAL "")
set(FFmpeg_FOUND YES)
endif()
unset(FFmpeg_LIBRARIES CACHE)
foreach(COMPONENT ${FFmpeg_COMPONENTS})
set(FFmpeg_${COMPONENT}_PREFIX "${FFmpeg_BUILD_DIR}/lib${COMPONENT}")
set(FFmpeg_${COMPONENT}_LIB_NAME "lib${COMPONENT}.a")
set(FFmpeg_${COMPONENT}_LIBRARY "${FFmpeg_${COMPONENT}_PREFIX}/${FFmpeg_${COMPONENT}_LIB_NAME}")
set(FFmpeg_LIBRARIES
${FFmpeg_LIBRARIES}
${FFmpeg_${COMPONENT}_LIBRARY}
CACHE PATH "Paths to FFmpeg libraries" FORCE)
endforeach()
Include(FindPkgConfig REQUIRED)
pkg_check_modules(LIBVA libva)
pkg_check_modules(CUDA cuda)
pkg_check_modules(FFNVCODEC ffnvcodec)
pkg_check_modules(VDPAU vdpau)
set(FFmpeg_HWACCEL_LIBRARIES)
set(FFmpeg_HWACCEL_FLAGS)
set(FFmpeg_HWACCEL_INCLUDE_DIRS)
set(FFmpeg_HWACCEL_LDFLAGS)
if(LIBVA_FOUND)
pkg_check_modules(LIBDRM libdrm REQUIRED)
find_package(X11 REQUIRED)
pkg_check_modules(LIBVA-DRM libva-drm REQUIRED)
pkg_check_modules(LIBVA-X11 libva-x11 REQUIRED)
list(APPEND FFmpeg_HWACCEL_LIBRARIES
${LIBDRM_LIBRARIES}
${X11_LIBRARIES}
${LIBVA-DRM_LIBRARIES}
${LIBVA-X11_LIBRARIES}
${LIBVA_LIBRARIES})
set(FFmpeg_HWACCEL_FLAGS
--enable-hwaccel=h264_vaapi
--enable-hwaccel=vp8_vaapi
--enable-hwaccel=vp9_vaapi
--enable-libdrm)
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
${LIBDRM_INCLUDE_DIRS}
${X11_INCLUDE_DIRS}
${LIBVA-DRM_INCLUDE_DIRS}
${LIBVA-X11_INCLUDE_DIRS}
${LIBVA_INCLUDE_DIRS}
)
message(STATUS "VA-API found")
else()
set(FFmpeg_HWACCEL_FLAGS --disable-vaapi)
endif()
if (FFNVCODEC_FOUND)
list(APPEND FFmpeg_HWACCEL_FLAGS
--enable-cuvid
--enable-ffnvcodec
--enable-nvdec
--enable-hwaccel=h264_nvdec
--enable-hwaccel=vp8_nvdec
--enable-hwaccel=vp9_nvdec
)
list(APPEND FFmpeg_HWACCEL_LIBRARIES ${FFNVCODEC_LIBRARIES})
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${FFNVCODEC_INCLUDE_DIRS})
list(APPEND FFmpeg_HWACCEL_LDFLAGS ${FFNVCODEC_LDFLAGS})
message(STATUS "ffnvcodec libraries version ${FFNVCODEC_VERSION} found")
# ffnvenc could load CUDA libraries at the runtime using dlopen/dlsym or LoadLibrary/GetProcAddress
# here we handle the hard-linking senario where CUDA is linked during compilation
if (CUDA_FOUND)
list(APPEND FFmpeg_HWACCEL_FLAGS --extra-cflags=-I${CUDA_INCLUDE_DIRS})
list(APPEND FFmpeg_HWACCEL_LIBRARIES ${CUDA_LIBRARIES})
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${CUDA_INCLUDE_DIRS})
list(APPEND FFmpeg_HWACCEL_LDFLAGS ${CUDA_LDFLAGS})
message(STATUS "CUDA libraries found, hard-linking will be performed")
endif(CUDA_FOUND)
endif()
if (VDPAU_FOUND)
list(APPEND FFmpeg_HWACCEL_FLAGS
--enable-vdpau
--enable-hwaccel=h264_vdpau
--enable-hwaccel=vp9_vdpau
)
list(APPEND FFmpeg_HWACCEL_LIBRARIES ${VDPAU_LIBRARIES})
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${VDPAU_INCLUDE_DIRS})
list(APPEND FFmpeg_HWACCEL_LDFLAGS ${VDPAU_LDFLAGS})
message(STATUS "vdpau libraries version ${VDPAU_VERSION} found")
else()
list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vdpau)
endif()
# `configure` parameters builds only exactly what yuzu needs from FFmpeg
# `--disable-vdpau` is needed to avoid linking issues
add_custom_command(
OUTPUT
${FFmpeg_MAKEFILE}
COMMAND
/bin/bash ${FFmpeg_PREFIX}/configure
--disable-avdevice
--disable-avfilter
--disable-avformat
--disable-doc
--disable-everything
--disable-ffmpeg
--disable-ffprobe
--disable-network
--disable-postproc
--disable-swresample
--enable-decoder=h264
--enable-decoder=vp8
--enable-decoder=vp9
--cc="${CMAKE_C_COMPILER}"
--cxx="${CMAKE_CXX_COMPILER}"
${FFmpeg_HWACCEL_FLAGS}
WORKING_DIRECTORY
${FFmpeg_BUILD_DIR}
)
unset(FFmpeg_HWACCEL_FLAGS)
# Workaround for Ubuntu 18.04's older version of make not being able to call make as a child
# with context of the jobserver. Also helps ninja users.
execute_process(
COMMAND
nproc
OUTPUT_VARIABLE
SYSTEM_THREADS)
set(FFmpeg_BUILD_LIBRARIES ${FFmpeg_LIBRARIES})
add_custom_command(
OUTPUT
${FFmpeg_BUILD_LIBRARIES}
COMMAND
make -j${SYSTEM_THREADS}
WORKING_DIRECTORY
${FFmpeg_BUILD_DIR}
)
set(FFmpeg_INCLUDE_DIR
"${FFmpeg_PREFIX};${FFmpeg_BUILD_DIR};${FFmpeg_HWACCEL_INCLUDE_DIRS}"
CACHE PATH "Path to FFmpeg headers" FORCE)
set(FFmpeg_LDFLAGS
"${FFmpeg_HWACCEL_LDFLAGS}"
CACHE STRING "FFmpeg linker flags" FORCE)
# ALL makes this custom target build every time
# but it won't actually build if the DEPENDS parameter is up to date
add_custom_target(ffmpeg-configure ALL DEPENDS ${FFmpeg_MAKEFILE})
add_custom_target(ffmpeg-build ALL DEPENDS ${FFmpeg_BUILD_LIBRARIES} ffmpeg-configure)
link_libraries(${FFmpeg_LIBVA_LIBRARIES})
set(FFmpeg_LIBRARIES ${FFmpeg_BUILD_LIBRARIES} ${FFmpeg_HWACCEL_LIBRARIES}
CACHE PATH "Paths to FFmpeg libraries" FORCE)
unset(FFmpeg_BUILD_LIBRARIES)
unset(FFmpeg_HWACCEL_FLAGS)
unset(FFmpeg_HWACCEL_INCLUDE_DIRS)
unset(FFmpeg_HWACCEL_LDFLAGS)
unset(FFmpeg_HWACCEL_LIBRARIES)
if (FFmpeg_FOUND)
message(STATUS "Found FFmpeg version ${FFmpeg_VERSION}")
else()
message(FATAL_ERROR "FFmpeg not found")
endif()
else(WIN32)
# Use yuzu FFmpeg binaries
set(FFmpeg_EXT_NAME "ffmpeg-4.4")
set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}")
download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "")
set(FFmpeg_FOUND YES)
set(FFmpeg_INCLUDE_DIR "${FFmpeg_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE)
set(FFmpeg_LIBRARY_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg library directory" FORCE)
set(FFmpeg_LDFLAGS "" CACHE STRING "FFmpeg linker flags" FORCE)
set(FFmpeg_DLL_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg dll's" FORCE)
set(FFmpeg_LIBRARIES
${FFmpeg_LIBRARY_DIR}/swscale.lib
${FFmpeg_LIBRARY_DIR}/avcodec.lib
${FFmpeg_LIBRARY_DIR}/avutil.lib
CACHE PATH "Paths to FFmpeg libraries" FORCE)
endif(WIN32)
unset(FFmpeg_COMPONENTS)

1
externals/ffmpeg/ffmpeg vendored Submodule

Submodule externals/ffmpeg/ffmpeg added at dc91b913b6

View File

@@ -1,18 +0,0 @@
# Exports:
# LIBUNICORN_FOUND
# LIBUNICORN_INCLUDE_DIR
# LIBUNICORN_LIBRARY
find_path(LIBUNICORN_INCLUDE_DIR
unicorn/unicorn.h
HINTS $ENV{UNICORNDIR}
PATH_SUFFIXES include)
find_library(LIBUNICORN_LIBRARY
NAMES unicorn
HINTS $ENV{UNICORNDIR})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(unicorn DEFAULT_MSG
LIBUNICORN_LIBRARY LIBUNICORN_INCLUDE_DIR)
mark_as_advanced(LIBUNICORN_INCLUDE_DIR LIBUNICORN_LIBRARY)

View File

@@ -24,6 +24,7 @@ if (MSVC)
# /W3 - Level 3 warnings
# /MP - Multi-threaded compilation
# /Zi - Output debugging information
# /Zm - Specifies the precompiled header memory allocation limit
# /Zo - Enhanced debug info for optimized builds
# /permissive- - Enables stricter C++ standards conformance checks
# /EHsc - C++-only exception handling semantics
@@ -36,6 +37,7 @@ if (MSVC)
add_compile_options(
/MP
/Zi
/Zm200
/Zo
/permissive-
/EHsc

View File

@@ -1,3 +1,7 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include "audio_core/delay_line.h"

View File

@@ -1,3 +1,7 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"

View File

@@ -1,3 +1,7 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#ifdef _WIN32
#include <iterator>

View File

@@ -114,6 +114,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Service, NGCT) \
SUB(Service, NIFM) \
SUB(Service, NIM) \
SUB(Service, NOTIF) \
SUB(Service, NPNS) \
SUB(Service, NS) \
SUB(Service, NVDRV) \

View File

@@ -82,6 +82,7 @@ enum class Class : u8 {
Service_NGCT, ///< The NGCT (No Good Content for Terra) service
Service_NIFM, ///< The NIFM (Network interface) service
Service_NIM, ///< The NIM service
Service_NOTIF, ///< The NOTIF (Notification) service
Service_NPNS, ///< The NPNS service
Service_NS, ///< The NS services
Service_NVDRV, ///< The NVDRV (Nvidia driver) service

View File

@@ -15,26 +15,26 @@
namespace Common {
u64 EstimateRDTSCFrequency() {
const auto milli_10 = std::chrono::milliseconds{10};
// get current time
// Discard the first result measuring the rdtsc.
_mm_mfence();
const u64 tscStart = __rdtsc();
const auto startTime = std::chrono::steady_clock::now();
// wait roughly 3 seconds
while (true) {
auto milli = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - startTime);
if (milli.count() >= 3000)
break;
std::this_thread::sleep_for(milli_10);
}
const auto endTime = std::chrono::steady_clock::now();
__rdtsc();
std::this_thread::sleep_for(std::chrono::milliseconds{1});
_mm_mfence();
const u64 tscEnd = __rdtsc();
// calculate difference
const u64 timer_diff =
std::chrono::duration_cast<std::chrono::nanoseconds>(endTime - startTime).count();
const u64 tsc_diff = tscEnd - tscStart;
__rdtsc();
// Get the current time.
const auto start_time = std::chrono::steady_clock::now();
_mm_mfence();
const u64 tsc_start = __rdtsc();
// Wait for 200 milliseconds.
std::this_thread::sleep_for(std::chrono::milliseconds{200});
const auto end_time = std::chrono::steady_clock::now();
_mm_mfence();
const u64 tsc_end = __rdtsc();
// Calculate differences.
const u64 timer_diff = static_cast<u64>(
std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
const u64 tsc_diff = tsc_end - tsc_start;
const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
return tsc_freq;
}

View File

@@ -179,12 +179,15 @@ add_library(core STATIC
hle/kernel/k_client_port.h
hle/kernel/k_client_session.cpp
hle/kernel/k_client_session.h
hle/kernel/k_code_memory.cpp
hle/kernel/k_code_memory.h
hle/kernel/k_condition_variable.cpp
hle/kernel/k_condition_variable.h
hle/kernel/k_event.cpp
hle/kernel/k_event.h
hle/kernel/k_handle_table.cpp
hle/kernel/k_handle_table.h
hle/kernel/k_light_condition_variable.cpp
hle/kernel/k_light_condition_variable.h
hle/kernel/k_light_lock.cpp
hle/kernel/k_light_lock.h
@@ -237,6 +240,7 @@ add_library(core STATIC
hle/kernel/k_system_control.h
hle/kernel/k_thread.cpp
hle/kernel/k_thread.h
hle/kernel/k_thread_queue.cpp
hle/kernel/k_thread_queue.h
hle/kernel/k_trace.h
hle/kernel/k_transfer_memory.cpp
@@ -408,6 +412,8 @@ add_library(core STATIC
hle/service/glue/glue.h
hle/service/glue/glue_manager.cpp
hle/service/glue/glue_manager.h
hle/service/glue/notif.cpp
hle/service/glue/notif.h
hle/service/grc/grc.cpp
hle/service/grc/grc.h
hle/service/hid/hid.cpp

View File

@@ -521,12 +521,6 @@ const ARM_Interface& System::CurrentArmInterface() const {
return impl->kernel.CurrentPhysicalCore().ArmInterface();
}
std::size_t System::CurrentCoreIndex() const {
std::size_t core = impl->kernel.GetCurrentHostThreadID();
ASSERT(core < Core::Hardware::NUM_CPU_CORES);
return core;
}
Kernel::PhysicalCore& System::CurrentPhysicalCore() {
return impl->kernel.CurrentPhysicalCore();
}

View File

@@ -208,9 +208,6 @@ public:
/// Gets an ARM interface to the CPU core that is currently running
[[nodiscard]] const ARM_Interface& CurrentArmInterface() const;
/// Gets the index of the currently running CPU core
[[nodiscard]] std::size_t CurrentCoreIndex() const;
/// Gets the physical core for the CPU core that is currently running
[[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore();

View File

@@ -117,17 +117,18 @@ void CpuManager::MultiCoreRunGuestLoop() {
physical_core = &kernel.CurrentPhysicalCore();
}
system.ExitDynarmicProfile();
physical_core->ArmInterface().ClearExclusiveState();
kernel.CurrentScheduler()->RescheduleCurrentCore();
{
Kernel::KScopedDisableDispatch dd(kernel);
physical_core->ArmInterface().ClearExclusiveState();
}
}
}
void CpuManager::MultiCoreRunIdleThread() {
auto& kernel = system.Kernel();
while (true) {
auto& physical_core = kernel.CurrentPhysicalCore();
physical_core.Idle();
kernel.CurrentScheduler()->RescheduleCurrentCore();
Kernel::KScopedDisableDispatch dd(kernel);
kernel.CurrentPhysicalCore().Idle();
}
}
@@ -135,12 +136,12 @@ void CpuManager::MultiCoreRunSuspendThread() {
auto& kernel = system.Kernel();
kernel.CurrentScheduler()->OnThreadStart();
while (true) {
auto core = kernel.GetCurrentHostThreadID();
auto core = kernel.CurrentPhysicalCoreIndex();
auto& scheduler = *kernel.CurrentScheduler();
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
ASSERT(scheduler.ContextSwitchPending());
ASSERT(core == kernel.GetCurrentHostThreadID());
ASSERT(core == kernel.CurrentPhysicalCoreIndex());
scheduler.RescheduleCurrentCore();
}
}
@@ -346,13 +347,9 @@ void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
sc_sync_first_use = false;
}
// Abort if emulation was killed before the session really starts
if (!system.IsPoweredOn()) {
return;
}
// Emulation was stopped
if (stop_token.stop_requested()) {
break;
return;
}
auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();

View File

@@ -866,7 +866,52 @@ void EmulatedController::SetLedPattern() {
}
}
void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) {
supported_style_tag = supported_styles;
if (!is_connected) {
return;
}
if (!IsControllerSupported()) {
LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller",
npad_type);
Disconnect();
}
}
bool EmulatedController::IsControllerSupported() const {
switch (npad_type) {
case NpadStyleIndex::ProController:
return supported_style_tag.fullkey;
case NpadStyleIndex::Handheld:
return supported_style_tag.handheld;
case NpadStyleIndex::JoyconDual:
return supported_style_tag.joycon_dual;
case NpadStyleIndex::JoyconLeft:
return supported_style_tag.joycon_left;
case NpadStyleIndex::JoyconRight:
return supported_style_tag.joycon_right;
case NpadStyleIndex::GameCube:
return supported_style_tag.gamecube;
case NpadStyleIndex::Pokeball:
return supported_style_tag.palma;
case NpadStyleIndex::NES:
return supported_style_tag.lark;
case NpadStyleIndex::SNES:
return supported_style_tag.lucia;
case NpadStyleIndex::N64:
return supported_style_tag.lagoon;
case NpadStyleIndex::SegaGenesis:
return supported_style_tag.lager;
default:
return false;
}
}
void EmulatedController::Connect() {
if (!IsControllerSupported()) {
LOG_ERROR(Service_HID, "Controller type {} is not supported", npad_type);
return;
}
{
std::lock_guard lock{mutex};
if (is_configuring) {

View File

@@ -160,6 +160,13 @@ public:
*/
NpadStyleIndex GetNpadStyleIndex(bool get_temporary_value = false) const;
/**
* Sets the supported controller types. Disconnects the controller if current type is not
* supported
* @param supported_styles bitflag with supported types
*/
void SetSupportedNpadStyleTag(NpadStyleTag supported_styles);
/// Sets the connected status to true
void Connect();
@@ -310,6 +317,12 @@ private:
/// Set the params for TAS devices
void LoadTASParams();
/**
* Checks the current controller type against the supported_style_tag
* @return true if the controller is supported
*/
bool IsControllerSupported() const;
/**
* Updates the button status of the controller
* @param callback A CallbackStatus containing the button status
@@ -354,6 +367,7 @@ private:
NpadIdType npad_id_type;
NpadStyleIndex npad_type{NpadStyleIndex::None};
NpadStyleTag supported_style_tag{NpadStyleSet::All};
bool is_connected{false};
bool is_configuring{false};
f32 motion_sensitivity{0.01f};

View File

@@ -108,6 +108,16 @@ const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t inde
void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) {
supported_style_tag.raw = style_tag.raw;
player_1->SetSupportedNpadStyleTag(supported_style_tag);
player_2->SetSupportedNpadStyleTag(supported_style_tag);
player_3->SetSupportedNpadStyleTag(supported_style_tag);
player_4->SetSupportedNpadStyleTag(supported_style_tag);
player_5->SetSupportedNpadStyleTag(supported_style_tag);
player_6->SetSupportedNpadStyleTag(supported_style_tag);
player_7->SetSupportedNpadStyleTag(supported_style_tag);
player_8->SetSupportedNpadStyleTag(supported_style_tag);
other->SetSupportedNpadStyleTag(supported_style_tag);
handheld->SetSupportedNpadStyleTag(supported_style_tag);
}
NpadStyleTag HIDCore::GetSupportedStyleTag() const {
@@ -135,6 +145,16 @@ NpadIdType HIDCore::GetFirstNpadId() const {
return NpadIdType::Player1;
}
NpadIdType HIDCore::GetFirstDisconnectedNpadId() const {
for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) {
const auto* const controller = GetEmulatedControllerByIndex(player_index);
if (!controller->IsConnected()) {
return controller->GetNpadIdType();
}
}
return NpadIdType::Player1;
}
void HIDCore::EnableAllControllerConfiguration() {
player_1->EnableConfiguration();
player_2->EnableConfiguration();

View File

@@ -45,6 +45,9 @@ public:
/// Returns the first connected npad id
NpadIdType GetFirstNpadId() const;
/// Returns the first disconnected npad id
NpadIdType GetFirstDisconnectedNpadId() const;
/// Sets all emulated controllers into configuring mode.
void EnableAllControllerConfiguration();
@@ -73,7 +76,7 @@ private:
std::unique_ptr<EmulatedController> handheld;
std::unique_ptr<EmulatedConsole> console;
std::unique_ptr<EmulatedDevices> devices;
NpadStyleTag supported_style_tag;
NpadStyleTag supported_style_tag{NpadStyleSet::All};
};
} // namespace Core::HID

View File

@@ -256,6 +256,8 @@ enum class NpadStyleSet : u32 {
Lager = 1U << 11,
SystemExt = 1U << 29,
System = 1U << 30,
All = 0xFFFFFFFFU,
};
static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");

View File

@@ -9,6 +9,7 @@
#include "core/core.h"
#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_event.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
@@ -32,6 +33,7 @@ namespace Kernel::Init {
HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \
HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \
HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \
HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \
HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \
HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__)

View File

@@ -8,6 +8,7 @@
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/time_manager.h"
@@ -28,7 +29,7 @@ bool ReadFromUser(Core::System& system, s32* out, VAddr address) {
bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) {
auto& monitor = system.Monitor();
const auto current_core = system.CurrentCoreIndex();
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
// TODO(bunnei): We should call CanAccessAtomic(..) here.
@@ -58,7 +59,7 @@ bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 valu
bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) {
auto& monitor = system.Monitor();
const auto current_core = system.CurrentCoreIndex();
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
// TODO(bunnei): We should call CanAccessAtomic(..) here.
@@ -85,6 +86,27 @@ bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32
return true;
}
class ThreadQueueImplForKAddressArbiter final : public KThreadQueue {
public:
explicit ThreadQueueImplForKAddressArbiter(KernelCore& kernel_, KAddressArbiter::ThreadTree* t)
: KThreadQueue(kernel_), m_tree(t) {}
void CancelWait(KThread* waiting_thread, ResultCode wait_result,
bool cancel_timer_task) override {
// If the thread is waiting on an address arbiter, remove it from the tree.
if (waiting_thread->IsWaitingForAddressArbiter()) {
m_tree->erase(m_tree->iterator_to(*waiting_thread));
waiting_thread->ClearAddressArbiter();
}
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
private:
KAddressArbiter::ThreadTree* m_tree;
};
} // namespace
ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
@@ -96,14 +118,14 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
auto it = thread_tree.nfind_light({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
// End the thread's wait.
KThread* target_thread = std::addressof(*it);
target_thread->SetSyncedObject(nullptr, ResultSuccess);
target_thread->EndWait(ResultSuccess);
ASSERT(target_thread->IsWaitingForAddressArbiter());
target_thread->Wakeup();
target_thread->ClearAddressArbiter();
it = thread_tree.erase(it);
target_thread->ClearAddressArbiter();
++num_waiters;
}
}
@@ -129,14 +151,14 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32
auto it = thread_tree.nfind_light({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
// End the thread's wait.
KThread* target_thread = std::addressof(*it);
target_thread->SetSyncedObject(nullptr, ResultSuccess);
target_thread->EndWait(ResultSuccess);
ASSERT(target_thread->IsWaitingForAddressArbiter());
target_thread->Wakeup();
target_thread->ClearAddressArbiter();
it = thread_tree.erase(it);
target_thread->ClearAddressArbiter();
++num_waiters;
}
}
@@ -197,14 +219,14 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
// End the thread's wait.
KThread* target_thread = std::addressof(*it);
target_thread->SetSyncedObject(nullptr, ResultSuccess);
target_thread->EndWait(ResultSuccess);
ASSERT(target_thread->IsWaitingForAddressArbiter());
target_thread->Wakeup();
target_thread->ClearAddressArbiter();
it = thread_tree.erase(it);
target_thread->ClearAddressArbiter();
++num_waiters;
}
}
@@ -214,6 +236,7 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
{
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
@@ -224,9 +247,6 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
return ResultTerminationRequested;
}
// Set the synced object.
cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
// Read the value from userspace.
s32 user_value{};
bool succeeded{};
@@ -256,31 +276,20 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
// Set the arbiter.
cur_thread->SetAddressArbiter(&thread_tree, addr);
thread_tree.insert(*cur_thread);
cur_thread->SetState(ThreadState::Waiting);
// Wait for the thread to finish.
cur_thread->BeginWait(std::addressof(wait_queue));
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
}
// Cancel the timer wait.
kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
// Remove from the address arbiter.
{
KScopedSchedulerLock sl(kernel);
if (cur_thread->IsWaitingForAddressArbiter()) {
thread_tree.erase(thread_tree.iterator_to(*cur_thread));
cur_thread->ClearAddressArbiter();
}
}
// Get the result.
KSynchronizationObject* dummy{};
return cur_thread->GetWaitResult(&dummy);
return cur_thread->GetWaitResult();
}
ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
{
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
@@ -291,9 +300,6 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
return ResultTerminationRequested;
}
// Set the synced object.
cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
// Read the value from userspace.
s32 user_value{};
if (!ReadFromUser(system, &user_value, addr)) {
@@ -316,26 +322,14 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
// Set the arbiter.
cur_thread->SetAddressArbiter(&thread_tree, addr);
thread_tree.insert(*cur_thread);
cur_thread->SetState(ThreadState::Waiting);
// Wait for the thread to finish.
cur_thread->BeginWait(std::addressof(wait_queue));
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
}
// Cancel the timer wait.
kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
// Remove from the address arbiter.
{
KScopedSchedulerLock sl(kernel);
if (cur_thread->IsWaitingForAddressArbiter()) {
thread_tree.erase(thread_tree.iterator_to(*cur_thread));
cur_thread->ClearAddressArbiter();
}
}
// Get the result.
KSynchronizationObject* dummy{};
return cur_thread->GetWaitResult(&dummy);
return cur_thread->GetWaitResult();
}
} // namespace Kernel

View File

@@ -170,6 +170,10 @@ public:
}
}
const std::string& GetName() const {
return name;
}
private:
void RegisterWithKernel();
void UnregisterWithKernel();

View File

@@ -6,6 +6,7 @@
#include "core/hle/kernel/k_class_token.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_client_session.h"
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_process.h"
@@ -48,7 +49,7 @@ static_assert(ClassToken<KWritableEvent> == 0b10001001'00000000);
static_assert(ClassToken<KTransferMemory> == 0b10010001'00000000);
// static_assert(ClassToken<KDeviceAddressSpace> == 0b01100001'00000000);
// static_assert(ClassToken<KSessionRequest> == 0b10100001'00000000);
// static_assert(ClassToken<KCodeMemory> == 0b11000001'00000000);
static_assert(ClassToken<KCodeMemory> == 0b11000001'00000000);
// Ensure that the token hierarchy is correct.
@@ -79,7 +80,7 @@ static_assert(ClassToken<KWritableEvent> == ((0b10001001 << 8) | ClassToken<KAut
static_assert(ClassToken<KTransferMemory> == ((0b10010001 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KDeviceAddressSpace> == ((0b01100001 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KSessionRequest> == ((0b10100001 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KCodeMemory> == ((0b11000001 << 8) | ClassToken<KAutoObject>));
static_assert(ClassToken<KCodeMemory> == ((0b11000001 << 8) | ClassToken<KAutoObject>));
// Ensure that the token hierarchy reflects the class hierarchy.

View File

@@ -0,0 +1,146 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/common_types.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_page_linked_list.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/kernel/svc_types.h"
#include "core/hle/result.h"
namespace Kernel {
KCodeMemory::KCodeMemory(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_}, m_lock(kernel_) {}
ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, size_t size) {
// Set members.
m_owner = kernel.CurrentProcess();
// Get the owner page table.
auto& page_table = m_owner->PageTable();
// Construct the page group.
KMemoryInfo kBlockInfo = page_table.QueryInfo(addr);
m_page_group = KPageLinkedList(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages());
// Lock the memory.
R_TRY(page_table.LockForCodeMemory(addr, size))
// Clear the memory.
for (const auto& block : m_page_group.Nodes()) {
std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
}
// Set remaining tracking members.
m_address = addr;
m_is_initialized = true;
m_is_owner_mapped = false;
m_is_mapped = false;
// We succeeded.
return ResultSuccess;
}
void KCodeMemory::Finalize() {
// Unlock.
if (!m_is_mapped && !m_is_owner_mapped) {
const size_t size = m_page_group.GetNumPages() * PageSize;
m_owner->PageTable().UnlockForCodeMemory(m_address, size);
}
}
ResultCode KCodeMemory::Map(VAddr address, size_t size) {
// Validate the size.
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
// Lock ourselves.
KScopedLightLock lk(m_lock);
// Ensure we're not already mapped.
R_UNLESS(!m_is_mapped, ResultInvalidState);
// Map the memory.
R_TRY(kernel.CurrentProcess()->PageTable().MapPages(
address, m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite));
// Mark ourselves as mapped.
m_is_mapped = true;
return ResultSuccess;
}
ResultCode KCodeMemory::Unmap(VAddr address, size_t size) {
// Validate the size.
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
// Lock ourselves.
KScopedLightLock lk(m_lock);
// Unmap the memory.
R_TRY(kernel.CurrentProcess()->PageTable().UnmapPages(address, m_page_group,
KMemoryState::CodeOut));
// Mark ourselves as unmapped.
m_is_mapped = false;
return ResultSuccess;
}
ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) {
// Validate the size.
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
// Lock ourselves.
KScopedLightLock lk(m_lock);
// Ensure we're not already mapped.
R_UNLESS(!m_is_owner_mapped, ResultInvalidState);
// Convert the memory permission.
KMemoryPermission k_perm{};
switch (perm) {
case Svc::MemoryPermission::Read:
k_perm = KMemoryPermission::UserRead;
break;
case Svc::MemoryPermission::ReadExecute:
k_perm = KMemoryPermission::UserReadExecute;
break;
default:
break;
}
// Map the memory.
R_TRY(
m_owner->PageTable().MapPages(address, m_page_group, KMemoryState::GeneratedCode, k_perm));
// Mark ourselves as mapped.
m_is_owner_mapped = true;
return ResultSuccess;
}
ResultCode KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
// Validate the size.
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
// Lock ourselves.
KScopedLightLock lk(m_lock);
// Unmap the memory.
R_TRY(m_owner->PageTable().UnmapPages(address, m_page_group, KMemoryState::GeneratedCode));
// Mark ourselves as unmapped.
m_is_owner_mapped = false;
return ResultSuccess;
}
} // namespace Kernel

View File

@@ -0,0 +1,66 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_page_linked_list.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/kernel/svc_types.h"
#include "core/hle/result.h"
namespace Kernel {
enum class CodeMemoryOperation : u32 {
Map = 0,
MapToOwner = 1,
Unmap = 2,
UnmapFromOwner = 3,
};
class KCodeMemory final
: public KAutoObjectWithSlabHeapAndContainer<KCodeMemory, KAutoObjectWithList> {
KERNEL_AUTOOBJECT_TRAITS(KCodeMemory, KAutoObject);
public:
explicit KCodeMemory(KernelCore& kernel_);
ResultCode Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size);
void Finalize();
ResultCode Map(VAddr address, size_t size);
ResultCode Unmap(VAddr address, size_t size);
ResultCode MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm);
ResultCode UnmapFromOwner(VAddr address, size_t size);
bool IsInitialized() const {
return m_is_initialized;
}
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
KProcess* GetOwner() const {
return m_owner;
}
VAddr GetSourceAddress() const {
return m_address;
}
size_t GetSize() const {
return m_is_initialized ? m_page_group.GetNumPages() * PageSize : 0;
}
private:
KPageLinkedList m_page_group{};
KProcess* m_owner{};
VAddr m_address{};
KLightLock m_lock;
bool m_is_initialized{};
bool m_is_owner_mapped{};
bool m_is_mapped{};
};
} // namespace Kernel

View File

@@ -11,6 +11,7 @@
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_common.h"
#include "core/hle/kernel/svc_results.h"
@@ -33,7 +34,7 @@ bool WriteToUser(Core::System& system, VAddr address, const u32* p) {
bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero,
u32 new_orr_mask) {
auto& monitor = system.Monitor();
const auto current_core = system.CurrentCoreIndex();
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
// Load the value from the address.
const auto expected = monitor.ExclusiveRead32(current_core, address);
@@ -57,6 +58,48 @@ bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero
return true;
}
class ThreadQueueImplForKConditionVariableWaitForAddress final : public KThreadQueue {
public:
explicit ThreadQueueImplForKConditionVariableWaitForAddress(KernelCore& kernel_)
: KThreadQueue(kernel_) {}
void CancelWait(KThread* waiting_thread, ResultCode wait_result,
bool cancel_timer_task) override {
// Remove the thread as a waiter from its owner.
waiting_thread->GetLockOwner()->RemoveWaiter(waiting_thread);
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
};
class ThreadQueueImplForKConditionVariableWaitConditionVariable final : public KThreadQueue {
private:
KConditionVariable::ThreadTree* m_tree;
public:
explicit ThreadQueueImplForKConditionVariableWaitConditionVariable(
KernelCore& kernel_, KConditionVariable::ThreadTree* t)
: KThreadQueue(kernel_), m_tree(t) {}
void CancelWait(KThread* waiting_thread, ResultCode wait_result,
bool cancel_timer_task) override {
// Remove the thread as a waiter from its owner.
if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) {
owner->RemoveWaiter(waiting_thread);
}
// If the thread is waiting on a condvar, remove it from the tree.
if (waiting_thread->IsWaitingForConditionVariable()) {
m_tree->erase(m_tree->iterator_to(*waiting_thread));
waiting_thread->ClearConditionVariable();
}
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
};
} // namespace
KConditionVariable::KConditionVariable(Core::System& system_)
@@ -78,84 +121,77 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
// Determine the next tag.
u32 next_value{};
if (next_owner_thread) {
if (next_owner_thread != nullptr) {
next_value = next_owner_thread->GetAddressKeyValue();
if (num_waiters > 1) {
next_value |= Svc::HandleWaitMask;
}
next_owner_thread->SetSyncedObject(nullptr, ResultSuccess);
next_owner_thread->Wakeup();
}
// Write the value to userspace.
if (!WriteToUser(system, addr, std::addressof(next_value))) {
if (next_owner_thread) {
next_owner_thread->SetSyncedObject(nullptr, ResultInvalidCurrentMemory);
// Write the value to userspace.
ResultCode result{ResultSuccess};
if (WriteToUser(system, addr, std::addressof(next_value))) [[likely]] {
result = ResultSuccess;
} else {
result = ResultInvalidCurrentMemory;
}
return ResultInvalidCurrentMemory;
// Signal the next owner thread.
next_owner_thread->EndWait(result);
return result;
} else {
// Just write the value to userspace.
R_UNLESS(WriteToUser(system, addr, std::addressof(next_value)),
ResultInvalidCurrentMemory);
return ResultSuccess;
}
}
return ResultSuccess;
}
ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel);
// Wait for the address.
{
KScopedAutoObject<KThread> owner_thread;
ASSERT(owner_thread.IsNull());
{
KScopedSchedulerLock sl(kernel);
cur_thread->SetSyncedObject(nullptr, ResultSuccess);
// Check if the thread should terminate.
R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
{
// Read the tag from userspace.
u32 test_tag{};
R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr),
ResultInvalidCurrentMemory);
// If the tag isn't the handle (with wait mask), we're done.
R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), ResultSuccess);
// Get the lock owner thread.
owner_thread =
kernel.CurrentProcess()->GetHandleTable().GetObjectWithoutPseudoHandle<KThread>(
handle);
R_UNLESS(owner_thread.IsNotNull(), ResultInvalidHandle);
// Update the lock.
cur_thread->SetAddressKey(addr, value);
owner_thread->AddWaiter(cur_thread);
cur_thread->SetState(ThreadState::Waiting);
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
cur_thread->SetMutexWaitAddressForDebugging(addr);
}
}
ASSERT(owner_thread.IsNotNull());
}
// Remove the thread as a waiter from the lock owner.
KThread* owner_thread{};
{
KScopedSchedulerLock sl(kernel);
KThread* owner_thread = cur_thread->GetLockOwner();
if (owner_thread != nullptr) {
owner_thread->RemoveWaiter(cur_thread);
}
// Check if the thread should terminate.
R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
// Read the tag from userspace.
u32 test_tag{};
R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr), ResultInvalidCurrentMemory);
// If the tag isn't the handle (with wait mask), we're done.
R_SUCCEED_IF(test_tag != (handle | Svc::HandleWaitMask));
// Get the lock owner thread.
owner_thread = kernel.CurrentProcess()
->GetHandleTable()
.GetObjectWithoutPseudoHandle<KThread>(handle)
.ReleasePointerUnsafe();
R_UNLESS(owner_thread != nullptr, ResultInvalidHandle);
// Update the lock.
cur_thread->SetAddressKey(addr, value);
owner_thread->AddWaiter(cur_thread);
// Begin waiting.
cur_thread->BeginWait(std::addressof(wait_queue));
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
cur_thread->SetMutexWaitAddressForDebugging(addr);
}
// Close our reference to the owner thread, now that the wait is over.
owner_thread->Close();
// Get the wait result.
KSynchronizationObject* dummy{};
return cur_thread->GetWaitResult(std::addressof(dummy));
return cur_thread->GetWaitResult();
}
KThread* KConditionVariable::SignalImpl(KThread* thread) {
void KConditionVariable::SignalImpl(KThread* thread) {
// Check pre-conditions.
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
@@ -169,18 +205,16 @@ KThread* KConditionVariable::SignalImpl(KThread* thread) {
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
// TODO(bunnei): We should call CanAccessAtomic(..) here.
can_access = true;
if (can_access) {
if (can_access) [[likely]] {
UpdateLockAtomic(system, std::addressof(prev_tag), address, own_tag,
Svc::HandleWaitMask);
}
}
KThread* thread_to_close = nullptr;
if (can_access) {
if (can_access) [[likely]] {
if (prev_tag == Svc::InvalidHandle) {
// If nobody held the lock previously, we're all good.
thread->SetSyncedObject(nullptr, ResultSuccess);
thread->Wakeup();
thread->EndWait(ResultSuccess);
} else {
// Get the previous owner.
KThread* owner_thread = kernel.CurrentProcess()
@@ -189,33 +223,22 @@ KThread* KConditionVariable::SignalImpl(KThread* thread) {
static_cast<Handle>(prev_tag & ~Svc::HandleWaitMask))
.ReleasePointerUnsafe();
if (owner_thread) {
if (owner_thread) [[likely]] {
// Add the thread as a waiter on the owner.
owner_thread->AddWaiter(thread);
thread_to_close = owner_thread;
owner_thread->Close();
} else {
// The lock was tagged with a thread that doesn't exist.
thread->SetSyncedObject(nullptr, ResultInvalidState);
thread->Wakeup();
thread->EndWait(ResultInvalidState);
}
}
} else {
// If the address wasn't accessible, note so.
thread->SetSyncedObject(nullptr, ResultInvalidCurrentMemory);
thread->Wakeup();
thread->EndWait(ResultInvalidCurrentMemory);
}
return thread_to_close;
}
void KConditionVariable::Signal(u64 cv_key, s32 count) {
// Prepare for signaling.
constexpr int MaxThreads = 16;
KLinkedList<KThread> thread_list{kernel};
std::array<KThread*, MaxThreads> thread_array;
s32 num_to_close{};
// Perform signaling.
s32 num_waiters{};
{
@@ -226,14 +249,7 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
(it->GetConditionVariableKey() == cv_key)) {
KThread* target_thread = std::addressof(*it);
if (KThread* thread = SignalImpl(target_thread); thread != nullptr) {
if (num_to_close < MaxThreads) {
thread_array[num_to_close++] = thread;
} else {
thread_list.push_back(*thread);
}
}
this->SignalImpl(target_thread);
it = thread_tree.erase(it);
target_thread->ClearConditionVariable();
++num_waiters;
@@ -245,27 +261,16 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
WriteToUser(system, cv_key, std::addressof(has_waiter_flag));
}
}
// Close threads in the array.
for (auto i = 0; i < num_to_close; ++i) {
thread_array[i]->Close();
}
// Close threads in the list.
for (auto it = thread_list.begin(); it != thread_list.end(); it = thread_list.erase(it)) {
(*it).Close();
}
}
ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
KThread* cur_thread = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKConditionVariableWaitConditionVariable wait_queue(
kernel, std::addressof(thread_tree));
{
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
// Set the synced object.
cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
KScopedSchedulerLockAndSleep slp(kernel, cur_thread, timeout);
// Check that the thread isn't terminating.
if (cur_thread->IsTerminationRequested()) {
@@ -290,8 +295,7 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
}
// Wake up the next owner.
next_owner_thread->SetSyncedObject(nullptr, ResultSuccess);
next_owner_thread->Wakeup();
next_owner_thread->EndWait(ResultSuccess);
}
// Write to the cv key.
@@ -308,40 +312,21 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
}
}
// If timeout is zero, time out.
R_UNLESS(timeout != 0, ResultTimedOut);
// Update condition variable tracking.
{
cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
thread_tree.insert(*cur_thread);
}
cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
thread_tree.insert(*cur_thread);
// If the timeout is non-zero, set the thread as waiting.
if (timeout != 0) {
cur_thread->SetState(ThreadState::Waiting);
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
cur_thread->SetMutexWaitAddressForDebugging(addr);
}
// Begin waiting.
cur_thread->BeginWait(std::addressof(wait_queue));
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
cur_thread->SetMutexWaitAddressForDebugging(addr);
}
// Cancel the timer wait.
kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
// Remove from the condition variable.
{
KScopedSchedulerLock sl(kernel);
if (KThread* owner = cur_thread->GetLockOwner(); owner != nullptr) {
owner->RemoveWaiter(cur_thread);
}
if (cur_thread->IsWaitingForConditionVariable()) {
thread_tree.erase(thread_tree.iterator_to(*cur_thread));
cur_thread->ClearConditionVariable();
}
}
// Get the result.
KSynchronizationObject* dummy{};
return cur_thread->GetWaitResult(std::addressof(dummy));
// Get the wait result.
return cur_thread->GetWaitResult();
}
} // namespace Kernel

View File

@@ -34,7 +34,7 @@ public:
[[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout);
private:
[[nodiscard]] KThread* SignalImpl(KThread* thread);
void SignalImpl(KThread* thread);
ThreadTree thread_tree;

View File

@@ -13,6 +13,7 @@ ResultCode KHandleTable::Finalize() {
// Get the table and clear our record of it.
u16 saved_table_size = 0;
{
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
std::swap(m_table_size, saved_table_size);
@@ -43,6 +44,7 @@ bool KHandleTable::Remove(Handle handle) {
// Find the object and free the entry.
KAutoObject* obj = nullptr;
{
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
if (this->IsValidHandle(handle)) {
@@ -62,6 +64,7 @@ bool KHandleTable::Remove(Handle handle) {
}
ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
// Never exceed our capacity.
@@ -84,6 +87,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
}
ResultCode KHandleTable::Reserve(Handle* out_handle) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
// Never exceed our capacity.
@@ -94,6 +98,7 @@ ResultCode KHandleTable::Reserve(Handle* out_handle) {
}
void KHandleTable::Unreserve(Handle handle) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
// Unpack the handle.
@@ -112,6 +117,7 @@ void KHandleTable::Unreserve(Handle handle) {
}
void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
// Unpack the handle.

View File

@@ -68,6 +68,7 @@ public:
template <typename T = KAutoObject>
KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const {
// Lock and look up in table.
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
if constexpr (std::is_same_v<T, KAutoObject>) {
@@ -122,6 +123,7 @@ public:
size_t num_opened;
{
// Lock the table.
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
for (num_opened = 0; num_opened < num_handles; num_opened++) {
// Get the current handle.

View File

@@ -0,0 +1,80 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/kernel/k_light_condition_variable.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
namespace {
class ThreadQueueImplForKLightConditionVariable final : public KThreadQueue {
public:
ThreadQueueImplForKLightConditionVariable(KernelCore& kernel_, KThread::WaiterList* wl,
bool term)
: KThreadQueue(kernel_), m_wait_list(wl), m_allow_terminating_thread(term) {}
void CancelWait(KThread* waiting_thread, ResultCode wait_result,
bool cancel_timer_task) override {
// Only process waits if we're allowed to.
if (ResultTerminationRequested == wait_result && m_allow_terminating_thread) {
return;
}
// Remove the thread from the waiting thread from the light condition variable.
m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
private:
KThread::WaiterList* m_wait_list;
bool m_allow_terminating_thread;
};
} // namespace
void KLightConditionVariable::Wait(KLightLock* lock, s64 timeout, bool allow_terminating_thread) {
// Create thread queue.
KThread* owner = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKLightConditionVariable wait_queue(kernel, std::addressof(wait_list),
allow_terminating_thread);
// Sleep the thread.
{
KScopedSchedulerLockAndSleep lk(kernel, owner, timeout);
if (!allow_terminating_thread && owner->IsTerminationRequested()) {
lk.CancelSleep();
return;
}
lock->Unlock();
// Add the thread to the queue.
wait_list.push_back(*owner);
// Begin waiting.
owner->BeginWait(std::addressof(wait_queue));
}
// Re-acquire the lock.
lock->Lock();
}
void KLightConditionVariable::Broadcast() {
KScopedSchedulerLock lk(kernel);
// Signal all threads.
for (auto it = wait_list.begin(); it != wait_list.end(); it = wait_list.erase(it)) {
it->EndWait(ResultSuccess);
}
}
} // namespace Kernel

View File

@@ -2,72 +2,24 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// This file references various implementation details from Atmosphere, an open-source firmware for
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/time_manager.h"
#include "core/hle/kernel/k_thread.h"
namespace Kernel {
class KernelCore;
class KLightLock;
class KLightConditionVariable {
public:
explicit KLightConditionVariable(KernelCore& kernel_) : kernel{kernel_} {}
void Wait(KLightLock* lock, s64 timeout = -1, bool allow_terminating_thread = true) {
WaitImpl(lock, timeout, allow_terminating_thread);
}
void Broadcast() {
KScopedSchedulerLock lk{kernel};
// Signal all threads.
for (auto& thread : wait_list) {
thread.SetState(ThreadState::Runnable);
}
}
void Wait(KLightLock* lock, s64 timeout = -1, bool allow_terminating_thread = true);
void Broadcast();
private:
void WaitImpl(KLightLock* lock, s64 timeout, bool allow_terminating_thread) {
KThread* owner = GetCurrentThreadPointer(kernel);
// Sleep the thread.
{
KScopedSchedulerLockAndSleep lk{kernel, owner, timeout};
if (!allow_terminating_thread && owner->IsTerminationRequested()) {
lk.CancelSleep();
return;
}
lock->Unlock();
// Set the thread as waiting.
GetCurrentThread(kernel).SetState(ThreadState::Waiting);
// Add the thread to the queue.
wait_list.push_back(GetCurrentThread(kernel));
}
// Remove the thread from the wait list.
{
KScopedSchedulerLock sl{kernel};
wait_list.erase(wait_list.iterator_to(GetCurrentThread(kernel)));
}
// Cancel the task that the sleep setup.
kernel.TimeManager().UnscheduleTimeEvent(owner);
// Re-acquire the lock.
lock->Lock();
}
KernelCore& kernel;
KThread::WaiterList wait_list{};
};

View File

@@ -5,44 +5,59 @@
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel {
namespace {
class ThreadQueueImplForKLightLock final : public KThreadQueue {
public:
explicit ThreadQueueImplForKLightLock(KernelCore& kernel_) : KThreadQueue(kernel_) {}
void CancelWait(KThread* waiting_thread, ResultCode wait_result,
bool cancel_timer_task) override {
// Remove the thread as a waiter from its owner.
if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) {
owner->RemoveWaiter(waiting_thread);
}
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
};
} // namespace
void KLightLock::Lock() {
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
const uintptr_t cur_thread_tag = (cur_thread | 1);
while (true) {
uintptr_t old_tag = tag.load(std::memory_order_relaxed);
while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : old_tag | 1,
while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : (old_tag | 1),
std::memory_order_acquire)) {
if ((old_tag | 1) == cur_thread_tag) {
return;
}
}
if ((old_tag == 0) || ((old_tag | 1) == cur_thread_tag)) {
if (old_tag == 0 || this->LockSlowPath(old_tag | 1, cur_thread)) {
break;
}
LockSlowPath(old_tag | 1, cur_thread);
}
}
void KLightLock::Unlock() {
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
uintptr_t expected = cur_thread;
do {
if (expected != cur_thread) {
return UnlockSlowPath(cur_thread);
}
} while (!tag.compare_exchange_weak(expected, 0, std::memory_order_release));
if (!tag.compare_exchange_strong(expected, 0, std::memory_order_release)) {
this->UnlockSlowPath(cur_thread);
}
}
void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
bool KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
KThread* cur_thread = reinterpret_cast<KThread*>(_cur_thread);
ThreadQueueImplForKLightLock wait_queue(kernel);
// Pend the current thread waiting on the owner thread.
{
@@ -50,7 +65,7 @@ void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
// Ensure we actually have locking to do.
if (tag.load(std::memory_order_relaxed) != _owner) {
return;
return false;
}
// Add the current thread as a waiter on the owner.
@@ -58,22 +73,15 @@ void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag)));
owner_thread->AddWaiter(cur_thread);
// Set thread states.
cur_thread->SetState(ThreadState::Waiting);
// Begin waiting to hold the lock.
cur_thread->BeginWait(std::addressof(wait_queue));
if (owner_thread->IsSuspended()) {
owner_thread->ContinueIfHasKernelWaiters();
}
}
// We're no longer waiting on the lock owner.
{
KScopedSchedulerLock sl{kernel};
if (KThread* owner_thread = cur_thread->GetLockOwner(); owner_thread != nullptr) {
owner_thread->RemoveWaiter(cur_thread);
}
}
return true;
}
void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
@@ -81,22 +89,20 @@ void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
// Unlock.
{
KScopedSchedulerLock sl{kernel};
KScopedSchedulerLock sl(kernel);
// Get the next owner.
s32 num_waiters = 0;
s32 num_waiters;
KThread* next_owner = owner_thread->RemoveWaiterByKey(
std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(tag)));
// Pass the lock to the next owner.
uintptr_t next_tag = 0;
if (next_owner != nullptr) {
next_tag = reinterpret_cast<uintptr_t>(next_owner);
if (num_waiters > 1) {
next_tag |= 0x1;
}
next_tag =
reinterpret_cast<uintptr_t>(next_owner) | static_cast<uintptr_t>(num_waiters > 1);
next_owner->SetState(ThreadState::Runnable);
next_owner->EndWait(ResultSuccess);
if (next_owner->IsSuspended()) {
next_owner->ContinueIfHasKernelWaiters();
@@ -110,7 +116,7 @@ void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
}
// Write the new tag value.
tag.store(next_tag);
tag.store(next_tag, std::memory_order_release);
}
}

View File

@@ -20,7 +20,7 @@ public:
void Unlock();
void LockSlowPath(uintptr_t owner, uintptr_t cur_thread);
bool LockSlowPath(uintptr_t owner, uintptr_t cur_thread);
void UnlockSlowPath(uintptr_t cur_thread);

View File

@@ -131,6 +131,26 @@ enum class KMemoryPermission : u8 {
UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write |
Svc::MemoryPermission::Execute),
KernelShift = 3,
KernelRead = Read << KernelShift,
KernelWrite = Write << KernelShift,
KernelExecute = Execute << KernelShift,
NotMapped = (1 << (2 * KernelShift)),
KernelReadWrite = KernelRead | KernelWrite,
KernelReadExecute = KernelRead | KernelExecute,
UserRead = Read | KernelRead,
UserWrite = Write | KernelWrite,
UserExecute = Execute,
UserReadWrite = UserRead | UserWrite,
UserReadExecute = UserRead | UserExecute,
IpcLockChangeMask = NotMapped | UserReadWrite
};
DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission);

View File

@@ -27,6 +27,10 @@ public:
return num_pages;
}
constexpr std::size_t GetSize() const {
return GetNumPages() * PageSize;
}
private:
u64 addr{};
std::size_t num_pages{};

View File

@@ -368,6 +368,33 @@ ResultCode KPageTable::UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, st
return ResultSuccess;
}
ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
KPageTable& src_page_table, VAddr src_addr) {
std::lock_guard lock{page_table_lock};
const std::size_t num_pages{size / PageSize};
// Check that the memory is mapped in the destination process.
size_t num_allocator_blocks;
R_TRY(CheckMemoryState(&num_allocator_blocks, dst_addr, size, KMemoryState::All,
KMemoryState::SharedCode, KMemoryPermission::UserReadWrite,
KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
KMemoryAttribute::None));
// Check that the memory is mapped in the source process.
R_TRY(src_page_table.CheckMemoryState(src_addr, size, KMemoryState::FlagCanMapProcess,
KMemoryState::FlagCanMapProcess, KMemoryPermission::None,
KMemoryPermission::None, KMemoryAttribute::All,
KMemoryAttribute::None));
CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap));
// Apply the memory block update.
block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
KMemoryAttribute::None);
return ResultSuccess;
}
void KPageTable::MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end) {
auto node{page_linked_list.Nodes().begin()};
PAddr map_addr{node->GetAddress()};
@@ -942,6 +969,60 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
return ResultSuccess;
}
ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite;
KMemoryPermission old_perm{};
if (const ResultCode result{CheckMemoryState(
nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
KMemoryState::FlagCanCodeMemory, KMemoryPermission::Mask,
KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
result.IsError()) {
return result;
}
new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
block_manager->UpdateLock(
addr, size / PageSize,
[](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
block->ShareToDevice(permission);
},
new_perm);
return ResultSuccess;
}
ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
std::lock_guard lock{page_table_lock};
KMemoryPermission new_perm = KMemoryPermission::UserReadWrite;
KMemoryPermission old_perm{};
if (const ResultCode result{CheckMemoryState(
nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::All, KMemoryAttribute::Locked)};
result.IsError()) {
return result;
}
new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
block_manager->UpdateLock(
addr, size / PageSize,
[](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
block->UnshareToDevice(permission);
},
new_perm);
return ResultSuccess;
}
ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
block_manager = std::make_unique<KMemoryBlockManager>(start, end);
@@ -1231,4 +1312,42 @@ ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermissi
return ResultSuccess;
}
ResultCode KPageTable::CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr) const {
// Get information about the first block.
const VAddr last_addr = addr + size - 1;
KMemoryBlockManager::const_iterator it{block_manager->FindIterator(addr)};
KMemoryInfo info = it->GetMemoryInfo();
// If the start address isn't aligned, we need a block.
const size_t blocks_for_start_align =
(Common::AlignDown(addr, PageSize) != info.GetAddress()) ? 1 : 0;
while (true) {
// Validate against the provided masks.
R_TRY(CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
// Break once we're done.
if (last_addr <= info.GetLastAddress()) {
break;
}
// Advance our iterator.
it++;
info = it->GetMemoryInfo();
}
// If the end address isn't aligned, we need a block.
const size_t blocks_for_end_align =
(Common::AlignUp(addr + size, PageSize) != info.GetEndAddress()) ? 1 : 0;
if (out_blocks_needed != nullptr) {
*out_blocks_needed = blocks_for_start_align + blocks_for_end_align;
}
return ResultSuccess;
}
} // namespace Kernel

View File

@@ -33,6 +33,8 @@ public:
KMemoryPermission perm);
ResultCode MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
ResultCode UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table,
VAddr src_addr);
ResultCode MapPhysicalMemory(VAddr addr, std::size_t size);
ResultCode UnmapPhysicalMemory(VAddr addr, std::size_t size);
ResultCode UnmapMemory(VAddr addr, std::size_t size);
@@ -55,6 +57,8 @@ public:
KMemoryPermission perm, PAddr map_addr = 0);
ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size);
ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
ResultCode LockForCodeMemory(VAddr addr, std::size_t size);
ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size);
Common::PageTable& PageTableImpl() {
return page_table_impl;
@@ -115,6 +119,10 @@ private:
return CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask,
perm, attr_mask, attr, ignore_attr);
}
ResultCode CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
std::recursive_mutex page_table_lock;
std::unique_ptr<KMemoryBlockManager> block_manager;

View File

@@ -60,6 +60,7 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority
thread->GetContext64().cpu_registers[0] = 0;
thread->GetContext32().cpu_registers[1] = thread_handle;
thread->GetContext64().cpu_registers[1] = thread_handle;
thread->DisableDispatch();
auto& kernel = system.Kernel();
// Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
@@ -227,12 +228,15 @@ void KProcess::PinCurrentThread() {
const s32 core_id = GetCurrentCoreId(kernel);
KThread* cur_thread = GetCurrentThreadPointer(kernel);
// Pin it.
PinThread(core_id, cur_thread);
cur_thread->Pin();
// If the thread isn't terminated, pin it.
if (!cur_thread->IsTerminationRequested()) {
// Pin it.
PinThread(core_id, cur_thread);
cur_thread->Pin();
// An update is needed.
KScheduler::SetSchedulerUpdateNeeded(kernel);
// An update is needed.
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
}
void KProcess::UnpinCurrentThread() {
@@ -250,6 +254,20 @@ void KProcess::UnpinCurrentThread() {
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
void KProcess::UnpinThread(KThread* thread) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Get the thread's core id.
const auto core_id = thread->GetActiveCore();
// Unpin it.
UnpinThread(core_id, thread);
thread->Unpin();
// An update is needed.
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
ResultCode KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address,
[[maybe_unused]] size_t size) {
// Lock ourselves, to prevent concurrent access.

View File

@@ -347,6 +347,7 @@ public:
void PinCurrentThread();
void UnpinCurrentThread();
void UnpinThread(KThread* thread);
KLightLock& GetStateLock() {
return state_lock;

View File

@@ -240,8 +240,8 @@ void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s3
// If the thread is runnable, we want to change its priority in the queue.
if (thread->GetRawState() == ThreadState::Runnable) {
GetPriorityQueue(kernel).ChangePriority(
old_priority, thread == kernel.CurrentScheduler()->GetCurrentThread(), thread);
GetPriorityQueue(kernel).ChangePriority(old_priority,
thread == kernel.GetCurrentEmuThread(), thread);
IncrementScheduledCount(thread);
SetSchedulerUpdateNeeded(kernel);
}
@@ -360,7 +360,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
}
bool KScheduler::CanSchedule(KernelCore& kernel) {
return kernel.CurrentScheduler()->GetCurrentThread()->GetDisableDispatchCount() <= 1;
return kernel.GetCurrentEmuThread()->GetDisableDispatchCount() <= 1;
}
bool KScheduler::IsSchedulerUpdateNeeded(const KernelCore& kernel) {
@@ -376,20 +376,30 @@ void KScheduler::ClearSchedulerUpdateNeeded(KernelCore& kernel) {
}
void KScheduler::DisableScheduling(KernelCore& kernel) {
if (auto* scheduler = kernel.CurrentScheduler(); scheduler) {
ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 0);
scheduler->GetCurrentThread()->DisableDispatch();
// If we are shutting down the kernel, none of this is relevant anymore.
if (kernel.IsShuttingDown()) {
return;
}
ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 0);
GetCurrentThreadPointer(kernel)->DisableDispatch();
}
void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
if (auto* scheduler = kernel.CurrentScheduler(); scheduler) {
ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1);
if (scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1) {
scheduler->GetCurrentThread()->EnableDispatch();
}
// If we are shutting down the kernel, none of this is relevant anymore.
if (kernel.IsShuttingDown()) {
return;
}
auto* current_thread = GetCurrentThreadPointer(kernel);
ASSERT(current_thread->GetDisableDispatchCount() >= 1);
if (current_thread->GetDisableDispatchCount() > 1) {
current_thread->EnableDispatch();
} else {
RescheduleCores(kernel, cores_needing_scheduling);
}
RescheduleCores(kernel, cores_needing_scheduling);
}
u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
@@ -617,13 +627,17 @@ KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, c
state.highest_priority_thread = nullptr;
}
KScheduler::~KScheduler() {
void KScheduler::Finalize() {
if (idle_thread) {
idle_thread->Close();
idle_thread = nullptr;
}
}
KScheduler::~KScheduler() {
ASSERT(!idle_thread);
}
KThread* KScheduler::GetCurrentThread() const {
if (auto result = current_thread.load(); result) {
return result;
@@ -642,10 +656,12 @@ void KScheduler::RescheduleCurrentCore() {
if (phys_core.IsInterrupted()) {
phys_core.ClearInterrupt();
}
guard.Lock();
if (state.needs_scheduling.load()) {
Schedule();
} else {
GetCurrentThread()->EnableDispatch();
guard.Unlock();
}
}
@@ -655,26 +671,33 @@ void KScheduler::OnThreadStart() {
}
void KScheduler::Unload(KThread* thread) {
ASSERT(thread);
LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr");
if (thread) {
if (thread->IsCallingSvc()) {
thread->ClearIsCallingSvc();
}
if (!thread->IsTerminationRequested()) {
prev_thread = thread;
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
cpu_core.SaveContext(thread->GetContext32());
cpu_core.SaveContext(thread->GetContext64());
// Save the TPIDR_EL0 system register in case it was modified.
thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
cpu_core.ClearExclusiveState();
} else {
prev_thread = nullptr;
}
thread->context_guard.Unlock();
if (thread->IsCallingSvc()) {
thread->ClearIsCallingSvc();
}
auto& physical_core = system.Kernel().PhysicalCore(core_id);
if (!physical_core.IsInitialized()) {
return;
}
Core::ARM_Interface& cpu_core = physical_core.ArmInterface();
cpu_core.SaveContext(thread->GetContext32());
cpu_core.SaveContext(thread->GetContext64());
// Save the TPIDR_EL0 system register in case it was modified.
thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
cpu_core.ClearExclusiveState();
if (!thread->IsTerminationRequested() && thread->GetActiveCore() == core_id) {
prev_thread = thread;
} else {
prev_thread = nullptr;
}
thread->context_guard.Unlock();
}
void KScheduler::Reload(KThread* thread) {
@@ -683,11 +706,6 @@ void KScheduler::Reload(KThread* thread) {
if (thread) {
ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable.");
auto* const thread_owner_process = thread->GetOwnerProcess();
if (thread_owner_process != nullptr) {
system.Kernel().MakeCurrentProcess(thread_owner_process);
}
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
cpu_core.LoadContext(thread->GetContext32());
cpu_core.LoadContext(thread->GetContext64());
@@ -705,7 +723,7 @@ void KScheduler::SwitchContextStep2() {
}
void KScheduler::ScheduleImpl() {
KThread* previous_thread = current_thread.load();
KThread* previous_thread = GetCurrentThread();
KThread* next_thread = state.highest_priority_thread;
state.needs_scheduling = false;
@@ -717,10 +735,15 @@ void KScheduler::ScheduleImpl() {
// If we're not actually switching thread, there's nothing to do.
if (next_thread == current_thread.load()) {
previous_thread->EnableDispatch();
guard.Unlock();
return;
}
if (next_thread->GetCurrentCore() != core_id) {
next_thread->SetCurrentCore(core_id);
}
current_thread.store(next_thread);
KProcess* const previous_process = system.Kernel().CurrentProcess();
@@ -731,11 +754,7 @@ void KScheduler::ScheduleImpl() {
Unload(previous_thread);
std::shared_ptr<Common::Fiber>* old_context;
if (previous_thread != nullptr) {
old_context = &previous_thread->GetHostContext();
} else {
old_context = &idle_thread->GetHostContext();
}
old_context = &previous_thread->GetHostContext();
guard.Unlock();
Common::Fiber::YieldTo(*old_context, *switch_fiber);

View File

@@ -33,6 +33,8 @@ public:
explicit KScheduler(Core::System& system_, s32 core_id_);
~KScheduler();
void Finalize();
/// Reschedules to the next available thread (call after current thread is suspended)
void RescheduleCurrentCore();

View File

@@ -23,6 +23,11 @@ public:
}
void Lock() {
// If we are shutting down the kernel, none of this is relevant anymore.
if (kernel.IsShuttingDown()) {
return;
}
if (IsLockedByCurrentThread()) {
// If we already own the lock, we can just increment the count.
ASSERT(lock_count > 0);
@@ -43,6 +48,11 @@ public:
}
void Unlock() {
// If we are shutting down the kernel, none of this is relevant anymore.
if (kernel.IsShuttingDown()) {
return;
}
ASSERT(IsLockedByCurrentThread());
ASSERT(lock_count > 0);

View File

@@ -8,6 +8,7 @@
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/global_scheduler_context.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/time_manager.h"

View File

@@ -175,8 +175,7 @@ ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) {
{
KScopedSchedulerLock lock(kernel);
if (!context.IsThreadWaiting()) {
context.GetThread().Wakeup();
context.GetThread().SetSyncedObject(nullptr, result);
context.GetThread().EndWait(result);
}
}

View File

@@ -8,11 +8,66 @@
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
namespace {
class ThreadQueueImplForKSynchronizationObjectWait final : public KThreadQueueWithoutEndWait {
public:
ThreadQueueImplForKSynchronizationObjectWait(KernelCore& kernel_, KSynchronizationObject** o,
KSynchronizationObject::ThreadListNode* n, s32 c)
: KThreadQueueWithoutEndWait(kernel_), m_objects(o), m_nodes(n), m_count(c) {}
void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object,
ResultCode wait_result) override {
// Determine the sync index, and unlink all nodes.
s32 sync_index = -1;
for (auto i = 0; i < m_count; ++i) {
// Check if this is the signaled object.
if (m_objects[i] == signaled_object && sync_index == -1) {
sync_index = i;
}
// Unlink the current node from the current object.
m_objects[i]->UnlinkNode(std::addressof(m_nodes[i]));
}
// Set the waiting thread's sync index.
waiting_thread->SetSyncedIndex(sync_index);
// Set the waiting thread as not cancellable.
waiting_thread->ClearCancellable();
// Invoke the base end wait handler.
KThreadQueue::EndWait(waiting_thread, wait_result);
}
void CancelWait(KThread* waiting_thread, ResultCode wait_result,
bool cancel_timer_task) override {
// Remove all nodes from our list.
for (auto i = 0; i < m_count; ++i) {
m_objects[i]->UnlinkNode(std::addressof(m_nodes[i]));
}
// Set the waiting thread as not cancellable.
waiting_thread->ClearCancellable();
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
private:
KSynchronizationObject** m_objects;
KSynchronizationObject::ThreadListNode* m_nodes;
s32 m_count;
};
} // namespace
void KSynchronizationObject::Finalize() {
this->OnFinalizeSynchronizationObject();
KAutoObject::Finalize();
@@ -25,11 +80,19 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
std::vector<ThreadListNode> thread_nodes(num_objects);
// Prepare for wait.
KThread* thread = kernel_ctx.CurrentScheduler()->GetCurrentThread();
KThread* thread = GetCurrentThreadPointer(kernel_ctx);
ThreadQueueImplForKSynchronizationObjectWait wait_queue(kernel_ctx, objects,
thread_nodes.data(), num_objects);
{
// Setup the scheduling lock and sleep.
KScopedSchedulerLockAndSleep slp{kernel_ctx, thread, timeout};
KScopedSchedulerLockAndSleep slp(kernel_ctx, thread, timeout);
// Check if the thread should terminate.
if (thread->IsTerminationRequested()) {
slp.CancelSleep();
return ResultTerminationRequested;
}
// Check if any of the objects are already signaled.
for (auto i = 0; i < num_objects; ++i) {
@@ -48,12 +111,6 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
return ResultTimedOut;
}
// Check if the thread should terminate.
if (thread->IsTerminationRequested()) {
slp.CancelSleep();
return ResultTerminationRequested;
}
// Check if waiting was canceled.
if (thread->IsWaitCancelled()) {
slp.CancelSleep();
@@ -66,73 +123,25 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
thread_nodes[i].thread = thread;
thread_nodes[i].next = nullptr;
if (objects[i]->thread_list_tail == nullptr) {
objects[i]->thread_list_head = std::addressof(thread_nodes[i]);
} else {
objects[i]->thread_list_tail->next = std::addressof(thread_nodes[i]);
}
objects[i]->thread_list_tail = std::addressof(thread_nodes[i]);
objects[i]->LinkNode(std::addressof(thread_nodes[i]));
}
// For debugging only
thread->SetWaitObjectsForDebugging({objects, static_cast<std::size_t>(num_objects)});
// Mark the thread as waiting.
// Mark the thread as cancellable.
thread->SetCancellable();
thread->SetSyncedObject(nullptr, ResultTimedOut);
thread->SetState(ThreadState::Waiting);
// Clear the thread's synced index.
thread->SetSyncedIndex(-1);
// Wait for an object to be signaled.
thread->BeginWait(std::addressof(wait_queue));
thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization);
}
// The lock/sleep is done, so we should be able to get our result.
// Thread is no longer cancellable.
thread->ClearCancellable();
// For debugging only
thread->SetWaitObjectsForDebugging({});
// Cancel the timer as needed.
kernel_ctx.TimeManager().UnscheduleTimeEvent(thread);
// Set the output index.
*out_index = thread->GetSyncedIndex();
// Get the wait result.
ResultCode wait_result{ResultSuccess};
s32 sync_index = -1;
{
KScopedSchedulerLock lock(kernel_ctx);
KSynchronizationObject* synced_obj;
wait_result = thread->GetWaitResult(std::addressof(synced_obj));
for (auto i = 0; i < num_objects; ++i) {
// Unlink the object from the list.
ThreadListNode* prev_ptr =
reinterpret_cast<ThreadListNode*>(std::addressof(objects[i]->thread_list_head));
ThreadListNode* prev_val = nullptr;
ThreadListNode *prev, *tail_prev;
do {
prev = prev_ptr;
prev_ptr = prev_ptr->next;
tail_prev = prev_val;
prev_val = prev_ptr;
} while (prev_ptr != std::addressof(thread_nodes[i]));
if (objects[i]->thread_list_tail == std::addressof(thread_nodes[i])) {
objects[i]->thread_list_tail = tail_prev;
}
prev->next = thread_nodes[i].next;
if (objects[i] == synced_obj) {
sync_index = i;
}
}
}
// Set output.
*out_index = sync_index;
return wait_result;
return thread->GetWaitResult();
}
KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_)
@@ -141,7 +150,7 @@ KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_)
KSynchronizationObject::~KSynchronizationObject() = default;
void KSynchronizationObject::NotifyAvailable(ResultCode result) {
KScopedSchedulerLock lock(kernel);
KScopedSchedulerLock sl(kernel);
// If we're not signaled, we've nothing to notify.
if (!this->IsSignaled()) {
@@ -150,11 +159,7 @@ void KSynchronizationObject::NotifyAvailable(ResultCode result) {
// Iterate over each thread.
for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
KThread* thread = cur_node->thread;
if (thread->GetState() == ThreadState::Waiting) {
thread->SetSyncedObject(this, result);
thread->SetState(ThreadState::Runnable);
}
cur_node->thread->NotifyAvailable(this, result);
}
}

View File

@@ -35,6 +35,38 @@ public:
[[nodiscard]] std::vector<KThread*> GetWaitingThreadsForDebugging() const;
void LinkNode(ThreadListNode* node_) {
// Link the node to the list.
if (thread_list_tail == nullptr) {
thread_list_head = node_;
} else {
thread_list_tail->next = node_;
}
thread_list_tail = node_;
}
void UnlinkNode(ThreadListNode* node_) {
// Unlink the node from the list.
ThreadListNode* prev_ptr =
reinterpret_cast<ThreadListNode*>(std::addressof(thread_list_head));
ThreadListNode* prev_val = nullptr;
ThreadListNode *prev, *tail_prev;
do {
prev = prev_ptr;
prev_ptr = prev_ptr->next;
tail_prev = prev_val;
prev_val = prev_ptr;
} while (prev_ptr != node_);
if (thread_list_tail == node_) {
thread_list_tail = tail_prev;
}
prev->next = node_->next;
}
protected:
explicit KSynchronizationObject(KernelCore& kernel);
~KSynchronizationObject() override;

View File

@@ -13,6 +13,9 @@
#include "common/common_types.h"
#include "common/fiber.h"
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "common/settings.h"
#include "common/thread_queue_list.h"
#include "core/core.h"
#include "core/cpu_manager.h"
#include "core/hardware_properties.h"
@@ -56,6 +59,34 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context,
namespace Kernel {
namespace {
class ThreadQueueImplForKThreadSleep final : public KThreadQueueWithoutEndWait {
public:
explicit ThreadQueueImplForKThreadSleep(KernelCore& kernel_)
: KThreadQueueWithoutEndWait(kernel_) {}
};
class ThreadQueueImplForKThreadSetProperty final : public KThreadQueue {
public:
explicit ThreadQueueImplForKThreadSetProperty(KernelCore& kernel_, KThread::WaiterList* wl)
: KThreadQueue(kernel_), m_wait_list(wl) {}
void CancelWait(KThread* waiting_thread, ResultCode wait_result,
bool cancel_timer_task) override {
// Remove the thread from the wait list.
m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
private:
KThread::WaiterList* m_wait_list;
};
} // namespace
KThread::KThread(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_}, activity_pause_lock{kernel_} {}
KThread::~KThread() = default;
@@ -82,6 +113,8 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
[[fallthrough]];
case ThreadType::HighPriority:
[[fallthrough]];
case ThreadType::Dummy:
[[fallthrough]];
case ThreadType::User:
ASSERT(((owner == nullptr) ||
(owner->GetCoreMask() | (1ULL << virt_core)) == owner->GetCoreMask()));
@@ -127,11 +160,8 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
priority = prio;
base_priority = prio;
// Set sync object and waiting lock to null.
synced_object = nullptr;
// Initialize sleeping queue.
sleeping_queue = nullptr;
wait_queue = nullptr;
// Set suspend flags.
suspend_request_flags = 0;
@@ -184,7 +214,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
// Setup the stack parameters.
StackParameters& sp = GetStackParameters();
sp.cur_thread = this;
sp.disable_count = 1;
sp.disable_count = 0;
SetInExceptionHandler();
// Set thread ID.
@@ -211,15 +241,16 @@ ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uint
// Initialize the thread.
R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type));
// Initialize host context.
// Initialize emulation parameters.
thread->host_context =
std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter);
thread->is_single_core = !Settings::values.use_multi_core.GetValue();
return ResultSuccess;
}
ResultCode KThread::InitializeDummyThread(KThread* thread) {
return thread->Initialize({}, {}, {}, DefaultThreadPriority, 3, {}, ThreadType::Main);
return thread->Initialize({}, {}, {}, DefaultThreadPriority, 3, {}, ThreadType::Dummy);
}
ResultCode KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) {
@@ -273,11 +304,14 @@ void KThread::Finalize() {
auto it = waiter_list.begin();
while (it != waiter_list.end()) {
// The thread shouldn't be a kernel waiter.
// Clear the lock owner
it->SetLockOwner(nullptr);
it->SetSyncedObject(nullptr, ResultInvalidState);
it->Wakeup();
// Erase the waiter from our list.
it = waiter_list.erase(it);
// Cancel the thread's wait.
it->CancelWait(ResultInvalidState, true);
}
}
@@ -294,15 +328,12 @@ bool KThread::IsSignaled() const {
return signaled;
}
void KThread::Wakeup() {
KScopedSchedulerLock sl{kernel};
void KThread::OnTimer() {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// If we're waiting, cancel the wait.
if (GetState() == ThreadState::Waiting) {
if (sleeping_queue != nullptr) {
sleeping_queue->WakeupThread(this);
} else {
SetState(ThreadState::Runnable);
}
wait_queue->CancelWait(this, ResultTimedOut, false);
}
}
@@ -327,7 +358,7 @@ void KThread::StartTermination() {
// Signal.
signaled = true;
NotifyAvailable();
KSynchronizationObject::NotifyAvailable();
// Clear previous thread in KScheduler.
KScheduler::ClearPreviousThread(kernel, this);
@@ -475,30 +506,32 @@ ResultCode KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_m
return ResultSuccess;
}
ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
ResultCode KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) {
ASSERT(parent != nullptr);
ASSERT(v_affinity_mask != 0);
KScopedLightLock lk{activity_pause_lock};
KScopedLightLock lk(activity_pause_lock);
// Set the core mask.
u64 p_affinity_mask = 0;
{
KScopedSchedulerLock sl{kernel};
KScopedSchedulerLock sl(kernel);
ASSERT(num_core_migration_disables >= 0);
// If the core id is no-update magic, preserve the ideal core id.
if (cpu_core_id == Svc::IdealCoreNoUpdate) {
cpu_core_id = virtual_ideal_core_id;
R_UNLESS(((1ULL << cpu_core_id) & v_affinity_mask) != 0, ResultInvalidCombination);
// If we're updating, set our ideal virtual core.
if (core_id_ != Svc::IdealCoreNoUpdate) {
virtual_ideal_core_id = core_id_;
} else {
// Preserve our ideal core id.
core_id_ = virtual_ideal_core_id;
R_UNLESS(((1ULL << core_id_) & v_affinity_mask) != 0, ResultInvalidCombination);
}
// Set the virtual core/affinity mask.
virtual_ideal_core_id = cpu_core_id;
// Set our affinity mask.
virtual_affinity_mask = v_affinity_mask;
// Translate the virtual core to a physical core.
if (cpu_core_id >= 0) {
cpu_core_id = Core::Hardware::VirtualToPhysicalCoreMap[cpu_core_id];
if (core_id_ >= 0) {
core_id_ = Core::Hardware::VirtualToPhysicalCoreMap[core_id_];
}
// Translate the virtual affinity mask to a physical one.
@@ -513,7 +546,7 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
const KAffinityMask old_mask = physical_affinity_mask;
// Set our new ideals.
physical_ideal_core_id = cpu_core_id;
physical_ideal_core_id = core_id_;
physical_affinity_mask.SetAffinityMask(p_affinity_mask);
if (physical_affinity_mask.GetAffinityMask() != old_mask.GetAffinityMask()) {
@@ -531,18 +564,18 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
}
} else {
// Otherwise, we edit the original affinity for restoration later.
original_physical_ideal_core_id = cpu_core_id;
original_physical_ideal_core_id = core_id_;
original_physical_affinity_mask.SetAffinityMask(p_affinity_mask);
}
}
// Update the pinned waiter list.
ThreadQueueImplForKThreadSetProperty wait_queue_(kernel, std::addressof(pinned_waiter_list));
{
bool retry_update{};
bool thread_is_pinned{};
do {
// Lock the scheduler.
KScopedSchedulerLock sl{kernel};
KScopedSchedulerLock sl(kernel);
// Don't do any further management if our termination has been requested.
R_SUCCEED_IF(IsTerminationRequested());
@@ -570,12 +603,9 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(),
ResultTerminationRequested);
// Note that the thread was pinned.
thread_is_pinned = true;
// Wait until the thread isn't pinned any more.
pinned_waiter_list.push_back(GetCurrentThread(kernel));
GetCurrentThread(kernel).SetState(ThreadState::Waiting);
GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue_));
} else {
// If the thread isn't pinned, release the scheduler lock and retry until it's
// not current.
@@ -583,16 +613,6 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
}
}
} while (retry_update);
// If the thread was pinned, it no longer is, and we should remove the current thread from
// our waiter list.
if (thread_is_pinned) {
// Lock the scheduler.
KScopedSchedulerLock sl{kernel};
// Remove from the list.
pinned_waiter_list.erase(pinned_waiter_list.iterator_to(GetCurrentThread(kernel)));
}
}
return ResultSuccess;
@@ -641,15 +661,9 @@ void KThread::WaitCancel() {
KScopedSchedulerLock sl{kernel};
// Check if we're waiting and cancellable.
if (GetState() == ThreadState::Waiting && cancellable) {
if (sleeping_queue != nullptr) {
sleeping_queue->WakeupThread(this);
wait_cancelled = true;
} else {
SetSyncedObject(nullptr, ResultCancelled);
SetState(ThreadState::Runnable);
wait_cancelled = false;
}
if (this->GetState() == ThreadState::Waiting && cancellable) {
wait_cancelled = false;
wait_queue->CancelWait(this, ResultCancelled, true);
} else {
// Otherwise, note that we cancelled a wait.
wait_cancelled = true;
@@ -700,60 +714,59 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
// Set the activity.
{
// Lock the scheduler.
KScopedSchedulerLock sl{kernel};
KScopedSchedulerLock sl(kernel);
// Verify our state.
const auto cur_state = GetState();
const auto cur_state = this->GetState();
R_UNLESS((cur_state == ThreadState::Waiting || cur_state == ThreadState::Runnable),
ResultInvalidState);
// Either pause or resume.
if (activity == Svc::ThreadActivity::Paused) {
// Verify that we're not suspended.
R_UNLESS(!IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
R_UNLESS(!this->IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
// Suspend.
RequestSuspend(SuspendType::Thread);
this->RequestSuspend(SuspendType::Thread);
} else {
ASSERT(activity == Svc::ThreadActivity::Runnable);
// Verify that we're suspended.
R_UNLESS(IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
R_UNLESS(this->IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
// Resume.
Resume(SuspendType::Thread);
this->Resume(SuspendType::Thread);
}
}
// If the thread is now paused, update the pinned waiter list.
if (activity == Svc::ThreadActivity::Paused) {
bool thread_is_pinned{};
bool thread_is_current{};
ThreadQueueImplForKThreadSetProperty wait_queue_(kernel,
std::addressof(pinned_waiter_list));
bool thread_is_current;
do {
// Lock the scheduler.
KScopedSchedulerLock sl{kernel};
KScopedSchedulerLock sl(kernel);
// Don't do any further management if our termination has been requested.
R_SUCCEED_IF(IsTerminationRequested());
R_SUCCEED_IF(this->IsTerminationRequested());
// By default, treat the thread as not current.
thread_is_current = false;
// Check whether the thread is pinned.
if (GetStackParameters().is_pinned) {
if (this->GetStackParameters().is_pinned) {
// Verify that the current thread isn't terminating.
R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(),
ResultTerminationRequested);
// Note that the thread was pinned and not current.
thread_is_pinned = true;
thread_is_current = false;
// Wait until the thread isn't pinned any more.
pinned_waiter_list.push_back(GetCurrentThread(kernel));
GetCurrentThread(kernel).SetState(ThreadState::Waiting);
GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue_));
} else {
// Check if the thread is currently running.
// If it is, we'll need to retry.
thread_is_current = false;
for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
if (kernel.Scheduler(i).GetCurrentThread() == this) {
thread_is_current = true;
@@ -762,16 +775,6 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
}
}
} while (thread_is_current);
// If the thread was pinned, it no longer is, and we should remove the current thread from
// our waiter list.
if (thread_is_pinned) {
// Lock the scheduler.
KScopedSchedulerLock sl{kernel};
// Remove from the list.
pinned_waiter_list.erase(pinned_waiter_list.iterator_to(GetCurrentThread(kernel)));
}
}
return ResultSuccess;
@@ -966,6 +969,9 @@ ResultCode KThread::Run() {
// Set our state and finish.
SetState(ThreadState::Runnable);
DisableDispatch();
return ResultSuccess;
}
}
@@ -996,29 +1002,63 @@ ResultCode KThread::Sleep(s64 timeout) {
ASSERT(this == GetCurrentThreadPointer(kernel));
ASSERT(timeout > 0);
ThreadQueueImplForKThreadSleep wait_queue_(kernel);
{
// Setup the scheduling lock and sleep.
KScopedSchedulerLockAndSleep slp{kernel, this, timeout};
KScopedSchedulerLockAndSleep slp(kernel, this, timeout);
// Check if the thread should terminate.
if (IsTerminationRequested()) {
if (this->IsTerminationRequested()) {
slp.CancelSleep();
return ResultTerminationRequested;
}
// Mark the thread as waiting.
SetState(ThreadState::Waiting);
// Wait for the sleep to end.
this->BeginWait(std::addressof(wait_queue_));
SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep);
}
// The lock/sleep is done.
// Cancel the timer.
kernel.TimeManager().UnscheduleTimeEvent(this);
return ResultSuccess;
}
void KThread::BeginWait(KThreadQueue* queue) {
// Set our state as waiting.
SetState(ThreadState::Waiting);
// Set our wait queue.
wait_queue = queue;
}
void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) {
// Lock the scheduler.
KScopedSchedulerLock sl(kernel);
// If we're waiting, notify our queue that we're available.
if (GetState() == ThreadState::Waiting) {
wait_queue->NotifyAvailable(this, signaled_object, wait_result_);
}
}
void KThread::EndWait(ResultCode wait_result_) {
// Lock the scheduler.
KScopedSchedulerLock sl(kernel);
// If we're waiting, notify our queue that we're available.
if (GetState() == ThreadState::Waiting) {
wait_queue->EndWait(this, wait_result_);
}
}
void KThread::CancelWait(ResultCode wait_result_, bool cancel_timer_task) {
// Lock the scheduler.
KScopedSchedulerLock sl(kernel);
// If we're waiting, notify our queue that we're available.
if (GetState() == ThreadState::Waiting) {
wait_queue->CancelWait(this, wait_result_, cancel_timer_task);
}
}
void KThread::SetState(ThreadState state) {
KScopedSchedulerLock sl{kernel};
@@ -1050,4 +1090,26 @@ s32 GetCurrentCoreId(KernelCore& kernel) {
return GetCurrentThread(kernel).GetCurrentCore();
}
KScopedDisableDispatch::~KScopedDisableDispatch() {
// If we are shutting down the kernel, none of this is relevant anymore.
if (kernel.IsShuttingDown()) {
return;
}
// Skip the reschedule if single-core, as dispatch tracking is disabled here.
if (!Settings::values.use_multi_core.GetValue()) {
return;
}
if (GetCurrentThread(kernel).GetDisableDispatchCount() <= 1) {
auto scheduler = kernel.CurrentScheduler();
if (scheduler) {
scheduler->RescheduleCurrentCore();
}
} else {
GetCurrentThread(kernel).EnableDispatch();
}
}
} // namespace Kernel

View File

@@ -48,6 +48,7 @@ enum class ThreadType : u32 {
Kernel = 1,
HighPriority = 2,
User = 3,
Dummy = 100, // Special thread type for emulation purposes only
};
DECLARE_ENUM_FLAG_OPERATORS(ThreadType);
@@ -161,8 +162,6 @@ public:
}
}
void Wakeup();
void SetBasePriority(s32 value);
[[nodiscard]] ResultCode Run();
@@ -197,13 +196,19 @@ public:
void Suspend();
void SetSyncedObject(KSynchronizationObject* obj, ResultCode wait_res) {
synced_object = obj;
constexpr void SetSyncedIndex(s32 index) {
synced_index = index;
}
[[nodiscard]] constexpr s32 GetSyncedIndex() const {
return synced_index;
}
constexpr void SetWaitResult(ResultCode wait_res) {
wait_result = wait_res;
}
[[nodiscard]] ResultCode GetWaitResult(KSynchronizationObject** out) const {
*out = synced_object;
[[nodiscard]] constexpr ResultCode GetWaitResult() const {
return wait_result;
}
@@ -374,6 +379,8 @@ public:
[[nodiscard]] bool IsSignaled() const override;
void OnTimer();
static void PostDestroy(uintptr_t arg);
[[nodiscard]] static ResultCode InitializeDummyThread(KThread* thread);
@@ -446,20 +453,39 @@ public:
return per_core_priority_queue_entry[core];
}
void SetSleepingQueue(KThreadQueue* q) {
sleeping_queue = q;
[[nodiscard]] bool IsKernelThread() const {
return GetActiveCore() == 3;
}
[[nodiscard]] bool IsDispatchTrackingDisabled() const {
return is_single_core || IsKernelThread();
}
[[nodiscard]] s32 GetDisableDispatchCount() const {
if (IsDispatchTrackingDisabled()) {
// TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
return 1;
}
return this->GetStackParameters().disable_count;
}
void DisableDispatch() {
if (IsDispatchTrackingDisabled()) {
// TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
return;
}
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
this->GetStackParameters().disable_count++;
}
void EnableDispatch() {
if (IsDispatchTrackingDisabled()) {
// TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
return;
}
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0);
this->GetStackParameters().disable_count--;
}
@@ -573,6 +599,15 @@ public:
address_key_value = val;
}
void ClearWaitQueue() {
wait_queue = nullptr;
}
void BeginWait(KThreadQueue* queue);
void NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_);
void EndWait(ResultCode wait_result_);
void CancelWait(ResultCode wait_result_, bool cancel_timer_task);
[[nodiscard]] bool HasWaiters() const {
return !waiter_list.empty();
}
@@ -667,7 +702,6 @@ private:
KAffinityMask physical_affinity_mask{};
u64 thread_id{};
std::atomic<s64> cpu_time{};
KSynchronizationObject* synced_object{};
VAddr address_key{};
KProcess* parent{};
VAddr kernel_stack_top{};
@@ -677,13 +711,14 @@ private:
s64 schedule_count{};
s64 last_scheduled_tick{};
std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{};
KThreadQueue* sleeping_queue{};
KThreadQueue* wait_queue{};
WaiterList waiter_list{};
WaiterList pinned_waiter_list{};
KThread* lock_owner{};
u32 address_key_value{};
u32 suspend_request_flags{};
u32 suspend_allowed_flags{};
s32 synced_index{};
ResultCode wait_result{ResultSuccess};
s32 base_priority{};
s32 physical_ideal_core_id{};
@@ -708,6 +743,7 @@ private:
// For emulation
std::shared_ptr<Common::Fiber> host_context{};
bool is_single_core{};
// For debugging
std::vector<KSynchronizationObject*> wait_objects_for_debugging;
@@ -752,4 +788,20 @@ public:
}
};
class KScopedDisableDispatch {
public:
[[nodiscard]] explicit KScopedDisableDispatch(KernelCore& kernel_) : kernel{kernel_} {
// If we are shutting down the kernel, none of this is relevant anymore.
if (kernel.IsShuttingDown()) {
return;
}
GetCurrentThread(kernel).DisableDispatch();
}
~KScopedDisableDispatch();
private:
KernelCore& kernel;
};
} // namespace Kernel

View File

@@ -0,0 +1,49 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/time_manager.h"
namespace Kernel {
void KThreadQueue::NotifyAvailable([[maybe_unused]] KThread* waiting_thread,
[[maybe_unused]] KSynchronizationObject* signaled_object,
[[maybe_unused]] ResultCode wait_result) {}
void KThreadQueue::EndWait(KThread* waiting_thread, ResultCode wait_result) {
// Set the thread's wait result.
waiting_thread->SetWaitResult(wait_result);
// Set the thread as runnable.
waiting_thread->SetState(ThreadState::Runnable);
// Clear the thread's wait queue.
waiting_thread->ClearWaitQueue();
// Cancel the thread task.
kernel.TimeManager().UnscheduleTimeEvent(waiting_thread);
}
void KThreadQueue::CancelWait(KThread* waiting_thread, ResultCode wait_result,
bool cancel_timer_task) {
// Set the thread's wait result.
waiting_thread->SetWaitResult(wait_result);
// Set the thread as runnable.
waiting_thread->SetState(ThreadState::Runnable);
// Clear the thread's wait queue.
waiting_thread->ClearWaitQueue();
// Cancel the thread task.
if (cancel_timer_task) {
kernel.TimeManager().UnscheduleTimeEvent(waiting_thread);
}
}
void KThreadQueueWithoutEndWait::EndWait([[maybe_unused]] KThread* waiting_thread,
[[maybe_unused]] ResultCode wait_result) {}
} // namespace Kernel

View File

@@ -4,6 +4,7 @@
#pragma once
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
namespace Kernel {
@@ -11,71 +12,24 @@ namespace Kernel {
class KThreadQueue {
public:
explicit KThreadQueue(KernelCore& kernel_) : kernel{kernel_} {}
virtual ~KThreadQueue() = default;
bool IsEmpty() const {
return wait_list.empty();
}
KThread::WaiterList::iterator begin() {
return wait_list.begin();
}
KThread::WaiterList::iterator end() {
return wait_list.end();
}
bool SleepThread(KThread* t) {
KScopedSchedulerLock sl{kernel};
// If the thread needs terminating, don't enqueue it.
if (t->IsTerminationRequested()) {
return false;
}
// Set the thread's queue and mark it as waiting.
t->SetSleepingQueue(this);
t->SetState(ThreadState::Waiting);
// Add the thread to the queue.
wait_list.push_back(*t);
return true;
}
void WakeupThread(KThread* t) {
KScopedSchedulerLock sl{kernel};
// Remove the thread from the queue.
wait_list.erase(wait_list.iterator_to(*t));
// Mark the thread as no longer sleeping.
t->SetState(ThreadState::Runnable);
t->SetSleepingQueue(nullptr);
}
KThread* WakeupFrontThread() {
KScopedSchedulerLock sl{kernel};
if (wait_list.empty()) {
return nullptr;
} else {
// Remove the thread from the queue.
auto it = wait_list.begin();
KThread* thread = std::addressof(*it);
wait_list.erase(it);
ASSERT(thread->GetState() == ThreadState::Waiting);
// Mark the thread as no longer sleeping.
thread->SetState(ThreadState::Runnable);
thread->SetSleepingQueue(nullptr);
return thread;
}
}
virtual void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object,
ResultCode wait_result);
virtual void EndWait(KThread* waiting_thread, ResultCode wait_result);
virtual void CancelWait(KThread* waiting_thread, ResultCode wait_result,
bool cancel_timer_task);
private:
KernelCore& kernel;
KThread::WaiterList wait_list{};
};
class KThreadQueueWithoutEndWait : public KThreadQueue {
public:
explicit KThreadQueueWithoutEndWait(KernelCore& kernel_) : KThreadQueue(kernel_) {}
void EndWait(KThread* waiting_thread, ResultCode wait_result) override final;
};
} // namespace Kernel

View File

@@ -14,6 +14,7 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "common/scope_exit.h"
#include "common/thread.h"
#include "common/thread_worker.h"
#include "core/arm/arm_interface.h"
@@ -83,12 +84,16 @@ struct KernelCore::Impl {
}
void InitializeCores() {
for (auto& core : cores) {
core.Initialize(current_process->Is64BitProcess());
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
cores[core_id].Initialize(current_process->Is64BitProcess());
system.Memory().SetCurrentPageTable(*current_process, core_id);
}
}
void Shutdown() {
is_shutting_down.store(true, std::memory_order_relaxed);
SCOPE_EXIT({ is_shutting_down.store(false, std::memory_order_relaxed); });
process_list.clear();
// Close all open server ports.
@@ -123,15 +128,6 @@ struct KernelCore::Impl {
next_user_process_id = KProcess::ProcessIDMin;
next_thread_id = 1;
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
if (suspend_threads[core_id]) {
suspend_threads[core_id]->Close();
suspend_threads[core_id] = nullptr;
}
schedulers[core_id].reset();
}
cores.clear();
global_handle_table->Finalize();
@@ -159,6 +155,16 @@ struct KernelCore::Impl {
CleanupObject(time_shared_mem);
CleanupObject(system_resource_limit);
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
if (suspend_threads[core_id]) {
suspend_threads[core_id]->Close();
suspend_threads[core_id] = nullptr;
}
schedulers[core_id]->Finalize();
schedulers[core_id].reset();
}
// Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
next_host_thread_id = Core::Hardware::NUM_CPU_CORES;
@@ -245,13 +251,11 @@ struct KernelCore::Impl {
KScopedSchedulerLock lock(kernel);
global_scheduler_context->PreemptThreads();
}
const auto time_interval = std::chrono::nanoseconds{
Core::Timing::msToCycles(std::chrono::milliseconds(10))};
const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)};
system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
});
const auto time_interval =
std::chrono::nanoseconds{Core::Timing::msToCycles(std::chrono::milliseconds(10))};
const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)};
system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
}
@@ -267,14 +271,6 @@ struct KernelCore::Impl {
void MakeCurrentProcess(KProcess* process) {
current_process = process;
if (process == nullptr) {
return;
}
const u32 core_id = GetCurrentHostThreadID();
if (core_id < Core::Hardware::NUM_CPU_CORES) {
system.Memory().SetCurrentPageTable(*process, core_id);
}
}
static inline thread_local u32 host_thread_id = UINT32_MAX;
@@ -344,7 +340,16 @@ struct KernelCore::Impl {
is_phantom_mode_for_singlecore = value;
}
bool IsShuttingDown() const {
return is_shutting_down.load(std::memory_order_relaxed);
}
KThread* GetCurrentEmuThread() {
// If we are shutting down the kernel, none of this is relevant anymore.
if (IsShuttingDown()) {
return {};
}
const auto thread_id = GetCurrentHostThreadID();
if (thread_id >= Core::Hardware::NUM_CPU_CORES) {
return GetHostDummyThread();
@@ -760,6 +765,7 @@ struct KernelCore::Impl {
std::vector<std::unique_ptr<KThread>> dummy_threads;
bool is_multicore{};
std::atomic_bool is_shutting_down{};
bool is_phantom_mode_for_singlecore{};
u32 single_core_thread_id{};
@@ -845,16 +851,20 @@ const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const {
return impl->cores[id];
}
size_t KernelCore::CurrentPhysicalCoreIndex() const {
const u32 core_id = impl->GetCurrentHostThreadID();
if (core_id >= Core::Hardware::NUM_CPU_CORES) {
return Core::Hardware::NUM_CPU_CORES - 1;
}
return core_id;
}
Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() {
u32 core_id = impl->GetCurrentHostThreadID();
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
return impl->cores[core_id];
return impl->cores[CurrentPhysicalCoreIndex()];
}
const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const {
u32 core_id = impl->GetCurrentHostThreadID();
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
return impl->cores[core_id];
return impl->cores[CurrentPhysicalCoreIndex()];
}
Kernel::KScheduler* KernelCore::CurrentScheduler() {
@@ -1057,6 +1067,9 @@ void KernelCore::Suspend(bool in_suspention) {
impl->suspend_threads[core_id]->SetState(state);
impl->suspend_threads[core_id]->SetWaitReasonForDebugging(
ThreadWaitReasonForDebugging::Suspended);
if (!should_suspend) {
impl->suspend_threads[core_id]->DisableDispatch();
}
}
}
}
@@ -1065,19 +1078,21 @@ bool KernelCore::IsMulticore() const {
return impl->is_multicore;
}
bool KernelCore::IsShuttingDown() const {
return impl->IsShuttingDown();
}
void KernelCore::ExceptionalExit() {
exception_exited = true;
Suspend(true);
}
void KernelCore::EnterSVCProfile() {
std::size_t core = impl->GetCurrentHostThreadID();
impl->svc_ticks[core] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC));
impl->svc_ticks[CurrentPhysicalCoreIndex()] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC));
}
void KernelCore::ExitSVCProfile() {
std::size_t core = impl->GetCurrentHostThreadID();
MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]);
MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[CurrentPhysicalCoreIndex()]);
}
std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) {

View File

@@ -53,6 +53,7 @@ class KSharedMemoryInfo;
class KThread;
class KTransferMemory;
class KWritableEvent;
class KCodeMemory;
class PhysicalCore;
class ServiceThread;
class Synchronization;
@@ -148,6 +149,9 @@ public:
/// Gets the an instance of the respective physical CPU core.
const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const;
/// Gets the current physical core index for the running host thread.
std::size_t CurrentPhysicalCoreIndex() const;
/// Gets the sole instance of the Scheduler at the current running core.
Kernel::KScheduler* CurrentScheduler();
@@ -271,6 +275,8 @@ public:
bool IsMulticore() const;
bool IsShuttingDown() const;
void EnterSVCProfile();
void ExitSVCProfile();
@@ -326,6 +332,8 @@ public:
return slab_heap_container->transfer_memory;
} else if constexpr (std::is_same_v<T, KWritableEvent>) {
return slab_heap_container->writeable_event;
} else if constexpr (std::is_same_v<T, KCodeMemory>) {
return slab_heap_container->code_memory;
}
}
@@ -377,6 +385,7 @@ private:
KSlabHeap<KThread> thread;
KSlabHeap<KTransferMemory> transfer_memory;
KSlabHeap<KWritableEvent> writeable_event;
KSlabHeap<KCodeMemory> code_memory;
};
std::unique_ptr<SlabHeapContainer> slab_heap_container;

View File

@@ -25,24 +25,27 @@ public:
void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context);
private:
std::vector<std::thread> threads;
std::vector<std::jthread> threads;
std::queue<std::function<void()>> requests;
std::mutex queue_mutex;
std::condition_variable condition;
std::condition_variable_any condition;
const std::string service_name;
bool stop{};
};
ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name)
: service_name{name} {
for (std::size_t i = 0; i < num_threads; ++i)
threads.emplace_back([this, &kernel] {
for (std::size_t i = 0; i < num_threads; ++i) {
threads.emplace_back([this, &kernel](std::stop_token stop_token) {
Common::SetCurrentThreadName(std::string{"yuzu:HleService:" + service_name}.c_str());
// Wait for first request before trying to acquire a render context
{
std::unique_lock lock{queue_mutex};
condition.wait(lock, [this] { return stop || !requests.empty(); });
condition.wait(lock, stop_token, [this] { return !requests.empty(); });
}
if (stop_token.stop_requested()) {
return;
}
kernel.RegisterHostThread();
@@ -52,10 +55,16 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std
{
std::unique_lock lock{queue_mutex};
condition.wait(lock, [this] { return stop || !requests.empty(); });
if (stop || requests.empty()) {
condition.wait(lock, stop_token, [this] { return !requests.empty(); });
if (stop_token.stop_requested()) {
return;
}
if (requests.empty()) {
continue;
}
task = std::move(requests.front());
requests.pop();
}
@@ -63,6 +72,7 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std
task();
}
});
}
}
void ServiceThread::Impl::QueueSyncRequest(KSession& session,
@@ -88,12 +98,9 @@ void ServiceThread::Impl::QueueSyncRequest(KSession& session,
}
ServiceThread::Impl::~Impl() {
{
std::unique_lock lock{queue_mutex};
stop = true;
}
condition.notify_all();
for (std::thread& thread : threads) {
for (auto& thread : threads) {
thread.request_stop();
thread.join();
}
}

View File

@@ -18,6 +18,7 @@
#include "core/core_timing.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_client_session.h"
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_handle_table.h"
#include "core/hle/kernel/k_memory_block.h"
@@ -31,6 +32,7 @@
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/kernel/kernel.h"
@@ -307,26 +309,29 @@ static ResultCode ConnectToNamedPort32(Core::System& system, Handle* out_handle,
/// Makes a blocking IPC call to an OS service.
static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
auto& kernel = system.Kernel();
// Create the wait queue.
KThreadQueue wait_queue(kernel);
// Get the client session from its handle.
KScopedAutoObject session =
kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle);
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
auto thread = kernel.CurrentScheduler()->GetCurrentThread();
{
KScopedSchedulerLock lock(kernel);
thread->SetState(ThreadState::Waiting);
thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
{
KScopedAutoObject session =
kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle);
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
session->SendSyncRequest(thread, system.Memory(), system.CoreTiming());
}
// This is a synchronous request, so we should wait for our request to complete.
GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue));
GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
session->SendSyncRequest(&GetCurrentThread(kernel), system.Memory(), system.CoreTiming());
}
KSynchronizationObject* dummy{};
return thread->GetWaitResult(std::addressof(dummy));
return thread->GetWaitResult();
}
static ResultCode SendSyncRequest32(Core::System& system, Handle handle) {
@@ -873,7 +878,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
const u64 thread_ticks = current_thread->GetCpuTime();
out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
} else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
} else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks;
}
@@ -887,7 +892,8 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
return ResultInvalidHandle;
}
if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id != system.CurrentCoreIndex()) {
if (info_sub_id != 0xFFFFFFFFFFFFFFFF &&
info_sub_id != system.Kernel().CurrentPhysicalCoreIndex()) {
LOG_ERROR(Kernel_SVC, "Core is not the current core, got {}", info_sub_id);
return ResultInvalidCombination;
}
@@ -1197,6 +1203,22 @@ constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) {
}
}
constexpr bool IsValidMapCodeMemoryPermission(Svc::MemoryPermission perm) {
return perm == Svc::MemoryPermission::ReadWrite;
}
constexpr bool IsValidMapToOwnerCodeMemoryPermission(Svc::MemoryPermission perm) {
return perm == Svc::MemoryPermission::Read || perm == Svc::MemoryPermission::ReadExecute;
}
constexpr bool IsValidUnmapCodeMemoryPermission(Svc::MemoryPermission perm) {
return perm == Svc::MemoryPermission::None;
}
constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(Svc::MemoryPermission perm) {
return perm == Svc::MemoryPermission::None;
}
} // Anonymous namespace
static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address,
@@ -1306,6 +1328,195 @@ static ResultCode SetProcessMemoryPermission(Core::System& system, Handle proces
return page_table.SetProcessMemoryPermission(address, size, ConvertToKMemoryPermission(perm));
}
static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
VAddr src_address, u64 size) {
LOG_TRACE(Kernel_SVC,
"called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
dst_address, process_handle, src_address, size);
// Validate the address/size.
R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
R_UNLESS(size > 0, ResultInvalidSize);
R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
// Get the processes.
KProcess* dst_process = system.CurrentProcess();
KScopedAutoObject src_process =
dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
// Get the page tables.
auto& dst_pt = dst_process->PageTable();
auto& src_pt = src_process->PageTable();
// Validate that the mapping is in range.
R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
ResultInvalidMemoryRegion);
// Create a new page group.
KMemoryInfo kBlockInfo = dst_pt.QueryInfo(dst_address);
KPageLinkedList pg(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages());
// Map the group.
R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode,
KMemoryPermission::UserReadWrite));
return ResultSuccess;
}
static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
VAddr src_address, u64 size) {
LOG_TRACE(Kernel_SVC,
"called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
dst_address, process_handle, src_address, size);
// Validate the address/size.
R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
R_UNLESS(size > 0, ResultInvalidSize);
R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
// Get the processes.
KProcess* dst_process = system.CurrentProcess();
KScopedAutoObject src_process =
dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
// Get the page tables.
auto& dst_pt = dst_process->PageTable();
auto& src_pt = src_process->PageTable();
// Validate that the mapping is in range.
R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
ResultInvalidMemoryRegion);
// Unmap the memory.
R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address));
return ResultSuccess;
}
static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
LOG_TRACE(Kernel_SVC, "called, handle_out=0x{:X}, address=0x{:X}, size=0x{:X}",
static_cast<void*>(out), address, size);
// Get kernel instance.
auto& kernel = system.Kernel();
// Validate address / size.
R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
R_UNLESS(size > 0, ResultInvalidSize);
R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
// Create the code memory.
KCodeMemory* code_mem = KCodeMemory::Create(kernel);
R_UNLESS(code_mem != nullptr, ResultOutOfResource);
// Verify that the region is in range.
R_UNLESS(system.CurrentProcess()->PageTable().Contains(address, size),
ResultInvalidCurrentMemory);
// Initialize the code memory.
R_TRY(code_mem->Initialize(system.DeviceMemory(), address, size));
// Register the code memory.
KCodeMemory::Register(kernel, code_mem);
// Add the code memory to the handle table.
R_TRY(system.CurrentProcess()->GetHandleTable().Add(out, code_mem));
code_mem->Close();
return ResultSuccess;
}
static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation,
VAddr address, size_t size, Svc::MemoryPermission perm) {
LOG_TRACE(Kernel_SVC,
"called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, "
"permission=0x{:X}",
code_memory_handle, operation, address, size, perm);
// Validate the address / size.
R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
R_UNLESS(size > 0, ResultInvalidSize);
R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
// Get the code memory from its handle.
KScopedAutoObject code_mem =
system.CurrentProcess()->GetHandleTable().GetObject<KCodeMemory>(code_memory_handle);
R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle);
// NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process.
// This enables homebrew usage of these SVCs for JIT.
// Perform the operation.
switch (static_cast<CodeMemoryOperation>(operation)) {
case CodeMemoryOperation::Map: {
// Check that the region is in range.
R_UNLESS(
system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
ResultInvalidMemoryRegion);
// Check the memory permission.
R_UNLESS(IsValidMapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
// Map the memory.
R_TRY(code_mem->Map(address, size));
} break;
case CodeMemoryOperation::Unmap: {
// Check that the region is in range.
R_UNLESS(
system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
ResultInvalidMemoryRegion);
// Check the memory permission.
R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
// Unmap the memory.
R_TRY(code_mem->Unmap(address, size));
} break;
case CodeMemoryOperation::MapToOwner: {
// Check that the region is in range.
R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
KMemoryState::GeneratedCode),
ResultInvalidMemoryRegion);
// Check the memory permission.
R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
// Map the memory to its owner.
R_TRY(code_mem->MapToOwner(address, size, perm));
} break;
case CodeMemoryOperation::UnmapFromOwner: {
// Check that the region is in range.
R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
KMemoryState::GeneratedCode),
ResultInvalidMemoryRegion);
// Check the memory permission.
R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
// Unmap the memory from its owner.
R_TRY(code_mem->UnmapFromOwner(address, size));
} break;
default:
return ResultInvalidEnumValue;
}
return ResultSuccess;
}
static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address,
VAddr page_info_address, Handle process_handle,
VAddr address) {
@@ -2600,8 +2811,8 @@ static const FunctionDef SVC_Table_64[] = {
{0x48, nullptr, "MapPhysicalMemoryUnsafe"},
{0x49, nullptr, "UnmapPhysicalMemoryUnsafe"},
{0x4A, nullptr, "SetUnsafeLimit"},
{0x4B, nullptr, "CreateCodeMemory"},
{0x4C, nullptr, "ControlCodeMemory"},
{0x4B, SvcWrap64<CreateCodeMemory>, "CreateCodeMemory"},
{0x4C, SvcWrap64<ControlCodeMemory>, "ControlCodeMemory"},
{0x4D, nullptr, "SleepSystem"},
{0x4E, nullptr, "ReadWriteRegister"},
{0x4F, nullptr, "SetProcessActivity"},
@@ -2641,8 +2852,8 @@ static const FunctionDef SVC_Table_64[] = {
{0x71, nullptr, "ManageNamedPort"},
{0x72, nullptr, "ConnectToPort"},
{0x73, SvcWrap64<SetProcessMemoryPermission>, "SetProcessMemoryPermission"},
{0x74, nullptr, "MapProcessMemory"},
{0x75, nullptr, "UnmapProcessMemory"},
{0x74, SvcWrap64<MapProcessMemory>, "MapProcessMemory"},
{0x75, SvcWrap64<UnmapProcessMemory>, "UnmapProcessMemory"},
{0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"},
{0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"},
{0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"},

View File

@@ -73,6 +73,23 @@ void SvcWrap64(Core::System& system) {
.raw);
}
// Used by MapProcessMemory and UnmapProcessMemory
template <ResultCode func(Core::System&, u64, u32, u64, u64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
Param(system, 2), Param(system, 3))
.raw);
}
// Used by ControlCodeMemory
template <ResultCode func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)),
static_cast<u32>(Param(system, 1)), Param(system, 2), Param(system, 3),
static_cast<Svc::MemoryPermission>(Param(system, 4)))
.raw);
}
template <ResultCode func(Core::System&, u32*)>
void SvcWrap64(Core::System& system) {
u32 param = 0;
@@ -301,6 +318,16 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
// Used by CreateCodeMemory
template <ResultCode func(Core::System&, Handle*, u64, u64)>
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2)).raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
}
template <ResultCode func(Core::System&, Handle*, u64, u32, u32)>
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;

View File

@@ -5,6 +5,7 @@
#include "common/assert.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/time_manager.h"
@@ -15,7 +16,10 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} {
Core::Timing::CreateEvent("Kernel::TimeManagerCallback",
[this](std::uintptr_t thread_handle, std::chrono::nanoseconds) {
KThread* thread = reinterpret_cast<KThread*>(thread_handle);
thread->Wakeup();
{
KScopedSchedulerLock sl(system.Kernel());
thread->OnTimer();
}
});
}

View File

@@ -8,6 +8,7 @@
#include "core/hle/service/glue/bgtc.h"
#include "core/hle/service/glue/ectx.h"
#include "core/hle/service/glue/glue.h"
#include "core/hle/service/glue/notif.h"
namespace Service::Glue {
@@ -24,6 +25,9 @@ void InstallInterfaces(Core::System& system) {
// Error Context
std::make_shared<ECTX_AW>(system)->InstallAsService(system.ServiceManager());
// Notification Services for application
std::make_shared<NOTIF_A>(system)->InstallAsService(system.ServiceManager());
}
} // namespace Service::Glue

View File

@@ -0,0 +1,44 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/glue/notif.h"
namespace Service::Glue {
NOTIF_A::NOTIF_A(Core::System& system_) : ServiceFramework{system_, "notif:a"} {
// clang-format off
static const FunctionInfo functions[] = {
{500, nullptr, "RegisterAlarmSetting"},
{510, nullptr, "UpdateAlarmSetting"},
{520, &NOTIF_A::ListAlarmSettings, "ListAlarmSettings"},
{530, nullptr, "LoadApplicationParameter"},
{540, nullptr, "DeleteAlarmSetting"},
{1000, &NOTIF_A::Initialize, "Initialize"},
};
// clang-format on
RegisterHandlers(functions);
}
NOTIF_A::~NOTIF_A() = default;
void NOTIF_A::ListAlarmSettings(Kernel::HLERequestContext& ctx) {
// Returns an array of AlarmSetting
constexpr s32 alarm_count = 0;
LOG_WARNING(Service_NOTIF, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(alarm_count);
}
void NOTIF_A::Initialize(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NOTIF, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
} // namespace Service::Glue

View File

@@ -0,0 +1,25 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::Glue {
class NOTIF_A final : public ServiceFramework<NOTIF_A> {
public:
explicit NOTIF_A(Core::System& system_);
~NOTIF_A() override;
private:
void ListAlarmSettings(Kernel::HLERequestContext& ctx);
void Initialize(Kernel::HLERequestContext& ctx);
};
} // namespace Service::Glue

View File

@@ -110,7 +110,7 @@ void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type,
UpdateControllerAt(npad_type, npad_id, is_connected);
break;
case Core::HID::ControllerTriggerType::Battery: {
if (!controller.is_connected) {
if (!controller.device->IsConnected()) {
return;
}
auto& shared_memory = controller.shared_memory_entry;
@@ -126,8 +126,11 @@ void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type,
}
void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
LOG_DEBUG(Service_HID, "Npad connected {}", npad_id);
auto& controller = GetControllerFromNpadIdType(npad_id);
if (!IsControllerSupported(controller.device->GetNpadStyleIndex())) {
return;
}
LOG_DEBUG(Service_HID, "Npad connected {}", npad_id);
const auto controller_type = controller.device->GetNpadStyleIndex();
auto& shared_memory = controller.shared_memory_entry;
if (controller_type == Core::HID::NpadStyleIndex::None) {
@@ -147,7 +150,6 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
shared_memory.system_properties.is_vertical.Assign(1);
shared_memory.system_properties.use_plus.Assign(1);
shared_memory.system_properties.use_minus.Assign(1);
shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
shared_memory.applet_footer.type = AppletFooterUiType::SwitchProController;
break;
case Core::HID::NpadStyleIndex::Handheld:
@@ -163,21 +165,30 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
break;
case Core::HID::NpadStyleIndex::JoyconDual:
shared_memory.style_tag.joycon_dual.Assign(1);
shared_memory.device_type.joycon_left.Assign(1);
shared_memory.device_type.joycon_right.Assign(1);
shared_memory.system_properties.is_vertical.Assign(1);
shared_memory.system_properties.use_plus.Assign(1);
shared_memory.system_properties.use_minus.Assign(1);
if (controller.is_dual_left_connected) {
shared_memory.device_type.joycon_left.Assign(1);
shared_memory.system_properties.use_minus.Assign(1);
}
if (controller.is_dual_right_connected) {
shared_memory.device_type.joycon_right.Assign(1);
shared_memory.system_properties.use_plus.Assign(1);
}
shared_memory.system_properties.use_directional_buttons.Assign(1);
shared_memory.system_properties.is_vertical.Assign(1);
shared_memory.assignment_mode = NpadJoyAssignmentMode::Dual;
shared_memory.applet_footer.type = AppletFooterUiType::JoyDual;
if (controller.is_dual_left_connected && controller.is_dual_right_connected) {
shared_memory.applet_footer.type = AppletFooterUiType::JoyDual;
} else if (controller.is_dual_left_connected) {
shared_memory.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly;
} else {
shared_memory.applet_footer.type = AppletFooterUiType::JoyDualRightOnly;
}
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
shared_memory.style_tag.joycon_left.Assign(1);
shared_memory.device_type.joycon_left.Assign(1);
shared_memory.system_properties.is_horizontal.Assign(1);
shared_memory.system_properties.use_minus.Assign(1);
shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
shared_memory.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal;
break;
case Core::HID::NpadStyleIndex::JoyconRight:
@@ -185,7 +196,6 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
shared_memory.device_type.joycon_right.Assign(1);
shared_memory.system_properties.is_horizontal.Assign(1);
shared_memory.system_properties.use_plus.Assign(1);
shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
shared_memory.applet_footer.type = AppletFooterUiType::JoyRightHorizontal;
break;
case Core::HID::NpadStyleIndex::GameCube:
@@ -197,7 +207,6 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
case Core::HID::NpadStyleIndex::Pokeball:
shared_memory.style_tag.palma.Assign(1);
shared_memory.device_type.palma.Assign(1);
shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
break;
case Core::HID::NpadStyleIndex::NES:
shared_memory.style_tag.lark.Assign(1);
@@ -255,19 +264,7 @@ void Controller_NPad::OnInit() {
if (hid_core.GetSupportedStyleTag().raw == Core::HID::NpadStyleSet::None) {
// We want to support all controllers
Core::HID::NpadStyleTag style{};
style.handheld.Assign(1);
style.joycon_left.Assign(1);
style.joycon_right.Assign(1);
style.joycon_dual.Assign(1);
style.fullkey.Assign(1);
style.gamecube.Assign(1);
style.palma.Assign(1);
style.lark.Assign(1);
style.lucia.Assign(1);
style.lagoon.Assign(1);
style.lager.Assign(1);
hid_core.SetSupportedStyleTag(style);
hid_core.SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
}
supported_npad_id_types.resize(npad_id_list.size());
@@ -452,11 +449,15 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
case Core::HID::NpadStyleIndex::JoyconDual:
pad_state.connection_status.raw = 0;
pad_state.connection_status.is_connected.Assign(1);
pad_state.connection_status.is_left_connected.Assign(1);
pad_state.connection_status.is_right_connected.Assign(1);
if (controller.is_dual_left_connected) {
pad_state.connection_status.is_left_connected.Assign(1);
libnx_state.connection_status.is_left_connected.Assign(1);
}
if (controller.is_dual_right_connected) {
pad_state.connection_status.is_right_connected.Assign(1);
libnx_state.connection_status.is_right_connected.Assign(1);
}
libnx_state.connection_status.is_left_connected.Assign(1);
libnx_state.connection_status.is_right_connected.Assign(1);
pad_state.sampling_number =
npad.joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1;
npad.joy_dual_lifo.WriteNextEntry(pad_state);
@@ -696,7 +697,7 @@ Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode
return communication_mode;
}
void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id,
void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
NpadJoyAssignmentMode assignment_mode) {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
@@ -707,6 +708,62 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id,
if (controller.shared_memory_entry.assignment_mode != assignment_mode) {
controller.shared_memory_entry.assignment_mode = assignment_mode;
}
if (!controller.device->IsConnected()) {
return;
}
if (assignment_mode == NpadJoyAssignmentMode::Dual) {
if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft) {
DisconnectNpad(npad_id);
controller.is_dual_left_connected = true;
controller.is_dual_right_connected = false;
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
return;
}
if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) {
DisconnectNpad(npad_id);
controller.is_dual_left_connected = false;
controller.is_dual_right_connected = true;
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
return;
}
return;
}
// This is for NpadJoyAssignmentMode::Single
// Only JoyconDual get affected by this function
if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::JoyconDual) {
return;
}
if (controller.is_dual_left_connected && !controller.is_dual_right_connected) {
DisconnectNpad(npad_id);
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
return;
}
if (!controller.is_dual_left_connected && controller.is_dual_right_connected) {
DisconnectNpad(npad_id);
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
return;
}
// We have two controllers connected to the same npad_id we need to split them
const auto npad_id_2 = hid_core.GetFirstDisconnectedNpadId();
auto& controller_2 = GetControllerFromNpadIdType(npad_id_2);
DisconnectNpad(npad_id);
if (npad_device_type == NpadJoyDeviceType::Left) {
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
controller_2.is_dual_left_connected = false;
controller_2.is_dual_right_connected = true;
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true);
} else {
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
controller_2.is_dual_left_connected = true;
controller_2.is_dual_right_connected = false;
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true);
}
}
bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
@@ -916,6 +973,7 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
}
auto& shared_memory_entry = controller.shared_memory_entry;
// Don't reset shared_memory_entry.assignment_mode this value is persistent
shared_memory_entry.style_tag.raw = Core::HID::NpadStyleSet::None; // Zero out
shared_memory_entry.device_type.raw = 0;
shared_memory_entry.system_properties.raw = 0;
@@ -932,9 +990,10 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
.left = {},
.right = {},
};
shared_memory_entry.assignment_mode = NpadJoyAssignmentMode::Dual;
shared_memory_entry.applet_footer.type = AppletFooterUiType::None;
controller.is_dual_left_connected = true;
controller.is_dual_right_connected = true;
controller.is_connected = false;
controller.device->Disconnect();
SignalStyleSetChangedEvent(npad_id);
@@ -1031,19 +1090,70 @@ void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
npad_id_2);
return;
}
auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device;
auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device;
auto& controller_1 = GetControllerFromNpadIdType(npad_id_1);
auto& controller_2 = GetControllerFromNpadIdType(npad_id_2);
const auto controller_style_1 = controller_1.device->GetNpadStyleIndex();
const auto controller_style_2 = controller_2.device->GetNpadStyleIndex();
bool merge_controllers = false;
// If the controllers at both npad indices form a pair of left and right joycons, merge them.
// Otherwise, do nothing.
if ((controller_1->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft &&
controller_2->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) ||
(controller_2->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft &&
controller_1->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight)) {
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight) {
merge_controllers = true;
}
if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft &&
controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight) {
merge_controllers = true;
}
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight &&
controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) {
merge_controllers = true;
}
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft &&
!controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) {
merge_controllers = true;
}
if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight &&
controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
merge_controllers = true;
}
if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
!controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
merge_controllers = true;
}
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected &&
!controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
merge_controllers = true;
}
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
!controller_1.is_dual_left_connected && controller_1.is_dual_right_connected &&
controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
merge_controllers = true;
}
if (merge_controllers) {
// Disconnect the joycon at the second id and connect the dual joycon at the first index.
DisconnectNpad(npad_id_2);
controller_1.is_dual_left_connected = true;
controller_1.is_dual_right_connected = true;
AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
return;
}
LOG_WARNING(Service_HID,
"Controllers can't be merged npad_id_1:{}, npad_id_2:{}, type_1:{}, type_2:{}, "
"dual_1(left/right):{}/{}, dual_2(left/right):{}/{}",
npad_id_1, npad_id_2, controller_1.device->GetNpadStyleIndex(),
controller_2.device->GetNpadStyleIndex(), controller_1.is_dual_left_connected,
controller_1.is_dual_right_connected, controller_2.is_dual_left_connected,
controller_2.is_dual_right_connected);
}
void Controller_NPad::StartLRAssignmentMode() {
@@ -1072,13 +1182,18 @@ bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
const auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device;
const auto type_index_1 = controller_1->GetNpadStyleIndex();
const auto type_index_2 = controller_2->GetNpadStyleIndex();
const auto is_connected_1 = controller_1->IsConnected();
const auto is_connected_2 = controller_2->IsConnected();
if (!IsControllerSupported(type_index_1) || !IsControllerSupported(type_index_2)) {
if (!IsControllerSupported(type_index_1) && is_connected_1) {
return false;
}
if (!IsControllerSupported(type_index_2) && is_connected_2) {
return false;
}
AddNewControllerAt(type_index_2, npad_id_1);
AddNewControllerAt(type_index_1, npad_id_2);
UpdateControllerAt(type_index_2, npad_id_1, is_connected_2);
UpdateControllerAt(type_index_1, npad_id_2, is_connected_1);
return true;
}

View File

@@ -113,7 +113,8 @@ public:
void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_);
NpadCommunicationMode GetNpadCommunicationMode() const;
void SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyAssignmentMode assignment_mode);
void SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
NpadJoyAssignmentMode assignment_mode);
bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
const Core::HID::VibrationValue& vibration_value);
@@ -464,7 +465,10 @@ private:
std::array<VibrationData, 2> vibration{};
bool unintended_home_button_input_protection{};
bool is_connected{};
Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
// Dual joycons can have only one side connected
bool is_dual_left_connected{true};
bool is_dual_right_connected{true};
// Motion parameters
bool sixaxis_at_rest{true};

View File

@@ -293,8 +293,8 @@ Hid::Hid(Core::System& system_)
{132, &Hid::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"},
{133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"},
{134, &Hid::SetNpadAnalogStickUseCenterClamp, "SetNpadAnalogStickUseCenterClamp"},
{135, nullptr, "SetNpadCaptureButtonAssignment"},
{136, nullptr, "ClearNpadCaptureButtonAssignment"},
{135, &Hid::SetNpadCaptureButtonAssignment, "SetNpadCaptureButtonAssignment"},
{136, &Hid::ClearNpadCaptureButtonAssignment, "ClearNpadCaptureButtonAssignment"},
{200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"},
{201, &Hid::SendVibrationValue, "SendVibrationValue"},
{202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"},
@@ -975,35 +975,35 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx
const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyAssignmentMode::Single);
.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left,
Controller_NPad::NpadJoyAssignmentMode::Single);
LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
parameters.npad_id, parameters.applet_resource_user_id);
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
// TODO: Check the differences between this and SetNpadJoyAssignmentModeSingleByDefault
IPC::RequestParser rp{ctx};
struct Parameters {
Core::HID::NpadIdType npad_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
u64 npad_joy_device_type;
Controller_NPad::NpadJoyDeviceType npad_joy_device_type;
};
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyAssignmentMode::Single);
.SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type,
Controller_NPad::NpadJoyAssignmentMode::Single);
LOG_WARNING(Service_HID,
"(STUBBED) called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
parameters.npad_id, parameters.applet_resource_user_id,
parameters.npad_joy_device_type);
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
parameters.npad_id, parameters.applet_resource_user_id,
parameters.npad_joy_device_type);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -1021,10 +1021,10 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyAssignmentMode::Dual);
.SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual);
LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
parameters.npad_id, parameters.applet_resource_user_id);
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -1186,6 +1186,37 @@ void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
void Hid::SetNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::HID::NpadStyleSet npad_styleset;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::HID::NpadButton button;
};
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_HID,
"(STUBBED) called, npad_styleset={}, applet_resource_user_id={}, button={}",
parameters.npad_styleset, parameters.applet_resource_user_id, parameters.button);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Hid::ClearNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};

View File

@@ -136,6 +136,8 @@ private:
void IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx);
void EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx);
void SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx);
void SetNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx);
void ClearNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx);
void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx);
void SendVibrationValue(Kernel::HLERequestContext& ctx);
void GetActualVibrationValue(Kernel::HLERequestContext& ctx);

View File

@@ -20,8 +20,12 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>&
switch (command.group) {
case 0x0:
switch (command.cmd) {
case 0x1:
return Submit(input, output);
case 0x1: {
if (!fd_to_id.contains(fd)) {
fd_to_id[fd] = next_id++;
}
return Submit(fd, input, output);
}
case 0x2:
return GetSyncpoint(input, output);
case 0x3:
@@ -66,7 +70,10 @@ void nvhost_nvdec::OnOpen(DeviceFD fd) {}
void nvhost_nvdec::OnClose(DeviceFD fd) {
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
system.GPU().ClearCdmaInstance();
const auto iter = fd_to_id.find(fd);
if (iter != fd_to_id.end()) {
system.GPU().ClearCdmaInstance(iter->second);
}
}
} // namespace Service::Nvidia::Devices

View File

@@ -24,6 +24,9 @@ public:
void OnOpen(DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
private:
u32 next_id{};
};
} // namespace Service::Nvidia::Devices

View File

@@ -59,7 +59,8 @@ NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
return NvResult::Success;
}
NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
NvResult nvhost_nvdec_common::Submit(DeviceFD fd, const std::vector<u8>& input,
std::vector<u8>& output) {
IoctlSubmit params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count);
@@ -93,7 +94,7 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u
Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count);
system.Memory().ReadBlock(object->addr + cmd_buffer.offset, cmdlist.data(),
cmdlist.size() * sizeof(u32));
gpu.PushCommandBuffer(cmdlist);
gpu.PushCommandBuffer(fd_to_id[fd], cmdlist);
}
std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
// Some games expect command_buffers to be written back

View File

@@ -104,13 +104,14 @@ protected:
/// Ioctl command implementations
NvResult SetNVMAPfd(const std::vector<u8>& input);
NvResult Submit(const std::vector<u8>& input, std::vector<u8>& output);
NvResult Submit(DeviceFD fd, const std::vector<u8>& input, std::vector<u8>& output);
NvResult GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
NvResult MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
std::unordered_map<DeviceFD, u32> fd_to_id{};
s32_le nvmap_fd{};
u32_le submit_timeout{};
std::shared_ptr<nvmap> nvmap_dev;

View File

@@ -21,7 +21,10 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& i
case 0x0:
switch (command.cmd) {
case 0x1:
return Submit(input, output);
if (!fd_to_id.contains(fd)) {
fd_to_id[fd] = next_id++;
}
return Submit(fd, input, output);
case 0x2:
return GetSyncpoint(input, output);
case 0x3:
@@ -65,7 +68,10 @@ NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& i
void nvhost_vic::OnOpen(DeviceFD fd) {}
void nvhost_vic::OnClose(DeviceFD fd) {
system.GPU().ClearCdmaInstance();
const auto iter = fd_to_id.find(fd);
if (iter != fd_to_id.end()) {
system.GPU().ClearCdmaInstance(iter->second);
}
}
} // namespace Service::Nvidia::Devices

View File

@@ -23,5 +23,8 @@ public:
void OnOpen(DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
private:
u32 next_id{};
};
} // namespace Service::Nvidia::Devices

View File

@@ -1,3 +1,7 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>

View File

@@ -125,8 +125,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
}
metadata.Print();
const auto static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
"subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"};
const auto static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2",
"subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7",
"subsdk8", "subsdk9", "sdk"};
// Use the NSO module loader to figure out the code layout
std::size_t code_size{};

View File

@@ -3,7 +3,6 @@
// Refer to the license.txt file included.
#include <cstring>
#include <regex>
#include <fmt/format.h>
#include "common/fs/file.h"
@@ -15,7 +14,7 @@
namespace InputCommon::TasInput {
enum TasAxes : u8 {
enum class Tas::TasAxis : u8 {
StickX,
StickY,
SubstickX,
@@ -66,7 +65,7 @@ Tas::Tas(const std::string& input_engine_) : InputCommon::InputEngine(input_engi
Tas::~Tas() {
Stop();
};
}
void Tas::LoadTasFiles() {
script_length = 0;
@@ -79,43 +78,43 @@ void Tas::LoadTasFiles() {
}
void Tas::LoadTasFile(size_t player_index, size_t file_index) {
if (!commands[player_index].empty()) {
commands[player_index].clear();
}
commands[player_index].clear();
std::string file = Common::FS::ReadStringFromFile(
Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) /
fmt::format("script{}-{}.txt", file_index, player_index + 1),
Common::FS::FileType::BinaryFile);
std::stringstream command_line(file);
std::istringstream command_line(file);
std::string line;
int frame_no = 0;
while (std::getline(command_line, line, '\n')) {
if (line.empty()) {
continue;
}
std::smatch m;
std::stringstream linestream(line);
std::string segment;
std::vector<std::string> seglist;
while (std::getline(linestream, segment, ' ')) {
seglist.push_back(segment);
std::vector<std::string> seg_list;
{
std::istringstream line_stream(line);
std::string segment;
while (std::getline(line_stream, segment, ' ')) {
seg_list.push_back(std::move(segment));
}
}
if (seglist.size() < 4) {
if (seg_list.size() < 4) {
continue;
}
while (frame_no < std::stoi(seglist.at(0))) {
commands[player_index].push_back({});
const auto num_frames = std::stoi(seg_list[0]);
while (frame_no < num_frames) {
commands[player_index].emplace_back();
frame_no++;
}
TASCommand command = {
.buttons = ReadCommandButtons(seglist.at(1)),
.l_axis = ReadCommandAxis(seglist.at(2)),
.r_axis = ReadCommandAxis(seglist.at(3)),
.buttons = ReadCommandButtons(seg_list[1]),
.l_axis = ReadCommandAxis(seg_list[2]),
.r_axis = ReadCommandAxis(seg_list[3]),
};
commands[player_index].push_back(command);
frame_no++;
@@ -123,16 +122,17 @@ void Tas::LoadTasFile(size_t player_index, size_t file_index) {
LOG_INFO(Input, "TAS file loaded! {} frames", frame_no);
}
void Tas::WriteTasFile(std::u8string file_name) {
void Tas::WriteTasFile(std::u8string_view file_name) {
std::string output_text;
for (size_t frame = 0; frame < record_commands.size(); frame++) {
const TASCommand& line = record_commands[frame];
output_text += fmt::format("{} {} {} {}\n", frame, WriteCommandButtons(line.buttons),
WriteCommandAxis(line.l_axis), WriteCommandAxis(line.r_axis));
}
const auto bytes_written = Common::FS::WriteStringToFile(
Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) / file_name,
Common::FS::FileType::TextFile, output_text);
const auto tas_file_name = Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) / file_name;
const auto bytes_written =
Common::FS::WriteStringToFile(tas_file_name, Common::FS::FileType::TextFile, output_text);
if (bytes_written == output_text.size()) {
LOG_INFO(Input, "TAS file written to file!");
} else {
@@ -205,10 +205,10 @@ void Tas::UpdateThread() {
const int button = static_cast<int>(i);
SetButton(identifier, button, button_status);
}
SetAxis(identifier, TasAxes::StickX, command.l_axis.x);
SetAxis(identifier, TasAxes::StickY, command.l_axis.y);
SetAxis(identifier, TasAxes::SubstickX, command.r_axis.x);
SetAxis(identifier, TasAxes::SubstickY, command.r_axis.y);
SetTasAxis(identifier, TasAxis::StickX, command.l_axis.x);
SetTasAxis(identifier, TasAxis::StickY, command.l_axis.y);
SetTasAxis(identifier, TasAxis::SubstickX, command.r_axis.x);
SetTasAxis(identifier, TasAxis::SubstickY, command.r_axis.y);
}
} else {
is_running = Settings::values.tas_loop.GetValue();
@@ -224,27 +224,28 @@ void Tas::ClearInput() {
}
TasAnalog Tas::ReadCommandAxis(const std::string& line) const {
std::stringstream linestream(line);
std::string segment;
std::vector<std::string> seglist;
while (std::getline(linestream, segment, ';')) {
seglist.push_back(segment);
std::vector<std::string> seg_list;
{
std::istringstream line_stream(line);
std::string segment;
while (std::getline(line_stream, segment, ';')) {
seg_list.push_back(std::move(segment));
}
}
const float x = std::stof(seglist.at(0)) / 32767.0f;
const float y = std::stof(seglist.at(1)) / 32767.0f;
const float x = std::stof(seg_list.at(0)) / 32767.0f;
const float y = std::stof(seg_list.at(1)) / 32767.0f;
return {x, y};
}
u64 Tas::ReadCommandButtons(const std::string& data) const {
std::stringstream button_text(data);
std::string line;
u64 Tas::ReadCommandButtons(const std::string& line) const {
std::istringstream button_text(line);
std::string button_line;
u64 buttons = 0;
while (std::getline(button_text, line, ';')) {
for (auto [text, tas_button] : text_to_tas_button) {
if (text == line) {
while (std::getline(button_text, button_line, ';')) {
for (const auto& [text, tas_button] : text_to_tas_button) {
if (text == button_line) {
buttons |= static_cast<u64>(tas_button);
break;
}
@@ -254,8 +255,8 @@ u64 Tas::ReadCommandButtons(const std::string& data) const {
}
std::string Tas::WriteCommandButtons(u64 buttons) const {
std::string returns = "";
for (auto [text_button, tas_button] : text_to_tas_button) {
std::string returns;
for (const auto& [text_button, tas_button] : text_to_tas_button) {
if ((buttons & static_cast<u64>(tas_button)) != 0) {
returns += fmt::format("{};", text_button);
}
@@ -267,6 +268,10 @@ std::string Tas::WriteCommandAxis(TasAnalog analog) const {
return fmt::format("{};{}", analog.x * 32767, analog.y * 32767);
}
void Tas::SetTasAxis(const PadIdentifier& identifier, TasAxis axis, f32 value) {
SetAxis(identifier, static_cast<int>(axis), value);
}
void Tas::StartStop() {
if (!Settings::values.tas_enable) {
return;

View File

@@ -5,11 +5,11 @@
#pragma once
#include <array>
#include <string>
#include <vector>
#include "common/common_types.h"
#include "common/settings_input.h"
#include "input_common/input_engine.h"
#include "input_common/main.h"
/*
To play back TAS scripts on Yuzu, select the folder with scripts in the configuration menu below
@@ -88,39 +88,39 @@ public:
/**
* Changes the input status that will be stored in each frame
* @param buttons: bitfield with the status of the buttons
* @param left_axis: value of the left axis
* @param right_axis: value of the right axis
* @param buttons Bitfield with the status of the buttons
* @param left_axis Value of the left axis
* @param right_axis Value of the right axis
*/
void RecordInput(u64 buttons, TasAnalog left_axis, TasAnalog right_axis);
// Main loop that records or executes input
void UpdateThread();
// Sets the flag to start or stop the TAS command excecution and swaps controllers profiles
// Sets the flag to start or stop the TAS command execution and swaps controllers profiles
void StartStop();
// Stop the TAS and reverts any controller profile
// Stop the TAS and reverts any controller profile
void Stop();
// Sets the flag to reload the file and start from the begining in the next update
// Sets the flag to reload the file and start from the beginning in the next update
void Reset();
/**
* Sets the flag to enable or disable recording of inputs
* @return Returns true if the current recording status is enabled
* @returns true if the current recording status is enabled
*/
bool Record();
/**
* Saves contents of record_commands on a file
* @param overwrite_file: Indicates if player 1 should be overwritten
* @param overwrite_file Indicates if player 1 should be overwritten
*/
void SaveRecording(bool overwrite_file);
/**
* Returns the current status values of TAS playback/recording
* @return Tuple of
* @returns A Tuple of
* TasState indicating the current state out of Running ;
* Current playback progress ;
* Total length of script file currently loaded or being recorded
@@ -128,6 +128,8 @@ public:
std::tuple<TasState, size_t, size_t> GetStatus() const;
private:
enum class TasAxis : u8;
struct TASCommand {
u64 buttons{};
TasAnalog l_axis{};
@@ -137,29 +139,31 @@ private:
/// Loads TAS files from all players
void LoadTasFiles();
/** Loads TAS file from the specified player
* @param player_index: player number to save the script
* @param file_index: script number of the file
/**
* Loads TAS file from the specified player
* @param player_index Player number to save the script
* @param file_index Script number of the file
*/
void LoadTasFile(size_t player_index, size_t file_index);
/** Writes a TAS file from the recorded commands
* @param file_name: name of the file to be written
/**
* Writes a TAS file from the recorded commands
* @param file_name Name of the file to be written
*/
void WriteTasFile(std::u8string file_name);
void WriteTasFile(std::u8string_view file_name);
/**
* Parses a string containing the axis values. X and Y have a range from -32767 to 32767
* @param line: string containing axis values with the following format "x;y"
* @return Returns a TAS analog object with axis values with range from -1.0 to 1.0
* @param line String containing axis values with the following format "x;y"
* @returns A TAS analog object with axis values with range from -1.0 to 1.0
*/
TasAnalog ReadCommandAxis(const std::string& line) const;
/**
* Parses a string containing the button values. Each button is represented by it's text format
* specified in text_to_tas_button array
* @param line: string containing button name with the following format "a;b;c;d..."
* @return Returns a u64 with each bit representing the status of a button
* @param line string containing button name with the following format "a;b;c;d..."
* @returns A u64 with each bit representing the status of a button
*/
u64 ReadCommandButtons(const std::string& line) const;
@@ -170,17 +174,20 @@ private:
/**
* Converts an u64 containing the button status into the text equivalent
* @param buttons: bitfield with the status of the buttons
* @return Returns a string with the name of the buttons to be written to the file
* @param buttons Bitfield with the status of the buttons
* @returns A string with the name of the buttons to be written to the file
*/
std::string WriteCommandButtons(u64 buttons) const;
/**
* Converts an TAS analog object containing the axis status into the text equivalent
* @param data: value of the axis
* @return A string with the value of the axis to be written to the file
* @param analog Value of the axis
* @returns A string with the value of the axis to be written to the file
*/
std::string WriteCommandAxis(TasAnalog data) const;
std::string WriteCommandAxis(TasAnalog analog) const;
/// Sets an axis for a particular pad to the given value.
void SetTasAxis(const PadIdentifier& identifier, TasAxis axis, f32 value);
size_t script_length{0};
bool is_recording{false};

View File

@@ -1,7 +1,5 @@
add_library(shader_recompiler STATIC
backend/bindings.h
backend/glasm/emit_context.cpp
backend/glasm/emit_context.h
backend/glasm/emit_glasm.cpp
backend/glasm/emit_glasm.h
backend/glasm/emit_glasm_barriers.cpp
@@ -22,10 +20,10 @@ add_library(shader_recompiler STATIC
backend/glasm/emit_glasm_special.cpp
backend/glasm/emit_glasm_undefined.cpp
backend/glasm/emit_glasm_warp.cpp
backend/glasm/glasm_emit_context.cpp
backend/glasm/glasm_emit_context.h
backend/glasm/reg_alloc.cpp
backend/glasm/reg_alloc.h
backend/glsl/emit_context.cpp
backend/glsl/emit_context.h
backend/glsl/emit_glsl.cpp
backend/glsl/emit_glsl.h
backend/glsl/emit_glsl_atomic.cpp
@@ -47,10 +45,10 @@ add_library(shader_recompiler STATIC
backend/glsl/emit_glsl_special.cpp
backend/glsl/emit_glsl_undefined.cpp
backend/glsl/emit_glsl_warp.cpp
backend/glsl/glsl_emit_context.cpp
backend/glsl/glsl_emit_context.h
backend/glsl/var_alloc.cpp
backend/glsl/var_alloc.h
backend/spirv/emit_context.cpp
backend/spirv/emit_context.h
backend/spirv/emit_spirv.cpp
backend/spirv/emit_spirv.h
backend/spirv/emit_spirv_atomic.cpp
@@ -72,6 +70,8 @@ add_library(shader_recompiler STATIC
backend/spirv/emit_spirv_special.cpp
backend/spirv/emit_spirv_undefined.cpp
backend/spirv/emit_spirv_warp.cpp
backend/spirv/spirv_emit_context.cpp
backend/spirv/spirv_emit_context.h
environment.h
exception.h
frontend/ir/abstract_syntax_list.h

View File

@@ -9,9 +9,9 @@
#include "common/div_ceil.h"
#include "common/settings.h"
#include "shader_recompiler/backend/bindings.h"
#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/ir_emitter.h"
#include "shader_recompiler/frontend/ir/program.h"
#include "shader_recompiler/profile.h"

View File

@@ -0,0 +1,22 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
namespace Shader::Backend::GLASM {
void EmitBarrier(EmitContext& ctx) {
ctx.Add("BAR;");
}
void EmitWorkgroupMemoryBarrier(EmitContext& ctx) {
ctx.Add("MEMBAR.CTA;");
}
void EmitDeviceMemoryBarrier(EmitContext& ctx) {
ctx.Add("MEMBAR;");
}
} // namespace Shader::Backend::GLASM

View File

@@ -2,8 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::Backend::GLASM {

View File

@@ -2,8 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::Backend::GLASM {

View File

@@ -4,8 +4,8 @@
#include <string_view>
#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
#include "shader_recompiler/profile.h"
#include "shader_recompiler/shader_info.h"
@@ -335,6 +335,35 @@ void EmitSetFragDepth(EmitContext& ctx, ScalarF32 value) {
ctx.Add("MOV.F result.depth.z,{};", value);
}
void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst) {
ctx.Add("MOV.S {},invocation.groupid;", inst);
}
void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst) {
ctx.Add("MOV.S {},invocation.localid;", inst);
}
void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
ctx.Add("MOV.S {}.x,primitive_invocation.x;", inst);
}
void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {
ctx.Add("MOV.S {}.x,fragment.sampleid.x;", inst);
}
void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst) {
ctx.Add("MOV.S {}.x,fragment.helperthread.x;", inst);
}
void EmitYDirection(EmitContext& ctx, IR::Inst& inst) {
ctx.uses_y_direction = true;
ctx.Add("MOV.F {}.x,y_direction[0].w;", inst);
}
void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) {
ctx.Add("MOV.F {}.x,scaling[0].z;", inst);
}
void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset) {
ctx.Add("MOV.U {},lmem[{}].x;", inst, word_offset);
}

View File

@@ -0,0 +1,18 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
namespace Shader::Backend::GLASM {
void EmitJoin(EmitContext&) {
throw NotImplementedException("Join shouldn't be emitted");
}
void EmitDemoteToHelperInvocation(EmitContext& ctx) {
ctx.Add("KIL TR.x;");
}
} // namespace Shader::Backend::GLASM

View File

@@ -4,8 +4,8 @@
#include <string_view>
#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/modifiers.h"
#include "shader_recompiler/frontend/ir/value.h"

View File

@@ -4,8 +4,8 @@
#include <string_view>
#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/modifiers.h"
#include "shader_recompiler/frontend/ir/value.h"

View File

@@ -4,8 +4,8 @@
#include <utility>
#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/modifiers.h"
#include "shader_recompiler/frontend/ir/value.h"

View File

@@ -2,8 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::Backend::GLASM {

View File

@@ -0,0 +1,26 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
namespace Shader::Backend::GLASM {
void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
ctx.Add("OR.S {},{},{};", inst, a, b);
}
void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
ctx.Add("AND.S {},{},{};", inst, a, b);
}
void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
ctx.Add("XOR.S {},{},{};", inst, a, b);
}
void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
ctx.Add("SEQ.S {},{},0;", inst, value);
}
} // namespace Shader::Backend::GLASM

View File

@@ -4,8 +4,8 @@
#include <string_view>
#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/program.h"
#include "shader_recompiler/frontend/ir/value.h"
#include "shader_recompiler/runtime_info.h"

View File

@@ -4,8 +4,8 @@
#include <string_view>
#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/program.h"
#include "shader_recompiler/frontend/ir/value.h"
@@ -17,110 +17,6 @@ namespace Shader::Backend::GLASM {
#define NotImplemented() throw NotImplementedException("GLASM instruction {}", __LINE__)
static void DefinePhi(EmitContext& ctx, IR::Inst& phi) {
switch (phi.Type()) {
case IR::Type::U1:
case IR::Type::U32:
case IR::Type::F32:
ctx.reg_alloc.Define(phi);
break;
case IR::Type::U64:
case IR::Type::F64:
ctx.reg_alloc.LongDefine(phi);
break;
default:
throw NotImplementedException("Phi node type {}", phi.Type());
}
}
void EmitPhi(EmitContext& ctx, IR::Inst& phi) {
const size_t num_args{phi.NumArgs()};
for (size_t i = 0; i < num_args; ++i) {
ctx.reg_alloc.Consume(phi.Arg(i));
}
if (!phi.Definition<Id>().is_valid) {
// The phi node wasn't forward defined
DefinePhi(ctx, phi);
}
}
void EmitVoid(EmitContext&) {}
void EmitReference(EmitContext& ctx, const IR::Value& value) {
ctx.reg_alloc.Consume(value);
}
void EmitPhiMove(EmitContext& ctx, const IR::Value& phi_value, const IR::Value& value) {
IR::Inst& phi{RegAlloc::AliasInst(*phi_value.Inst())};
if (!phi.Definition<Id>().is_valid) {
// The phi node wasn't forward defined
DefinePhi(ctx, phi);
}
const Register phi_reg{ctx.reg_alloc.Consume(IR::Value{&phi})};
const Value eval_value{ctx.reg_alloc.Consume(value)};
if (phi_reg == eval_value) {
return;
}
switch (phi.Flags<IR::Type>()) {
case IR::Type::U1:
case IR::Type::U32:
case IR::Type::F32:
ctx.Add("MOV.S {}.x,{};", phi_reg, ScalarS32{eval_value});
break;
case IR::Type::U64:
case IR::Type::F64:
ctx.Add("MOV.U64 {}.x,{};", phi_reg, ScalarRegister{eval_value});
break;
default:
throw NotImplementedException("Phi node type {}", phi.Type());
}
}
void EmitJoin(EmitContext& ctx) {
NotImplemented();
}
void EmitDemoteToHelperInvocation(EmitContext& ctx) {
ctx.Add("KIL TR.x;");
}
void EmitBarrier(EmitContext& ctx) {
ctx.Add("BAR;");
}
void EmitWorkgroupMemoryBarrier(EmitContext& ctx) {
ctx.Add("MEMBAR.CTA;");
}
void EmitDeviceMemoryBarrier(EmitContext& ctx) {
ctx.Add("MEMBAR;");
}
void EmitPrologue(EmitContext& ctx) {
// TODO
}
void EmitEpilogue(EmitContext& ctx) {
// TODO
}
void EmitEmitVertex(EmitContext& ctx, ScalarS32 stream) {
if (stream.type == Type::U32 && stream.imm_u32 == 0) {
ctx.Add("EMIT;");
} else {
ctx.Add("EMITS {};", stream);
}
}
void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
if (!stream.IsImmediate()) {
LOG_WARNING(Shader_GLASM, "Stream is not immediate");
}
ctx.reg_alloc.Consume(stream);
ctx.Add("ENDPRIM;");
}
void EmitGetRegister(EmitContext& ctx) {
NotImplemented();
}
@@ -185,55 +81,6 @@ void EmitSetOFlag(EmitContext& ctx) {
NotImplemented();
}
void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst) {
ctx.Add("MOV.S {},invocation.groupid;", inst);
}
void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst) {
ctx.Add("MOV.S {},invocation.localid;", inst);
}
void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
ctx.Add("MOV.S {}.x,primitive_invocation.x;", inst);
}
void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {
ctx.Add("MOV.S {}.x,fragment.sampleid.x;", inst);
}
void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst) {
ctx.Add("MOV.S {}.x,fragment.helperthread.x;", inst);
}
void EmitYDirection(EmitContext& ctx, IR::Inst& inst) {
ctx.uses_y_direction = true;
ctx.Add("MOV.F {}.x,y_direction[0].w;", inst);
}
void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) {
ctx.Add("MOV.F {}.x,scaling[0].z;", inst);
}
void EmitUndefU1(EmitContext& ctx, IR::Inst& inst) {
ctx.Add("MOV.S {}.x,0;", inst);
}
void EmitUndefU8(EmitContext& ctx, IR::Inst& inst) {
ctx.Add("MOV.S {}.x,0;", inst);
}
void EmitUndefU16(EmitContext& ctx, IR::Inst& inst) {
ctx.Add("MOV.S {}.x,0;", inst);
}
void EmitUndefU32(EmitContext& ctx, IR::Inst& inst) {
ctx.Add("MOV.S {}.x,0;", inst);
}
void EmitUndefU64(EmitContext& ctx, IR::Inst& inst) {
ctx.LongAdd("MOV.S64 {}.x,0;", inst);
}
void EmitGetZeroFromOp(EmitContext& ctx) {
NotImplemented();
}
@@ -258,20 +105,4 @@ void EmitGetInBoundsFromOp(EmitContext& ctx) {
NotImplemented();
}
void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
ctx.Add("OR.S {},{},{};", inst, a, b);
}
void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
ctx.Add("AND.S {},{},{};", inst, a, b);
}
void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
ctx.Add("XOR.S {},{},{};", inst, a, b);
}
void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
ctx.Add("SEQ.S {},{},0;", inst, value);
}
} // namespace Shader::Backend::GLASM

View File

@@ -3,8 +3,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::Backend::GLASM {

View File

@@ -3,8 +3,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::Backend::GLASM {

View File

@@ -0,0 +1,95 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::Backend::GLASM {
static void DefinePhi(EmitContext& ctx, IR::Inst& phi) {
switch (phi.Type()) {
case IR::Type::U1:
case IR::Type::U32:
case IR::Type::F32:
ctx.reg_alloc.Define(phi);
break;
case IR::Type::U64:
case IR::Type::F64:
ctx.reg_alloc.LongDefine(phi);
break;
default:
throw NotImplementedException("Phi node type {}", phi.Type());
}
}
void EmitPhi(EmitContext& ctx, IR::Inst& phi) {
const size_t num_args{phi.NumArgs()};
for (size_t i = 0; i < num_args; ++i) {
ctx.reg_alloc.Consume(phi.Arg(i));
}
if (!phi.Definition<Id>().is_valid) {
// The phi node wasn't forward defined
DefinePhi(ctx, phi);
}
}
void EmitVoid(EmitContext&) {}
void EmitReference(EmitContext& ctx, const IR::Value& value) {
ctx.reg_alloc.Consume(value);
}
void EmitPhiMove(EmitContext& ctx, const IR::Value& phi_value, const IR::Value& value) {
IR::Inst& phi{RegAlloc::AliasInst(*phi_value.Inst())};
if (!phi.Definition<Id>().is_valid) {
// The phi node wasn't forward defined
DefinePhi(ctx, phi);
}
const Register phi_reg{ctx.reg_alloc.Consume(IR::Value{&phi})};
const Value eval_value{ctx.reg_alloc.Consume(value)};
if (phi_reg == eval_value) {
return;
}
switch (phi.Flags<IR::Type>()) {
case IR::Type::U1:
case IR::Type::U32:
case IR::Type::F32:
ctx.Add("MOV.S {}.x,{};", phi_reg, ScalarS32{eval_value});
break;
case IR::Type::U64:
case IR::Type::F64:
ctx.Add("MOV.U64 {}.x,{};", phi_reg, ScalarRegister{eval_value});
break;
default:
throw NotImplementedException("Phi node type {}", phi.Type());
}
}
void EmitPrologue(EmitContext&) {
// TODO
}
void EmitEpilogue(EmitContext&) {
// TODO
}
void EmitEmitVertex(EmitContext& ctx, ScalarS32 stream) {
if (stream.type == Type::U32 && stream.imm_u32 == 0) {
ctx.Add("EMIT;");
} else {
ctx.Add("EMITS {};", stream);
}
}
void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
if (!stream.IsImmediate()) {
LOG_WARNING(Shader_GLASM, "Stream is not immediate");
}
ctx.reg_alloc.Consume(stream);
ctx.Add("ENDPRIM;");
}
} // namespace Shader::Backend::GLASM

View File

@@ -0,0 +1,30 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
namespace Shader::Backend::GLASM {
void EmitUndefU1(EmitContext& ctx, IR::Inst& inst) {
ctx.Add("MOV.S {}.x,0;", inst);
}
void EmitUndefU8(EmitContext& ctx, IR::Inst& inst) {
ctx.Add("MOV.S {}.x,0;", inst);
}
void EmitUndefU16(EmitContext& ctx, IR::Inst& inst) {
ctx.Add("MOV.S {}.x,0;", inst);
}
void EmitUndefU32(EmitContext& ctx, IR::Inst& inst) {
ctx.Add("MOV.S {}.x,0;", inst);
}
void EmitUndefU64(EmitContext& ctx, IR::Inst& inst) {
ctx.LongAdd("MOV.S64 {}.x,0;", inst);
}
} // namespace Shader::Backend::GLASM

View File

@@ -2,8 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "shader_recompiler/backend/glasm/emit_context.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
#include "shader_recompiler/profile.h"

Some files were not shown because too many files have changed in this diff Show More