Compare commits

..

60 Commits

Author SHA1 Message Date
lizzie 4188417d6f 2026-09-18 17:28:36
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-18 17:28:36 +00:00
lizzie ccbcc9958c swift reworks 2026-09-18 17:24:39 +00:00
lizzie 8b514d39fe fix 2026-09-18 17:24:39 +00:00
lizzie a3d1871002 fix shit 2 2026-09-18 17:24:39 +00:00
lizzie 2387863c4a swiftuijoystick 2026-09-18 17:24:39 +00:00
lizzie 9fc078ac49 merge more 2026-09-18 17:24:39 +00:00
lizzie 5000815f9d add YUZU_USE_EXTERNAL_SDL2 2026-09-18 17:24:39 +00:00
lizzie 1da937c5f1 fix? 2026-09-18 17:24:39 +00:00
lizzie 5f894ea6e5 pomelo scraps 2026-09-18 17:24:39 +00:00
lizzie f48e2f9647 remove toolchain file + remove multibuild thingy 2026-09-18 17:24:39 +00:00
crueter 1a8c30b332 Fix build script
Signed-off-by: crueter <crueter@eden-emu.dev>
2026-09-18 17:24:39 +00:00
lizzie 9ac88a6576 AAAAAA 2026-09-18 17:24:39 +00:00
crueter 190275fd97 Build fixes
Signed-off-by: crueter <crueter@eden-emu.dev>
2026-09-18 17:24:38 +00:00
lizzie f43902a845 add basic ios shit 2026-09-18 17:24:29 +00:00
crueter dc87555982 Revert stbi
Signed-off-by: crueter <crueter@eden-emu.dev>
2026-09-18 17:24:29 +00:00
crueter bd4130055d fix libs
Signed-off-by: crueter <crueter@eden-emu.dev>
2026-09-18 17:24:27 +00:00
crueter faca1b377a Fix most build errors
Signed-off-by: crueter <crueter@eden-emu.dev>
2026-09-18 17:20:12 +00:00
crueter 56536549cc Update CPMUtil, and fix script
Signed-off-by: crueter <crueter@eden-emu.dev>
2026-09-18 17:19:40 +00:00
lizzie a2b542693b DISABLE BY DEFAULT ON IOS FFS 2026-09-18 17:19:40 +00:00
lizzie e3a6a99cae fix life 2026-09-18 17:19:40 +00:00
lizzie e553e14d53 fix sirit i think, add ios-aarch64 2026-09-18 17:19:40 +00:00
lizzie f4cd650f95 changes? 2026-09-18 17:19:40 +00:00
lizzie 38d7021a54 fix shit? 2026-09-18 17:19:40 +00:00
lizzie b2223ec9db fix #include "common/logging.h" 2026-09-18 17:19:40 +00:00
lizzie 2b1caac3e8 fix IOS again fucking objc bridge 2026-09-18 17:19:40 +00:00
lizzie 0fc949d29d fix? 2026-09-18 17:19:40 +00:00
lizzie 88d1fb47d4 properly use bridging header, fix headers 2026-09-18 17:19:40 +00:00
lizzie 4f4bf58153 fix swift driver I HOPE 2026-09-18 17:19:40 +00:00
lizzie 61c0c8bb7c license 2026-09-18 17:19:40 +00:00
lizzie a86291a812 [temporary c++ shit] 2026-09-18 17:19:40 +00:00
lizzie ea337df5dd bit of cmake fuckery 2026-09-18 17:19:39 +00:00
lizzie f0fe092be8 use language generator exprs 2026-09-18 17:19:15 +00:00
lizzie 6c1bb8d5c5 $<$<COMPILE_LANGUAGE:C,CXX>:-Werror=missing-declarations> 2026-09-18 17:19:15 +00:00
lizzie f2cf3028c5 proper linkings?! 2026-09-18 17:19:15 +00:00
lizzie 463f255299 bridge changes/fixes 2026-09-18 17:19:15 +00:00
lizzie aea66ab8e1 fix license 2026-09-18 17:19:15 +00:00
lizzie 084014d942 fix xcode 2? 2026-09-18 17:19:15 +00:00
lizzie 349b19fc1a fix xcode paths? 2026-09-18 17:19:15 +00:00
lizzie 896ee3080f fx 2026-09-18 17:19:15 +00:00
lizzie 63b5e01627 fx 2026-09-18 17:19:15 +00:00
lizzie 62b0169f05 fix boost 2026-09-18 17:19:13 +00:00
lizzie 8d799eaf1c fx 2026-09-18 17:15:27 +00:00
lizzie 15a1d783cd fix stuff 2026-09-18 17:15:27 +00:00
lizzie af9f924658 stupid macos 2026-09-18 17:15:27 +00:00
lizzie cc8ea0f574 fix1 2026-09-18 17:15:27 +00:00
lizzie a020ce62f8 fx 2026-09-18 17:15:27 +00:00
lizzie bd532f51b4 fix spirv-tools 2026-09-18 17:15:27 +00:00
lizzie 8694f708eb fix license 2026-09-18 17:15:27 +00:00
lizzie 339694df1b fix ffmpeg 2026-09-18 17:15:27 +00:00
lizzie 0043edb66f fx 2026-09-18 17:15:27 +00:00
lizzie 29888a6f35 fx 2026-09-18 17:15:27 +00:00
lizzie 78aa04ed51 license 2026-09-18 17:15:27 +00:00
lizzie fdc8ea262d ios toolchain cmake 2026-09-18 17:15:27 +00:00
lizzie c09c3edd11 license 2026-09-18 17:15:27 +00:00
lizzie 910c59ceab license headers 2026-09-18 17:15:27 +00:00
lizzie ee378e1322 flatten + cmake 2026-09-18 17:15:27 +00:00
lizzie 5b525e5f35 flatten 2026-09-18 17:15:27 +00:00
lizzie 6b42e8346d loicense 2026-09-18 17:15:27 +00:00
lizzie bba106569a modernize #1 2026-09-18 17:15:26 +00:00
lizzie 4dea82c984 sudachi ios stuff 2026-09-18 17:15:26 +00:00
164 changed files with 18870 additions and 16361 deletions
+20
View File
@@ -0,0 +1,20 @@
#!/bin/sh -ex
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
WORK_DIR="$PWD"
xcrun --sdk iphoneos --show-sdk-path
# TODO: support iphonesimulator sdk
cmake -G Xcode -B build/ios \
-DCMAKE_OSX_DEPLOYMENT_TARGET=16.0 \
-DCMAKE_OSX_SYSROOT=iphoneos \
-DCMAKE_SYSTEM_NAME=iOS \
-DCMAKE_OSX_ARCHITECTURES="arm64" \
-DCMAKE_BUILD_TYPE=Release \
-DYUZU_USE_EXTERNAL_SDL2=ON \
"$@"
cmake --build build/ios -t eden-ios --config Release
+1 -1
View File
@@ -115,7 +115,7 @@ for file in $FILES; do
*.cmake|*.sh|*.ps1|*.py|*.rb|*.perl|*.pl|*.nix|*CMakeLists.txt)
begin="#"
;;
*.kt|*.kts|*.cpp|*.h|*.qml|*.c|*.hpp|*.hxx|*.cxx|*.h.in|*.inc)
*.kt|*.kts|*.cpp|*.h|*.qml|*.c|*.hpp|*.hxx|*.cxx|*.h.in|*.inc|*.mm)
begin="//"
;;
*)
+31
View File
@@ -0,0 +1,31 @@
diff --git a/libs/process/src/shell.cpp b/libs/process/src/shell.cpp
index bf4bbfd8..bc4aae89 100644
--- a/libs/process/src/shell.cpp
+++ b/libs/process/src/shell.cpp
@@ -19,7 +19,7 @@
#if defined(BOOST_PROCESS_V2_WINDOWS)
#include <windows.h>
#include <shellapi.h>
-#elif !defined(__OpenBSD__) && !defined(__ANDROID__)
+#elif !defined(__OpenBSD__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IPHONE)
#include <wordexp.h>
#endif
@@ -30,7 +30,7 @@ BOOST_PROCESS_V2_DECL const error_category& get_shell_category()
{
return system_category();
}
-#elif !defined(__OpenBSD__) && !defined(__ANDROID__)
+#elif !defined(__OpenBSD__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IPHONE)
struct shell_category_t final : public error_category
{
@@ -99,7 +99,7 @@ auto shell::args() const-> args_type
return input_.c_str();
}
-#elif !defined(__OpenBSD__) && !defined(__ANDROID__)
+#elif !defined(__OpenBSD__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IPHONE)
void shell::parse_()
{
+48 -12
View File
@@ -24,6 +24,29 @@ include(CMakeDependentOption)
include(CTest)
include(CPMUtil)
# TODO(crueter): Make this more automatic.
if (IOS)
list(APPEND CMAKE_FIND_ROOT_PATH "${CMAKE_OSX_SYSROOT_INT}" CACHE INTERNAL "")
list(APPEND CMAKE_PROGRAM_PATH "/opt/homebrew/bin" CACHE INTERNAL "")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH CACHE INTERNAL "")
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH CACHE INTERNAL "")
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH CACHE INTERNAL "")
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH CACHE INTERNAL "")
list(LENGTH CMAKE_OSX_ARCHITECTURES _arch_len)
if (NOT _arch_len EQUAL 1)
message(FATAL_ERROR "CMAKE_OSX_ARCHITECTURES must contain exactly one architecture.")
endif()
# TODO(crueter): Proper handling for this.
if (CMAKE_OSX_ARCHITECTURES STREQUAL arm64)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
else()
set(CMAKE_SYSTEM_PROCESSOR ${CMAKE_OSX_ARCHITECTURES})
endif()
endif()
if (NOT DEFINED ARCHITECTURE)
message(FATAL_ERROR "Architecture didn't make it out of scope, did you delete DetectArchitecture.cmake?")
endif()
@@ -45,7 +68,7 @@ if (NETBSD)
set(ENV{PKG_CONFIG_PATH} "${PKG_CONFIG_PATH}:${CMAKE_SYSROOT}/usr/pkg/lib/ffmpeg7/pkgconfig")
endif()
cmake_dependent_option(YUZU_STATIC_ROOM "Build a static room executable only (CI only)" OFF "LINUX" OFF)
cmake_dependent_option(YUZU_STATIC_ROOM "Build a static room executable only (CI only)" OFF "LINUX OR (APPLE AND NOT IOS)" OFF)
if (YUZU_STATIC_ROOM)
set(YUZU_ROOM ON)
set(YUZU_ROOM_STANDALONE ON)
@@ -71,9 +94,15 @@ if (YUZU_STATIC_ROOM)
endif()
# qt stuff
option(ENABLE_QT "Enable the Qt frontend" ON)
if (IOS OR ANDROID)
set(_default_qt OFF)
else()
set(_default_qt ON)
endif()
option(ENABLE_QT "Enable the Qt frontend" ${_default_qt})
option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF)
option(ENABLE_UPDATE_CHECKER "Enable update checker (for Qt and Android)" OFF)
cmake_dependent_option(ENABLE_UPDATE_CHECKER "Enable update checker (for Qt and Android)" OFF "ENABLE_QT OR ANDROID" OFF)
cmake_dependent_option(YUZU_USE_QT_MULTIMEDIA "Use QtMultimedia for Camera" OFF "NOT YUZU_USE_BUNDLED_QT" OFF)
cmake_dependent_option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF "NOT YUZU_USE_BUNDLED_QT" OFF)
set(YUZU_QT_MIRROR "" CACHE STRING "What mirror to use for downloading the bundled Qt libraries")
@@ -220,7 +249,7 @@ endif()
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
set(EXT_DEFAULT OFF)
if (MSVC OR ANDROID)
if (MSVC OR ANDROID OR IOS)
set(EXT_DEFAULT ON)
endif()
@@ -232,14 +261,14 @@ cmake_dependent_option(YUZU_USE_EXTERNAL_FFMPEG "Build FFmpeg from external sour
# sirit
set(BUNDLED_SIRIT_DEFAULT OFF)
if (MSVC AND NOT (CMAKE_BUILD_TYPE MATCHES "Deb") OR ANDROID)
if ((MSVC AND NOT (CMAKE_BUILD_TYPE MATCHES "Deb")) OR ANDROID OR IOS)
set(BUNDLED_SIRIT_DEFAULT ON)
endif()
option(YUZU_USE_BUNDLED_SIRIT "Download bundled sirit" ${BUNDLED_SIRIT_DEFAULT})
# FreeBSD 15+ has libusb, versions below should disable it
cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "WIN32 OR LINUX OR FREEBSD OR APPLE" OFF)
cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "WIN32 OR LINUX OR FREEBSD OR (APPLE AND NOT IOS)" OFF)
cmake_dependent_option(ENABLE_OPENGL "Enable OpenGL" ON "NOT (WIN32 AND ARCHITECTURE_arm64) AND NOT APPLE AND NOT ANDROID" OFF)
mark_as_advanced(FORCE ENABLE_OPENGL)
@@ -261,10 +290,10 @@ option(YUZU_LEGACY "Apply patches that improve compatibility with older GPUs (e.
option(NIGHTLY_BUILD "Use Nightly qualifiers in the update checker and build metadata" OFF)
cmake_dependent_option(YUZU_ROOM "Enable dedicated room functionality" ON "NOT ANDROID" OFF)
cmake_dependent_option(YUZU_ROOM "Enable dedicated room functionality" ON "NOT ANDROID AND NOT IOS" OFF)
cmake_dependent_option(YUZU_ROOM_STANDALONE "Enable standalone room executable" ON "YUZU_ROOM" OFF)
cmake_dependent_option(YUZU_CMD "Compile the eden-cli executable" ON "NOT ANDROID" OFF)
cmake_dependent_option(YUZU_CMD "Compile the eden-cli executable" ON "NOT ANDROID AND NOT IOS" OFF)
cmake_dependent_option(YUZU_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR LINUX" OFF)
@@ -308,7 +337,7 @@ if (YUZU_ROOM)
add_compile_definitions(YUZU_ROOM)
endif()
if (UNIX AND NOT (LINUX OR WIN32))
if ((UNIX OR IOS) AND NOT (LINUX OR WIN32))
if(CXX_APPLE OR CXX_CLANG)
# libc++ has stop_token and jthread as experimental
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexperimental-library")
@@ -381,7 +410,10 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
find_package(RenderDoc MODULE)
find_package(RenderDoc MODULE QUIET)
if (NOT RenderDoc_FOUND)
message(WARNING "RenderDoc not found. Some debugging features may be disabled.")
endif()
# openssl funniness
if (YUZU_USE_BUNDLED_OPENSSL)
@@ -509,9 +541,13 @@ endfunction()
# Platform-specific library requirements
# Put these BEFORE EXTERNALS or Boost WILL die
# =============================================
if (APPLE)
foreach(fw Carbon Metal Cocoa IOKit CoreVideo CoreMedia Security UniformTypeIdentifiers Foundation)
if (IOS)
set(_libs Carbon Metal IOKit CoreVideo CoreMedia Security UniformTypeIdentifiers Foundation)
else()
set(_libs Carbon Metal Cocoa IOKit CoreVideo CoreMedia Security UniformTypeIdentifiers Foundation)
endif()
foreach(fw ${_libs})
find_library(${fw}_LIBRARY ${fw} REQUIRED)
list(APPEND PLATFORM_LIBRARIES ${${fw}_LIBRARY})
endforeach()
+579 -624
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+503 -548
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+508 -550
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+527 -573
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+11
View File
@@ -6,6 +6,7 @@
- [macOS](#macos)
- [OpenIndiana](#openindiana)
- [OmniOS](#omnios)
- [iOS](#ios)
- [HaikuOS](#haikuos)
- [OpenBSD](#openbsd)
- [FreeBSD](#freebsd)
@@ -90,6 +91,16 @@ If it wasn't obvious already, you require a X11 server to properly run the emula
For Solaris based OSes, `${CMAKE_SYSTEM_NAME}` isn't properly set on CMake (it's set to i686 on AMD64), you may find issues when building OpenSSL from `openssl-cmake`.
## iOS
iOS has a dedicated build script, we **highly** recommend using that instead of doing anything else, we don't support any other configuration than the one present in said build script.
To build, it's simply as easy as doing
```sh
chmod +x .ci/ios/build.sh
.ci/ios/build.sh
```
## HaikuOS
It's recommended to do a `pkgman full-sync` before installing. See [HaikuOS: Installing applications](https://www.haiku-os.org/guides/daily-tasks/install-applications/). Sometimes the process may be interrupted by an error like "Interrupted syscall". Simply firing the command again fixes the issue. By default `g++` is included on the default installation.
+6
View File
@@ -272,6 +272,12 @@ if (VulkanMemoryAllocator_ADDED)
endif()
endif()
# httplib
if (IOS)
set(HTTPLIB_USE_BROTLI_IF_AVAILABLE OFF)
endif()
AddJsonPackage(httplib)
# cpp-jwt
if (ENABLE_WEB_SERVICE OR ENABLE_UPDATE_CHECKER)
AddJsonPackage(cpp-jwt)
+14 -9
View File
@@ -35,16 +35,21 @@ This file is based off of Yuzu and Dynarmic.
# Do note that situations where multiple architectures are defined
# should NOT be too dependent on the architecture
# otherwise, you may end up with duplicate code
if (CMAKE_OSX_ARCHITECTURES)
if (DEFINED CMAKE_OSX_ARCHITECTURES)
set(MULTIARCH_BUILD 1)
set(ARCHITECTURE "${CMAKE_OSX_ARCHITECTURES}")
# hope and pray the architecture names match
foreach(ARCH IN ${CMAKE_OSX_ARCHITECTURES})
set(ARCHITECTURE_${ARCH} 1 PARENT_SCOPE)
add_definitions(-DARCHITECTURE_${ARCH}=1)
endforeach()
if (IOS)
# TODO: Right... the toolchain file won't properly accomodate OSX_ARCHITECTURE
# they aren't defining it as a list properly I assume?
set(ARCHITECTURE_arm64 1)
add_definitions(-DARCHITECTURE_arm64=1)
else ()
# hope and pray the architecture names match
foreach(ARCH ${CMAKE_OSX_ARCHITECTURES})
set(ARCHITECTURE_${ARCH} 1)
add_definitions(-DARCHITECTURE_${ARCH}=1)
endforeach()
endif()
return()
endif()
@@ -218,4 +223,4 @@ if (NOT DEFINED ARCHITECTURE)
add_definitions(-DARCHITECTURE_GENERIC=1)
endif()
message(STATUS "[DetectArchitecture] Target architecture: ${ARCHITECTURE}")
message(STATUS "[DetectArchitecture] Target architecture: ${ARCHITECTURE}")
+6
View File
@@ -54,6 +54,12 @@ elseif (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
set(CXX_APPLE ON)
endif()
# This fixes some quirks with xcrun or weird iOS toolchain cmake files
if (IOS)
unset(CXX_CLANG)
set(CXX_APPLE ON)
endif()
# https://gitlab.kitware.com/cmake/cmake/-/merge_requests/11112
# This works totally fine on MinGW64, but not CLANG{,ARM}64
if(MINGW AND CXX_CLANG)
+17 -1
View File
@@ -29,6 +29,22 @@ if (NOT YUZU_USE_BUNDLED_FFMPEG)
--extra-ldflags="-nostdlib"
)
set(FFmpeg_IS_CROSS_COMPILING TRUE)
elseif(IOS)
execute_process(COMMAND xcrun --sdk iphoneos --show-sdk-path OUTPUT_VARIABLE SYSROOT)
# Lovely extra newline apple adds that **we** must remove... thank you apple!
string(STRIP "${SYSROOT}" SYSROOT)
set(FFmpeg_CC xcrun --sdk iphoneos clang -arch arm64)
set(FFmpeg_CXX xcrun --sdk iphoneos clang++ -arch arm64)
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS
--arch=arm64
--enable-cross-compile
--sysroot="${SYSROOT}"
--extra-ldflags="-miphoneos-version-min=16.0"
--install-name-dir='@rpath'
--disable-audiotoolbox
--disable-videotoolbox
)
set(FFmpeg_IS_CROSS_COMPILING TRUE)
# User attempts to do a FFmpeg cross compilation because...
# Here we just quickly test against host/system processors not matching
# TODO: Test for versions not matching as well?
@@ -62,7 +78,7 @@ elseif (ANDROID)
--enable-mediacodec
--enable-jni
)
elseif (UNIX AND NOT DEFINED FFmpeg_IS_CROSS_COMPILING AND NOT ANDROID)
elseif (UNIX AND NOT DEFINED FFmpeg_IS_CROSS_COMPILING AND NOT ANDROID AND NOT IOS)
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBVA libva)
pkg_check_modules(CUDA cuda)
+2 -1
View File
@@ -24,7 +24,8 @@ if (MINGW OR LINUX OR APPLE)
message(FATAL_ERROR "Required program `autoconf` not found.")
endif()
find_program(LIBTOOLIZE libtoolize)
find_program(LIBTOOLIZE
NAMES libtoolize glibtoolize)
if ("${LIBTOOLIZE}" STREQUAL "LIBTOOLIZE-NOTFOUND")
message(FATAL_ERROR "Required program `libtoolize` not found.")
endif()
+8
View File
@@ -177,6 +177,9 @@ else()
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-invalid-offsetof>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-unused-parameter>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-missing-field-initializers>)
if (NOT IOS)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-Werror=missing-declarations>)
endif()
if (CXX_CLANG OR CXX_ICC OR CXX_APPLE) # Clang, AppleClang, or Intel C++
if (NOT MSVC)
@@ -295,4 +298,9 @@ if (ANDROID)
target_include_directories(yuzu-android PRIVATE android/app/src/main)
endif()
if (IOS)
add_subdirectory(ios)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-Wno-error>)
endif()
include(GenerateDepHashes)
+2 -2
View File
@@ -788,7 +788,7 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
jstring jprogramId,
jstring jupdatePath) {
const u64 program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
std::string updatePath = Common::Android::GetJString(env, jupdatePath);
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
@@ -796,7 +796,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* en
for (const auto& item : nsp->GetNCAs()) {
for (const auto& nca_details : item.second) {
if (nca_details.second->GetName().ends_with(".cnmt.nca")) {
const auto update_id = FileSys::GetBaseTitleID(nca_details.second->GetTitleId());
auto update_id = nca_details.second->GetTitleId() & ~0xFFFULL;
if (update_id == program_id) {
return true;
}
@@ -289,7 +289,6 @@
<string name="gpu_driver_fetcher">Obtenedor de controladores de la GPU</string>
<string name="gpu_driver_manager">Gestor de controladores de la GPU</string>
<string name="install_gpu_driver_description">Instale los controladores alternativos para obtener un posible mejor rendimiento o precisión</string>
<string name="post_processing_add">Añadir efecto</string>
<string name="post_processing_open_list">Abrir lista</string>
<string name="post_processing_close_list">Cerrar lista</string>
<string name="post_processing_reset">Restablecer a predeterminado</string>
@@ -318,13 +318,6 @@
<string name="frame_gen_target_rate">Целевая частота кадров</string>
<string name="frame_gen_target_rate_description">Выберите значение под ваш дисплей. Множитель подстроится сам, чтобы его держать, и откатит изменения, если игра начнёт тормозить.</string>
<string name="frame_gen_target_rate_off">Использовать фиксированный множитель</string>
<string name="frame_gen_on">Вкл.</string>
<string name="frame_gen_off">Выкл.</string>
<string name="frame_gen_fixed">Фикс.</string>
<string name="frame_gen_flow_auto">Авто</string>
<string name="frame_gen_quick_description">Включите или выключите генерацию кадров и выберите множитель кадров.</string>
<string name="frame_gen_target_rate_quick_description">Генерация кадров с заданной частотой кадров. Множитель регулируется автоматически.</string>
<string name="frame_gen_flow_scale_quick_description">Разрешение, используемое для оценки движения между кадрами. Меньшие значения экономят время ГПУ.</string>
<string name="frame_gen_queue_target">Целевой размер очереди кадров</string>
<string name="frame_gen_queue_target_description">Сколько готовых кадров может ожидать перед выводом на экран. Более длинные очереди сглаживают скачки ГПУ ценой задержки ввода.</string>
<string name="frame_gen_queue_target_0">Минимальная задержка (Без буферизации)</string>
@@ -335,14 +328,12 @@
<string name="frame_gen_flow_scale">Разрешение оценки движения</string>
<string name="frame_gen_flow_scale_description">Разрешение прохода оптического потока в долях от выходного разрешения. Его понижение — самый дешёвый способ вернуть производительность.</string>
<string name="frame_gen_unsupported">Генерация кадров недоступна</string>
<string name="frame_gen_unsupported_description">В этом ГПУ драйвере отсутствует поддержка модели памяти Vulkan или половинной точности (float16), которая требуется шейдерам Lossless Scaling.</string>
<string name="lossless_scaling_setup_description">Опционально. Укажите свой Lossless.dll для включения генерации кадров позже.</string>
<string name="lossless_scaling_install">Установить Lossless.dll</string>
<string name="lossless_scaling_install_description">Для генерации кадров требуется ваша собственная легальная копия Lossless.dll из Lossless Scaling</string>
<string name="lossless_scaling_replace_description">Выбрать другой файл Lossless.dll</string>
<string name="frame_generation_support">Генерация кадров</string>
<string name="frame_generation_supported">Поддерживается</string>
<string name="frame_generation_unsupported">Не поддерживается (нет модели памяти Vulkan или float16)</string>
<string name="lossless_scaling_description">Предоставьте свою копию Lossless.dll для включения генерации кадров</string>
<string name="lossless_scaling_installed">Установлена</string>
<string name="lossless_scaling_not_installed">Не установлена</string>
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_disclaimer">本软件可以运行用于 Nintendo Switch 游戏机的游戏。不包含任何游戏或密钥。<br /><br />在您开始之前, 请定位您设备存储上的 <![CDATA[<b> prod.keys </b>]]> 文件。<br /><br /><![CDATA[<a href=\"https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/QuickStart.md\">了解更多</a>]]></string>
<string name="notice_notification_channel_name">错误与注意事项</string>
<string name="notice_notification_channel_description">当发生错误时显示通知。</string>
<string name="notification_permission_not_granted">未授予通知权限!</string>
@@ -291,25 +290,6 @@
<string name="gpu_driver_fetcher">GPU驱动获取器</string>
<string name="gpu_driver_manager">GPU 驱动管理器</string>
<string name="install_gpu_driver_description">安装替代的驱动程序以获得更好的性能和精度</string>
<string name="post_processing">后处理效果</string>
<string name="post_processing_description">在渲染完成后应用 ReShade FX 效果</string>
<string name="post_processing_per_game_description">配置此游戏的效果链</string>
<string name="post_processing_add">添加效果</string>
<string name="post_processing_remove">移除</string>
<string name="post_processing_open_list">打开列表</string>
<string name="post_processing_close_list">关闭列表</string>
<string name="post_processing_remove_all">移除效果</string>
<string name="post_processing_preset_locked">预设已激活。在开始游戏前可以在后处理效果中编辑它。</string>
<string name="post_processing_preset_modified">数值已从原始预设被更改。</string>
<string name="post_processing_presets">预设</string>
<string name="post_processing_preset_new">新建预设</string>
<string name="post_processing_preset_new_description">把您加载的效果及当前数值保存为一个预设,以便今后选用。</string>
<string name="post_processing_preset_name_invalid">请给预设起一个不含等号的名称。</string>
<string name="post_processing_preset_none">无预设</string>
<string name="post_processing_preset_delete">删除预设</string>
<string name="post_processing_preset_reset">重置预设值</string>
<string name="post_processing_reset">重置为默认值</string>
<string name="post_processing_empty">找不到效果。请在此文件中放置 .fx 文件:</string>
<string name="frame_gen">帧生成</string>
<string name="frame_gen_per_game_description">配置针对此游戏的帧生成设定</string>
<string name="frame_gen_description">设定要在使用无损缩放渲染的帧之间应用的插帧。启用后强制采用FIFO呈现模式 。</string>
@@ -324,13 +304,6 @@
<string name="frame_gen_target_rate_60">60 FPS</string>
<string name="frame_gen_target_rate_90">90 FPS</string>
<string name="frame_gen_target_rate_120">120 FPS</string>
<string name="frame_gen_on">开</string>
<string name="frame_gen_off">关</string>
<string name="frame_gen_fixed">固定</string>
<string name="frame_gen_flow_auto">自动</string>
<string name="frame_gen_quick_description">打开或关闭帧生成并选择帧生成倍数。</string>
<string name="frame_gen_target_rate_quick_description">生成帧到目标帧率。倍率会自动调整。</string>
<string name="frame_gen_flow_scale_quick_description">用来估算帧间运动的分辨率。较低的值可以节省 GPU 时间。</string>
<string name="frame_gen_queue_target">帧队列目标</string>
<string name="frame_gen_queue_target_description">在显示之前有多少已完成渲染的帧正在等待。较大的队列可以缓解 GPU 突发的压力,但会增加输入延迟。</string>
<string name="frame_gen_queue_target_0">最低延迟 (无缓冲)</string>
@@ -341,14 +314,12 @@
<string name="frame_gen_flow_scale">运动预估分辨率</string>
<string name="frame_gen_flow_scale_description">光流通道的分辨率,以输出的比例表示。降低它是提升性能最经济的做法。</string>
<string name="frame_gen_unsupported">帧生成不可用</string>
<string name="frame_gen_unsupported_description">这个 GPU 驱动缺少 Lossless Scaling 着色器所需的 Vulkan 内存模型或半精度 (float16) 支持。</string>
<string name="lossless_scaling_setup_description">作为可选项。请提供您自己的 Lossless.dll 以便在之后可以启用帧生成。</string>
<string name="lossless_scaling_install">安装 Lossless.dll</string>
<string name="lossless_scaling_install_description">帧生成需要您自己从 Lossless Scaling 获得合法的 Lossless.dll 副本</string>
<string name="lossless_scaling_replace_description">选择其它 Lossless.dll 副本</string>
<string name="frame_generation_support">帧生成</string>
<string name="frame_generation_supported">支持</string>
<string name="frame_generation_unsupported">不支持 (无 Vulkan 内存模型或 float16)</string>
<string name="lossless_scaling">无损缩放</string>
<string name="lossless_scaling_description">提供您自己的 Lossless.dll 文件以启用帧生成</string>
<string name="lossless_scaling_installed">已安装</string>
@@ -640,11 +611,6 @@
<string name="log">日志记录</string>
<string name="flush_by_line">按行刷新调试日志</string>
<string name="flush_by_line_description">在每行写入时刷新调试日志,使在崩溃或冻结时调试更容易。</string>
<string name="extended_logging">开启扩展日志</string>
<string name="extended_logging_description">将最大日志文件大小从 100 MiB 提升至 1GiB。</string>
<string name="log_filter">日志筛选器</string>
<string name="log_filter_description">控制 Eden 的日志类别。例如: *:Info Service.LM:Debug</string>
<!-- GPU Logging strings -->
<string name="gpu_logging_header">GPU 日志</string>
<string name="gpu_log_level">日志等级</string>
@@ -928,8 +894,6 @@
<string name="loader_error_file_not_found">ROM 文件不存在</string>
<string name="loader_requires_firmware">游戏需要固件</string>
<string name="loader_requires_firmware_description"><![CDATA[这个游戏可能需要固件才能正常运行,但您还没有安装任何固件。请在启动前安装固件,或者按 “确定” 继续启动。]]></string>
<!-- Intent Launch strings -->
<string name="searching_for_game">正在搜索游戏...</string>
<string name="game_not_found_for_title_id">未找到标题ID的游戏: %1$s</string>
@@ -229,7 +229,7 @@
<string name="add_directory_success">遊戲目錄新增成功</string>
<string name="enable_update_checks">檢查更新</string>
<string name="enable_update_checks_description">在Eden啟動時檢查有無新版本並選擇是否要下載與安裝更新</string>
<string name="update_available">發現新版本</string>
<string name="update_available">偵測到新版本</string>
<string name="update_available_description">有更新可供安裝:%1$s\n\n您要進行更新嗎?</string>
<string name="downloading_update">下載更新中</string>
<string name="update_download_failed">更新下載失敗</string>
@@ -1020,40 +1020,9 @@
<string name="memory_6gb">6GB (不穩定)</string>
<string name="memory_8gb">8GB (不穩定)</string>
<!-- CPU clock levels -->
<string name="clock_normal">正常</string>
<string name="clock_boost">加速</string>
<string name="clock_fast">超頻</string>
<!-- GPU clock levels -->
<string name="fast_gpu_normal">正常</string>
<string name="fast_gpu_medium">加速</string>
<string name="fast_gpu_high">超頻</string>
<!-- GPU swizzle texture size -->
<string name="gpu_texturesizeswizzle_verysmall">極小 (16MB)</string>
<string name="gpu_texturesizeswizzle_small">小(32MB)</string>
<string name="gpu_texturesizeswizzle_normal">正常(128MB)</string>
<string name="gpu_texturesizeswizzle_large">大(256MB)</string>
<string name="gpu_texturesizeswizzle_verylarge">極大(512MB)</string>
<!-- GPU swizzle streams -->
<string name="gpu_swizzle_verylow">極小(4MB)</string>
<string name="gpu_swizzle_low">小(8MB)</string>
<string name="gpu_swizzle_normal">正常(16MB)</string>
<string name="gpu_swizzle_medium">中(32MB)</string>
<string name="gpu_swizzle_high">大(64MB)</string>
<!-- GPU swizzle chunks -->
<string name="gpu_swizzlechunk_verylow">極小(32)</string>
<string name="gpu_swizzlechunk_low">小(64)</string>
<string name="gpu_swizzlechunk_normal">正常(128)</string>
<string name="gpu_swizzlechunk_medium">中(256)</string>
<string name="gpu_swizzlechunk_high">大(512)</string>
<!-- Temperature Units -->
<string name="temperature_celsius">攝氏</string>
<string name="temperature_fahrenheit">華氏</string>
<string name="temperature_celsius">攝氏度</string>
<string name="temperature_fahrenheit">華氏度</string>
<!-- Memory Sizes -->
<string name="memory_byte_shorthand">B</string>
@@ -1066,8 +1035,6 @@
<string name="renderer_none">無</string>
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">效能</string>
<string name="renderer_accuracy_high">準確</string>
<!-- DMA Accuracy -->
+1 -6
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -19,11 +19,6 @@ ADSP::ADSP(Core::System& system, Sink::Sink& sink) {
}
}
void ADSP::Shutdown() {
audio_renderer->Stop();
opus_decoder->Shutdown();
}
AudioRenderer::AudioRenderer& ADSP::AudioRenderer() {
return *audio_renderer;
}
+1 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -45,7 +45,6 @@ public:
explicit ADSP(Core::System& system, Sink::Sink& sink);
~ADSP() = default;
void Shutdown();
AudioRenderer::AudioRenderer& AudioRenderer();
OpusDecoder::OpusDecoder& OpusDecoder();
@@ -34,19 +34,26 @@ void AudioRenderer::Start() {
mailbox.Send(Direction::DSP, Message::InitializeOK);
if (mailbox.Receive(Direction::Host) != Message::InitializeOK) {
LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown message response from ADSP!");
LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown "
"message response from ADSP!");
return;
}
running = true;
}
void AudioRenderer::Stop() {
if (main_thread.joinable()) {
main_thread.request_stop();
mailbox.Send(Direction::DSP, Message::Shutdown);
if (mailbox.Receive(Direction::Host, main_thread.get_stop_token()) != Message::Shutdown) {
LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown message response from ADSP!");
}
main_thread.join();
if (!running) {
return;
}
mailbox.Send(Direction::DSP, Message::Shutdown);
if (mailbox.Receive(Direction::Host) != Message::Shutdown) {
LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown "
"message response from ADSP!");
}
main_thread.request_stop();
main_thread.join();
for (auto& stream : streams) {
if (stream) {
stream->Stop();
@@ -54,6 +61,7 @@ void AudioRenderer::Stop() {
stream = nullptr;
}
}
running = false;
}
void AudioRenderer::Signal() {
@@ -61,12 +69,13 @@ void AudioRenderer::Signal() {
Send(Direction::DSP, Message::Render);
}
void AudioRenderer::Wait(std::stop_token stop_token) {
auto msg = mailbox.Receive(Direction::Host, stop_token);
void AudioRenderer::Wait() {
auto msg = Receive(Direction::Host);
if (msg != Message::RenderResponse) {
LOG_ERROR(Service_Audio,
"Did not receive the expected render response from the AudioRenderer! Expected "
"{}, got {}", Message::RenderResponse, msg);
"Did not receive the expected render response from the AudioRenderer! Expected "
"{}, got {}",
Message::RenderResponse, msg);
}
PostDSPClearCommandBuffer();
}
@@ -138,9 +147,7 @@ void AudioRenderer::Main(std::stop_token stop_token) {
constexpr u64 max_process_time{2'304'000ULL};
while (!stop_token.stop_requested()) {
auto msg = mailbox.Receive(Direction::DSP, stop_token);
if (stop_token.stop_requested())
break;
auto msg{mailbox.Receive(Direction::DSP)};
switch (msg) {
case Message::Shutdown:
mailbox.Send(Direction::Host, Message::Shutdown);
@@ -63,15 +63,13 @@ public:
*/
void Start();
void NotifyShutdown();
/**
* Stop the AudioRenderer.
*/
void Stop();
void Signal();
void Wait(std::stop_token stop_token);
void Wait();
void Send(Direction dir, u32 message);
u32 Receive(Direction dir);
@@ -104,6 +102,8 @@ private:
Mailbox mailbox;
/// Main thread
std::jthread main_thread{};
/// The current state
std::atomic<bool> running{};
/// Shared memory of input command buffers, set by host, read by DSP
std::array<CommandBuffer, MaxRendererSessions> command_buffers{};
/// The command lists to process
+23 -33
View File
@@ -41,16 +41,19 @@ OpusDecoder::OpusDecoder(Core::System& system_) : system{system_} {
}
OpusDecoder::~OpusDecoder() {
if (main_thread.joinable()) {
// Shutdown the thread
Send(Direction::DSP, Message::Shutdown);
main_thread.request_stop();
auto msg = Receive(Direction::Host, main_thread.get_stop_token());
ASSERT_MSG(msg == Message::ShutdownOK, "Expected Opus shutdown code {}, got {}", Message::ShutdownOK, msg);
main_thread.join();
} else {
if (!running) {
init_thread.request_stop();
return;
}
// Shutdown the thread
Send(Direction::DSP, Message::Shutdown);
auto msg = Receive(Direction::Host);
ASSERT_MSG(msg == Message::ShutdownOK, "Expected Opus shutdown code {}, got {}",
Message::ShutdownOK, msg);
main_thread.request_stop();
main_thread.join();
running = false;
}
void OpusDecoder::Send(Direction dir, u32 message) {
@@ -70,6 +73,7 @@ void OpusDecoder::Init(std::stop_token stop_token) {
return;
}
main_thread = std::jthread([this](std::stop_token st) { Main(st); });
running = true;
Send(Direction::Host, Message::StartOK);
}
@@ -78,15 +82,12 @@ void OpusDecoder::Main(std::stop_token stop_token) {
while (!stop_token.stop_requested()) {
auto msg = Receive(Direction::DSP, stop_token);
if (stop_token.stop_requested())
break;
switch (msg) {
case Message::Shutdown:
case Shutdown:
Send(Direction::Host, Message::ShutdownOK);
return;
case Message::GetWorkBufferSize: {
case GetWorkBufferSize: {
auto channel_count = static_cast<s32>(shared_memory->host_send_data[0]);
ASSERT(IsValidChannelCount(channel_count));
@@ -95,7 +96,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
Send(Direction::Host, Message::GetWorkBufferSizeOK);
} break;
case Message::InitializeDecodeObject: {
case InitializeDecodeObject: {
auto buffer = shared_memory->host_send_data[0];
auto buffer_size = shared_memory->host_send_data[1];
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
@@ -112,7 +113,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
Send(Direction::Host, Message::InitializeDecodeObjectOK);
} break;
case Message::ShutdownDecodeObject: {
case ShutdownDecodeObject: {
auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
@@ -122,7 +123,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
Send(Direction::Host, Message::ShutdownDecodeObjectOK);
} break;
case Message::DecodeInterleaved: {
case DecodeInterleaved: {
auto start_time = system.CoreTiming().GetGlobalTimeUs();
auto buffer = shared_memory->host_send_data[0];
@@ -160,19 +161,19 @@ void OpusDecoder::Main(std::stop_token stop_token) {
Send(Direction::Host, Message::DecodeInterleavedOK);
} break;
case Message::MapMemory: {
case MapMemory: {
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
Send(Direction::Host, Message::MapMemoryOK);
} break;
case Message::UnmapMemory: {
case UnmapMemory: {
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
Send(Direction::Host, Message::UnmapMemoryOK);
} break;
case Message::GetWorkBufferSizeForMultiStream: {
case GetWorkBufferSizeForMultiStream: {
auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[0]);
auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[1]);
@@ -183,7 +184,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK);
} break;
case Message::InitializeMultiStreamDecodeObject: {
case InitializeMultiStreamDecodeObject: {
auto buffer = shared_memory->host_send_data[0];
auto buffer_size = shared_memory->host_send_data[1];
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
@@ -210,7 +211,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK);
} break;
case Message::ShutdownMultiStreamDecodeObject: {
case ShutdownMultiStreamDecodeObject: {
auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
@@ -220,7 +221,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK);
} break;
case Message::DecodeInterleavedForMultiStream: {
case DecodeInterleavedForMultiStream: {
auto start_time = system.CoreTiming().GetGlobalTimeUs();
auto buffer = shared_memory->host_send_data[0];
@@ -265,15 +266,4 @@ void OpusDecoder::Main(std::stop_token stop_token) {
}
}
void OpusDecoder::Shutdown() {
if (init_thread.joinable()) {
init_thread.request_stop();
init_thread.join(); //must explicitly wait for init to join
}
if (main_thread.joinable()) {
main_thread.request_stop();
Send(Direction::DSP, Message::Shutdown);
}
}
} // namespace AudioCore::ADSP::OpusDecoder
+3 -3
View File
@@ -57,7 +57,7 @@ public:
~OpusDecoder();
bool IsRunning() const noexcept {
return main_thread.joinable();
return running;
}
void Send(Direction dir, u32 message);
@@ -67,8 +67,6 @@ public:
shared_memory = &shared_memory_;
}
void Shutdown();
private:
/**
* Initializing thread, launched at audio_core boot to avoid blocking the main emu boot thread.
@@ -87,6 +85,8 @@ private:
std::jthread init_thread{};
/// Main thread
std::jthread main_thread{};
/// The current state
bool running{};
/// Structure shared with the host, input data set by the host before sending a mailbox message,
/// and the responses are written back by the OpusDecoder.
SharedMemory* shared_memory{};
-1
View File
@@ -33,7 +33,6 @@ void AudioCore::CreateSinks() {
void AudioCore::Shutdown() {
audio_manager->Shutdown();
adsp->Shutdown();
}
AudioManager& AudioCore::GetAudioManager() {
+2 -2
View File
@@ -17,7 +17,7 @@ AudioManager::AudioManager() {
std::unique_lock l{events.GetAudioEventLock()};
events.ClearEvents();
while (!stop_token.stop_requested()) {
const auto timed_out = events.Wait(l, std::chrono::seconds{2});
const auto timed_out{events.Wait(l, std::chrono::seconds(2))};
if (events.CheckAudioEventSet(Event::Type::Max)) {
break;
}
@@ -35,9 +35,9 @@ AudioManager::AudioManager() {
}
void AudioManager::Shutdown() {
events.SetAudioEvent(Event::Type::Max, true);
if (thread.joinable()) {
thread.request_stop();
events.SetAudioEvent(Event::Type::Max, true);
thread.join();
}
}
+3 -1
View File
@@ -39,7 +39,9 @@ class AudioManager {
public:
explicit AudioManager();
/// @brief Shutdown the audio manager.
/**
* Shutdown the audio manager.
*/
void Shutdown();
/**
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -38,7 +35,7 @@ void CircularBufferSinkInfo::Update(BehaviorInfo::ErrorInfo& error_info, OutStat
auto current_params{reinterpret_cast<CircularBufferInParameter*>(parameter.data())};
auto current_state{reinterpret_cast<CircularBufferState*>(state.data())};
if (in_use == bool(buffer_params->in_use) && !buffer_unmapped) {
if (in_use == buffer_params->in_use && !buffer_unmapped) {
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
out_status.writeOffset = current_state->last_pos2;
+20 -19
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@@ -31,10 +31,10 @@ public:
};
struct DeviceInParameter {
/* 0x000 */ u8 name[0x100];
/* 0x000 */ char name[0x100];
/* 0x100 */ u32 input_count;
/* 0x104 */ std::array<s8, MaxChannels> inputs;
/* 0x10A */ u8 unk10A[0x1];
/* 0x10A */ char unk10A[0x1];
/* 0x10B */ bool downmix_enabled;
/* 0x10C */ std::array<f32, 4> downmix_coeff;
};
@@ -43,7 +43,7 @@ public:
struct DeviceState {
/* 0x00 */ UpsamplerInfo* upsampler_info;
/* 0x08 */ std::array<Common::FixedPoint<16, 16>, 4> downmix_coeff;
/* 0x18 */ u8 unk18[0x18];
/* 0x18 */ char unk18[0x18];
};
static_assert(sizeof(DeviceState) == 0x30, "DeviceState has the wrong size!");
@@ -55,8 +55,8 @@ public:
/* 0x14 */ u32 previous_pos;
/* 0x18 */ SampleFormat format;
/* 0x1C */ std::array<s8, MaxChannels> inputs;
/* 0x22 */ u8 in_use;
/* 0x23 */ u8 unk23[0x5];
/* 0x22 */ bool in_use;
/* 0x23 */ char unk23[0x5];
};
static_assert(sizeof(CircularBufferInParameter) == 0x28,
"CircularBufferInParameter has the wrong size!");
@@ -65,16 +65,16 @@ public:
/* 0x00 */ u32 last_pos2;
/* 0x04 */ s32 current_pos;
/* 0x08 */ u32 last_pos;
/* 0x0C */ u8 unk0C[0x4];
/* 0x0C */ char unk0C[0x4];
/* 0x10 */ AddressInfo address_info;
};
static_assert(sizeof(CircularBufferState) == 0x30, "CircularBufferState has the wrong size!");
struct InParameter {
/* 0x000 */ Type type;
/* 0x001 */ u8 in_use;
/* 0x001 */ bool in_use;
/* 0x004 */ u32 node_id;
/* 0x008 */ u8 unk08[0x18];
/* 0x008 */ char unk08[0x18];
union {
/* 0x020 */ DeviceInParameter device;
/* 0x020 */ CircularBufferInParameter circular_buffer;
@@ -84,7 +84,7 @@ public:
struct OutStatus {
/* 0x00 */ u32 writeOffset;
/* 0x04 */ u8 unk04[0x1C];
/* 0x04 */ char unk04[0x1C];
}; // size == 0x20
static_assert(sizeof(OutStatus) == 0x20, "SinkInfoBase::OutStatus has the wrong size!");
@@ -162,18 +162,19 @@ public:
u8* GetParameter();
protected:
/// Type of this sink
Type type{Type::Invalid};
/// Is this sink in use?
bool in_use{};
/// Is this sink's buffer unmapped? Circular only
bool buffer_unmapped{};
/// Node id for this sink
u32 node_id{};
/// State buffer for this sink
std::array<u8, (std::max)(sizeof(DeviceState), sizeof(CircularBufferState))> state{};
/// Parameter buffer for this sink
std::array<u8, (std::max)(sizeof(DeviceInParameter), sizeof(CircularBufferInParameter))> parameter{};
/// Type of this sink
Type type{Type::Invalid};
/// Node id for this sink
u32 node_id{};
/// Is this sink in use?
bool in_use : 1 = false;
/// Is this sink's buffer unmapped? Circular only
bool buffer_unmapped : 1 = false;
std::array<u8, (std::max)(sizeof(DeviceInParameter), sizeof(CircularBufferInParameter))>
parameter{};
};
} // namespace AudioCore::Renderer
+14 -14
View File
@@ -100,9 +100,8 @@ u64 System::GetWorkBufferSize(const AudioRendererParameterInternal& params) {
}
System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_)
: core{core_}
, adsp_rendered_event{adsp_rendered_event_}
{}
: core{core_}, audio_renderer{core.AudioCore().ADSP().AudioRenderer()},
adsp_rendered_event{adsp_rendered_event_} {}
Result System::Initialize(const AudioRendererParameterInternal& params,
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
@@ -407,7 +406,7 @@ void System::Finalize() {
return;
}
if (IsActive()) {
if (active) {
Stop();
}
@@ -434,12 +433,14 @@ void System::Start() {
std::scoped_lock l{lock};
frames_elapsed = 0;
state = State::Started;
active = true;
}
void System::Stop() {
{
std::scoped_lock l{lock};
state = State::Stopped;
active = false;
}
if (execution_mode == ExecutionMode::Auto) {
@@ -479,7 +480,7 @@ Result System::Update(std::span<const u8> input, std::span<u8> performance, std:
return result;
}
result = info_updater.UpdateEffects(effect_context, IsActive(), memory_pool_workbuffer,
result = info_updater.UpdateEffects(effect_context, active, memory_pool_workbuffer,
memory_pool_count);
if (result.IsError()) {
LOG_ERROR(Service_Audio, "Failed to update Effects!");
@@ -581,16 +582,16 @@ u32 System::GetRenderingDevice() const {
}
bool System::IsActive() const {
return state == State::Started;
return active;
}
void System::SendCommandToDsp() {
std::scoped_lock l{lock};
if (initialized) {
if (IsActive()) {
if (active) {
terminate_event.Reset();
const auto remaining_command_count = core.AudioCore().ADSP().AudioRenderer().GetRemainCommandCount(session_id);
const auto remaining_command_count{audio_renderer.GetRemainCommandCount(session_id)};
u64 command_size{0};
if (remaining_command_count) {
@@ -617,17 +618,16 @@ void System::SendCommandToDsp() {
auto time_limit{
static_cast<u64>((time_limit_percent / 100) * 2'880'000.0 *
(static_cast<f32>(render_time_limit_percent) / 100.0f))};
core.AudioCore().ADSP().AudioRenderer().SetCommandBuffer(session_id,
translated_addr, command_size,
time_limit, applet_resource_user_id,
process_handle, reset_command_buffers);
audio_renderer.SetCommandBuffer(session_id, translated_addr, command_size, time_limit,
applet_resource_user_id, process_handle,
reset_command_buffers);
reset_command_buffers = false;
command_buffer_size = command_size;
if (remaining_command_count == 0) {
adsp_rendered_event->Signal(core.Kernel());
}
} else {
core.AudioCore().ADSP().AudioRenderer().ClearRemainCommandCount(session_id);
audio_renderer.ClearRemainCommandCount(session_id);
terminate_event.Set();
}
}
@@ -738,7 +738,7 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer,
const auto end_time{core.CoreTiming().GetGlobalTimeNs().count()};
total_ticks_elapsed += end_time - start_time;
num_command_lists_generated++;
render_start_tick = core.AudioCore().ADSP().AudioRenderer().GetRenderingStartTick(session_id);
render_start_tick = audio_renderer.GetRenderingStartTick(session_id);
frames_elapsed++;
return command_buffer.size;
+2
View File
@@ -219,6 +219,8 @@ public:
private:
/// Core system
Core::System& core;
/// Reference to the ADSP's AudioRenderer for communication
::AudioCore::ADSP::AudioRenderer::AudioRenderer& audio_renderer;
/// Is this system initialized?
bool initialized{};
/// Is this system currently active?
+29 -26
View File
@@ -11,70 +11,73 @@
#include "audio_core/renderer/system_manager.h"
#include "common/thread.h"
#include "core/core.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/core_timing.h"
namespace AudioCore::Renderer {
SystemManager::SystemManager(Core::System& core_)
: core{core_}
{
}
: core{core_}, audio_renderer{core.AudioCore().ADSP().AudioRenderer()} {}
SystemManager::~SystemManager() {
Stop();
}
void SystemManager::InitializeUnsafe() {
if (!thread.joinable()) {
core.AudioCore().ADSP().AudioRenderer().Start();
thread = core.Kernel().RunOnHostCoreThread("AudioRenderSystemManager", [this]() {
auto& audio_renderer = core.AudioCore().ADSP().AudioRenderer();
auto const stop_token = thread.get_stop_token();
while (!stop_token.stop_requested()) {
if (!active) {
active = true;
audio_renderer.Start();
thread = std::jthread([this](std::stop_token stop_token) {
Common::SetCurrentThreadName("AudioRenderSystemManager");
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
while (active && !stop_token.stop_requested()) {
{
std::scoped_lock lk{mutex};
std::scoped_lock l{mutex1};
for (auto system : systems)
system->SendCommandToDsp();
}
audio_renderer.Signal();
audio_renderer.Wait(stop_token);
audio_renderer.Wait();
}
});
}
}
void SystemManager::Stop() {
core.AudioCore().ADSP().AudioRenderer().Stop();
if (thread.joinable()) {
if (active) {
active = false;
thread.request_stop();
thread.join();
audio_renderer.Stop();
}
}
bool SystemManager::Add(System& system_) {
std::scoped_lock lk{mutex};
if (systems.size() >= MaxRendererSessions) {
std::scoped_lock l2{mutex2};
if (systems.size() + 1 > MaxRendererSessions) {
LOG_ERROR(Service_Audio, "Maximum AudioRenderer Systems active, cannot add more!");
return false;
}
systems.push_back(&system_);
if (!systems.empty()) {
InitializeUnsafe();
{
std::scoped_lock l{mutex1};
if (systems.empty())
InitializeUnsafe();
}
systems.push_back(&system_);
return true;
}
bool SystemManager::Remove(System& system_) {
std::scoped_lock lk{mutex};
if (systems.remove(&system_) == 0) {
LOG_ERROR(Service_Audio, "Failed to remove a render system, it was not found in the list!");
return false;
std::scoped_lock l2{mutex2};
{
std::scoped_lock l{mutex1};
if (systems.remove(&system_) == 0) {
LOG_ERROR(Service_Audio, "Failed to remove a render system, it was not found in the list!");
return false;
}
}
if (systems.empty()) {
if (systems.empty())
Stop();
}
return true;
}
+8 -2
View File
@@ -75,8 +75,14 @@ private:
std::list<System*> systems{};
/// Main worker thread for generating command lists
std::jthread thread;
/// Mutex for the reading and adding/removing systems
std::mutex mutex{};
/// Mutex for the systems
std::mutex mutex1{};
/// Mutex for adding/removing systems
std::mutex mutex2{};
/// Is the system manager thread active?
std::atomic<bool> active{};
/// Reference to the ADSP's AudioRenderer for communication
::AudioCore::ADSP::AudioRenderer::AudioRenderer& audio_renderer;
};
} // namespace AudioCore::Renderer
+6 -6
View File
@@ -109,6 +109,12 @@ private:
if (read_index == producer.index.load(std::memory_order::acquire)) {
return false;
}
} else if constexpr (Mode == PopMode::Wait) {
// Wait until the queue is not empty.
std::unique_lock lock{consumer.cv_mutex};
consumer.cv.wait(lock, [this, read_index] {
return read_index != producer.index.load(std::memory_order::acquire);
});
} else if constexpr (Mode == PopMode::WaitWithStopToken) {
// Wait until the queue is not empty.
std::unique_lock lock{consumer.cv_mutex};
@@ -118,12 +124,6 @@ private:
if (stop_token.stop_requested()) {
return false;
}
} else if constexpr (Mode == PopMode::Wait) {
// Wait until the queue is not empty.
std::unique_lock lock{consumer.cv_mutex};
consumer.cv.wait(lock, [this, read_index] {
return read_index != producer.index.load(std::memory_order::acquire);
});
} else {
static_assert(Mode < PopMode::Count, "Invalid PopMode.");
}
+9 -5
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "device_power_state.h"
@@ -14,11 +14,14 @@ extern std::atomic<bool> g_has_battery;
#elif defined(__APPLE__)
#include <TargetConditionals.h>
#if TARGET_OS_MAC
#if defined(TARGET_OS_MAC) && TARGET_OS_MAC
#if TARGET_OS_IPHONE
// ios doesnt have this
#else
#include <IOKit/ps/IOPSKeys.h>
#include <IOKit/ps/IOPowerSources.h>
#endif
#endif
#elif defined(__linux__)
#include <fstream>
#include <string>
@@ -48,7 +51,9 @@ namespace Common {
info.percentage = g_battery_percentage.load(std::memory_order_relaxed);
info.charging = g_is_charging.load(std::memory_order_relaxed);
info.has_battery = g_has_battery.load(std::memory_order_relaxed);
#elif defined(__APPLE__) && TARGET_OS_IPHONE
// Not implemented
info.has_battery = false;
#elif defined(__APPLE__) && TARGET_OS_MAC
CFTypeRef info_ref = IOPSCopyPowerSourcesInfo();
CFArrayRef sources = IOPSCopyPowerSourcesList(info_ref);
@@ -96,7 +101,6 @@ namespace Common {
#else
info.has_battery = false;
#endif
return info;
}
}
+20 -18
View File
@@ -17,33 +17,35 @@
namespace Common {
// glibc, mlibc, musl, and newlib all define their own variants of strerror_r
// We don't need to use the preprocessor, we can just select depending on return type
template<typename T> std::string HandleStrerrorR(T r, char *err_str);
template<> std::string HandleStrerrorR(char* r, char *) { return std::string{r}; }
template<> std::string HandleStrerrorR(const char* r, char *) { return std::string{r}; }
template<> std::string HandleStrerrorR(int r, char *err_str) {
return std::string{r != 0
? "(strerror_r failed to format error)"
: err_str};
}
std::string NativeErrorToString(int e) {
#ifdef _WIN32
LPSTR err_str;
DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
LPSTR(&err_str), 1, nullptr);
if (res) {
std::string ret(err_str);
LocalFree(err_str);
return ret;
reinterpret_cast<LPSTR>(&err_str), 1, nullptr);
if (!res) {
return "(FormatMessageA failed to format error)";
}
return "(FormatMessageA failed to format error)";
std::string ret(err_str);
LocalFree(err_str);
return ret;
#else
char err_str[255];
return HandleStrerrorR(strerror_r(e, err_str, sizeof(err_str)), err_str);
#if defined(__ANDROID__) || \
(defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600)))
// Thread safe (GNU-specific)
const char* str = strerror_r(e, err_str, sizeof(err_str));
return std::string(str);
#else
// Thread safe (XSI-compliant)
int second_err = strerror_r(e, err_str, sizeof(err_str));
if (second_err != 0) {
return "(strerror_r failed to format error)";
}
return std::string(err_str);
#endif // GLIBC etc.
#endif // _WIN32
}
+4
View File
@@ -27,7 +27,11 @@
#include <sys/random.h>
#elif defined(__APPLE__)
#include <sys/types.h>
#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
// Not available on iOS for some fucking stupid reason...
#else
#include <sys/random.h>
#endif
#include <mach/vm_map.h>
#include <mach/mach.h>
#elif defined(__FreeBSD__)
+107 -6
View File
@@ -116,18 +116,119 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st
}
std::string UTF16ToUTF8(std::u16string_view input) {
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
return convert.to_bytes(input.data(), input.data() + input.size());
std::string result;
result.reserve(input.size());
for (size_t i = 0; i < input.size(); ++i) {
uint32_t codepoint = input[i];
// Handle surrogate pairs
if (codepoint >= 0xD800 && codepoint <= 0xDBFF) {
if (i + 1 < input.size()) {
uint32_t low = input[i + 1];
if (low >= 0xDC00 && low <= 0xDFFF) {
codepoint = ((codepoint - 0xD800) << 10) + (low - 0xDC00) + 0x10000;
++i;
}
}
}
if (codepoint <= 0x7F) {
result.push_back(static_cast<char>(codepoint));
} else if (codepoint <= 0x7FF) {
result.push_back(static_cast<char>(0xC0 | (codepoint >> 6)));
result.push_back(static_cast<char>(0x80 | (codepoint & 0x3F)));
} else if (codepoint <= 0xFFFF) {
result.push_back(static_cast<char>(0xE0 | (codepoint >> 12)));
result.push_back(static_cast<char>(0x80 | ((codepoint >> 6) & 0x3F)));
result.push_back(static_cast<char>(0x80 | (codepoint & 0x3F)));
} else {
result.push_back(static_cast<char>(0xF0 | (codepoint >> 18)));
result.push_back(static_cast<char>(0x80 | ((codepoint >> 12) & 0x3F)));
result.push_back(static_cast<char>(0x80 | ((codepoint >> 6) & 0x3F)));
result.push_back(static_cast<char>(0x80 | (codepoint & 0x3F)));
}
}
return result;
}
std::u16string UTF8ToUTF16(std::string_view input) {
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
return convert.from_bytes(input.data(), input.data() + input.size());
std::u16string result;
size_t i = 0;
while (i < input.size()) {
uint32_t codepoint = 0;
unsigned char c = input[i];
size_t extra = 0;
if ((c & 0x80) == 0) {
codepoint = c;
extra = 0;
} else if ((c & 0xE0) == 0xC0) {
codepoint = c & 0x1F;
extra = 1;
} else if ((c & 0xF0) == 0xE0) {
codepoint = c & 0x0F;
extra = 2;
} else if ((c & 0xF8) == 0xF0) {
codepoint = c & 0x07;
extra = 3;
} else {
// Invalid UTF-8
++i;
continue;
}
if (i + extra >= input.size()) break;
for (size_t j = 1; j <= extra; ++j) {
if ((input[i + j] & 0xC0) != 0x80) {
codepoint = 0xFFFD;
break;
}
codepoint = (codepoint << 6) | (input[i + j] & 0x3F);
}
if (codepoint <= 0xFFFF) {
result.push_back(static_cast<char16_t>(codepoint));
} else {
codepoint -= 0x10000;
result.push_back(static_cast<char16_t>(0xD800 + (codepoint >> 10)));
result.push_back(static_cast<char16_t>(0xDC00 + (codepoint & 0x3FF)));
}
i += extra + 1;
}
return result;
}
std::u32string UTF8ToUTF32(std::string_view input) {
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert;
return convert.from_bytes(input.data(), input.data() + input.size());
std::u32string result;
size_t i = 0;
while (i < input.size()) {
uint32_t codepoint = 0;
unsigned char c = input[i];
size_t extra = 0;
if ((c & 0x80) == 0) {
codepoint = c;
extra = 0;
} else if ((c & 0xE0) == 0xC0) {
codepoint = c & 0x1F;
extra = 1;
} else if ((c & 0xF0) == 0xE0) {
codepoint = c & 0x0F;
extra = 2;
} else if ((c & 0xF8) == 0xF0) {
codepoint = c & 0x07;
extra = 3;
} else {
// Invalid UTF-8
++i;
continue;
}
if (i + extra >= input.size()) break;
for (size_t j = 1; j <= extra; ++j) {
if ((input[i + j] & 0xC0) != 0x80) {
codepoint = 0xFFFD;
break;
}
codepoint = (codepoint << 6) | (input[i + j] & 0x3F);
}
result.push_back(codepoint);
i += extra + 1;
}
return result;
}
#ifdef _WIN32
+4
View File
@@ -1261,6 +1261,10 @@ if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITE
hle/service/jit/jit.cpp
hle/service/jit/jit.h)
target_link_libraries(core PRIVATE dynarmic::dynarmic)
# Quick hack for XCode generator...
if (IOS)
target_include_directories(core PRIVATE "${CMAKE_SOURCE_DIR}/dynarmic/src")
endif()
endif()
if (TARGET OpenSSL::SSL)
+1 -1
View File
@@ -6,7 +6,7 @@
#pragma once
#include <dynarmic/interface/halt_reason.h>
#include "dynarmic/src/dynarmic/interface/halt_reason.h"
#include "core/arm/arm_interface.h"
+34 -18
View File
@@ -22,15 +22,21 @@ DynarmicCallbacks32::DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
{}
u64 DynarmicCallbacks32::MemoryRead(u32 vaddr, size_t size) {
CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read);
switch (size) {
case sizeof(u64): return m_memory.Read64(vaddr);
case sizeof(u32): return m_memory.Read32(vaddr);
case sizeof(u16): return m_memory.Read16(vaddr);
case sizeof(u8): return m_memory.Read8(vaddr);
default: UNREACHABLE();
}
u8 DynarmicCallbacks32::MemoryRead8(u32 vaddr) {
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
return m_memory.Read8(vaddr);
}
u16 DynarmicCallbacks32::MemoryRead16(u32 vaddr) {
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
return m_memory.Read16(vaddr);
}
u32 DynarmicCallbacks32::MemoryRead32(u32 vaddr) {
CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
return m_memory.Read32(vaddr);
}
u64 DynarmicCallbacks32::MemoryRead64(u32 vaddr) {
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
return m_memory.Read64(vaddr);
}
std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) {
@@ -44,17 +50,27 @@ std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) {
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
}
void DynarmicCallbacks32::MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) {
if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) {
switch (size) {
case sizeof(u64): return m_memory.Write64(vaddr, value);
case sizeof(u32): return m_memory.Write32(vaddr, u32(value));
case sizeof(u16): return m_memory.Write16(vaddr, u16(value));
case sizeof(u8): return m_memory.Write8(vaddr, u8(value));
default: UNREACHABLE();
}
void DynarmicCallbacks32::MemoryWrite8(u32 vaddr, u8 value) {
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
m_memory.Write8(vaddr, value);
}
}
void DynarmicCallbacks32::MemoryWrite16(u32 vaddr, u16 value) {
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
m_memory.Write16(vaddr, value);
}
}
void DynarmicCallbacks32::MemoryWrite32(u32 vaddr, u32 value) {
if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
m_memory.Write32(vaddr, value);
}
}
void DynarmicCallbacks32::MemoryWrite64(u32 vaddr, u64 value) {
if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
m_memory.Write64(vaddr, value);
}
}
bool DynarmicCallbacks32::MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) {
return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
m_memory.WriteExclusive8(vaddr, value, expected);
+10 -4
View File
@@ -6,8 +6,8 @@
#pragma once
#include <dynarmic/interface/A32/a32.h>
#include <dynarmic/interface/code_page.h>
#include "dynarmic/src/dynarmic/interface/A32/a32.h"
#include "dynarmic/src/dynarmic/interface/code_page.h"
#include "core/arm/arm_interface.h"
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
@@ -30,12 +30,18 @@ class System;
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
public:
explicit DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess* process);
u64 MemoryRead(u32 vaddr, size_t size) override;
u8 MemoryRead8(u32 vaddr) override;
u16 MemoryRead16(u32 vaddr) override;
u32 MemoryRead32(u32 vaddr) override;
u64 MemoryRead64(u32 vaddr) override;
std::optional<u32> MemoryReadCode(u32 vaddr) override;
void InstructionSynchronizationBarrierRaised() override {
last_code_addr = u64(-1); //reset back, force refetch
}
void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) override;
void MemoryWrite8(u32 vaddr, u8 value) override;
void MemoryWrite16(u32 vaddr, u16 value) override;
void MemoryWrite32(u32 vaddr, u32 value) override;
void MemoryWrite64(u32 vaddr, u64 value) override;
bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override;
bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override;
bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override;
+33 -19
View File
@@ -10,7 +10,6 @@
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
#include "core/core_timing.h"
#include "core/hle/kernel/k_process.h"
#include "dynarmic/interface/A64/config.h"
namespace Core {
@@ -22,15 +21,21 @@ DynarmicCallbacks64::DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
{}
u64 DynarmicCallbacks64::MemoryRead(u64 vaddr, size_t size) {
CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read);
switch (size) {
case sizeof(u64): return m_memory.Read64(vaddr);
case sizeof(u32): return m_memory.Read32(vaddr);
case sizeof(u16): return m_memory.Read16(vaddr);
case sizeof(u8): return m_memory.Read8(vaddr);
default: UNREACHABLE();
}
u8 DynarmicCallbacks64::MemoryRead8(u64 vaddr) {
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
return m_memory.Read8(vaddr);
}
u16 DynarmicCallbacks64::MemoryRead16(u64 vaddr) {
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
return m_memory.Read16(vaddr);
}
u32 DynarmicCallbacks64::MemoryRead32(u64 vaddr) {
CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
return m_memory.Read32(vaddr);
}
u64 DynarmicCallbacks64::MemoryRead64(u64 vaddr) {
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
return m_memory.Read64(vaddr);
}
Dynarmic::A64::Vector DynarmicCallbacks64::MemoryRead128(u64 vaddr) {
CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read);
@@ -48,15 +53,24 @@ std::optional<u32> DynarmicCallbacks64::MemoryReadCode(u64 vaddr) {
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
}
void DynarmicCallbacks64::MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) {
if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) {
switch (size) {
case sizeof(u64): return m_memory.Write64(vaddr, u64(value));
case sizeof(u32): return m_memory.Write32(vaddr, u32(value));
case sizeof(u16): return m_memory.Write16(vaddr, u16(value));
case sizeof(u8): return m_memory.Write8(vaddr, u8(value));
default: UNREACHABLE();
}
void DynarmicCallbacks64::MemoryWrite8(u64 vaddr, u8 value) {
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
m_memory.Write8(vaddr, value);
}
}
void DynarmicCallbacks64::MemoryWrite16(u64 vaddr, u16 value) {
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
m_memory.Write16(vaddr, value);
}
}
void DynarmicCallbacks64::MemoryWrite32(u64 vaddr, u32 value) {
if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
m_memory.Write32(vaddr, value);
}
}
void DynarmicCallbacks64::MemoryWrite64(u64 vaddr, u64 value) {
if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
m_memory.Write64(vaddr, value);
}
}
void DynarmicCallbacks64::MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) {
+14 -9
View File
@@ -10,13 +10,12 @@
#include <memory>
#include "common/container/unordered_map.h"
#include <dynarmic/interface/A64/a64.h>
#include <dynarmic/interface/code_page.h>
#include "common/common_types.h"
#include "common/hash.h"
#include "core/arm/arm_interface.h"
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
#include "dynarmic/interface/A64/config.h"
#include "../../../dynarmic/src/dynarmic/interface/A64/a64.h"
#include "../../../dynarmic/src/dynarmic/interface/code_page.h"
#include "../../../common/common_types.h"
#include "../../../common/hash.h"
#include "../arm_interface.h"
#include "dynarmic_exclusive_monitor.h"
namespace Core::Memory {
class Memory;
@@ -37,13 +36,19 @@ class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
public:
explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process);
u64 MemoryRead(u64 vaddr, size_t size) override;
u8 MemoryRead8(u64 vaddr) override;
u16 MemoryRead16(u64 vaddr) override;
u32 MemoryRead32(u64 vaddr) override;
u64 MemoryRead64(u64 vaddr) override;
Dynarmic::A64::Vector MemoryRead128(u64 vaddr) override;
std::optional<u32> MemoryReadCode(u64 vaddr) override;
void InstructionSynchronizationBarrierRaised() override {
last_code_addr = u64(-1); //reset back, force refetch
}
void MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) override;
void MemoryWrite8(u64 vaddr, u8 value) override;
void MemoryWrite16(u64 vaddr, u16 value) override;
void MemoryWrite32(u64 vaddr, u32 value) override;
void MemoryWrite64(u64 vaddr, u64 value) override;
void MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) override;
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override;
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override;
+4 -1
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -5,7 +8,7 @@
#include <optional>
#include <dynarmic/interface/A32/coprocessor.h>
#include "dynarmic/interface/A32/coprocessor.h"
#include "common/common_types.h"
namespace Core {
@@ -1,9 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <dynarmic/interface/exclusive_monitor.h>
#include "dynarmic/src/dynarmic/interface/exclusive_monitor.h"
#include "common/common_types.h"
#include "core/arm/exclusive_monitor.h"
+6 -3
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2023 merryhime <https://mary.rs>
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -7,9 +10,9 @@
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#include <dynarmic/frontend/A64/a64_types.h>
#include <dynarmic/frontend/A64/decoder/a64.h>
#include <dynarmic/frontend/imm.h>
#include "dynarmic/frontend/A64/a64_types.h"
#include "dynarmic/frontend/A64/decoder/a64.h"
#include "dynarmic/frontend/imm.h"
#pragma GCC diagnostic pop
+23 -45
View File
@@ -110,13 +110,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
struct System::Impl {
explicit Impl(System& system)
: kernel{system}
, fs_controller{system}
, hid_core{kernel}
, cpu_manager{system}
, reporter{system}
, profile_manager{}
{}
: kernel{system}, fs_controller{system}, hid_core{kernel}, cpu_manager{system},
reporter{system}, applet_manager{system}, frontend_applets{system}, profile_manager{} {}
u64 program_id;
@@ -129,12 +124,6 @@ struct System::Impl {
core_timing.SetMulticore(is_multicore);
core_timing.Initialize([&system]() { system.RegisterHostThread(); });
applet_manager.emplace(system);
frontend_applets.emplace(system);
apm_controller.emplace(core_timing);
arp_manager.emplace();
profile_manager.emplace();
// Create a default fs if one doesn't already exist.
if (virtual_filesystem == nullptr) {
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
@@ -144,7 +133,7 @@ struct System::Impl {
}
// Create default implementations of applets if one is not provided.
frontend_applets->SetDefaultAppletsIfMissing();
frontend_applets.SetDefaultAppletsIfMissing();
auto const is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue();
@@ -387,7 +376,7 @@ struct System::Impl {
// Register with applet manager
// All threads are started, begin main process execution, now that we're in the clear
applet_manager->CreateAndInsertByFrontendAppletParameters(std::move(process), params);
applet_manager.CreateAndInsertByFrontendAppletParameters(std::move(process), params);
if (Settings::values.gamecard_inserted) {
if (Settings::values.gamecard_current_game) {
@@ -439,27 +428,13 @@ struct System::Impl {
core_timing.SyncPause(false);
Network::CancelPendingSocketOperations();
kernel.SuspendEmulation(true);
kernel.ShutdownCores();
// Notify services helpers of shutdown
audio_core->Shutdown();
// Wait for threads/services to join
kernel.CloseServices();
// service shutdown
kernel.ShutdownCores();
services.reset();
service_manager.reset();
frontend_applets.reset();
applet_manager.reset();
apm_controller.reset();
arp_manager.reset();
profile_manager.reset();
fs_controller.Reset();
cheat_engine.reset();
core_timing.ClearPendingEvents();
core_timing.Reset();
app_loader.reset();
audio_core.reset();
gpu_core.reset();
@@ -476,6 +451,9 @@ struct System::Impl {
room_member->SendGameInfo(game_info);
}
// Reset all glue registrations
arp_manager.ResetAll();
LOG_DEBUG(Core, "Shutdown OK");
}
@@ -504,13 +482,13 @@ struct System::Impl {
CpuManager cpu_manager;
Reporter reporter;
/// Applets
std::optional<Service::AM::AppletManager> applet_manager;
std::optional<Service::AM::Frontend::FrontendAppletHolder> frontend_applets;
Service::AM::AppletManager applet_manager;
Service::AM::Frontend::FrontendAppletHolder frontend_applets;
/// APM (Performance) services
std::optional<Service::APM::Controller> apm_controller;
Service::APM::Controller apm_controller{core_timing};
/// Service State
std::optional<Service::Glue::ARPManager> arp_manager;
std::optional<Service::Account::ProfileManager> profile_manager;
Service::Glue::ARPManager arp_manager;
Service::Account::ProfileManager profile_manager;
/// Network instance
Network::NetworkInstance network_instance;
Core::SpeedLimiter speed_limiter;
@@ -837,19 +815,19 @@ void System::RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
}
void System::SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set) {
impl->frontend_applets->SetFrontendAppletSet(std::move(set));
impl->frontend_applets.SetFrontendAppletSet(std::move(set));
}
Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() {
return *impl->frontend_applets;
return impl->frontend_applets;
}
const Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() const {
return *impl->frontend_applets;
return impl->frontend_applets;
}
Service::AM::AppletManager& System::GetAppletManager() {
return *impl->applet_manager;
return impl->applet_manager;
}
void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) {
@@ -890,27 +868,27 @@ const Reporter& System::GetReporter() const {
}
Service::Glue::ARPManager& System::GetARPManager() {
return *impl->arp_manager;
return impl->arp_manager;
}
const Service::Glue::ARPManager& System::GetARPManager() const {
return *impl->arp_manager;
return impl->arp_manager;
}
Service::APM::Controller& System::GetAPMController() {
return *impl->apm_controller;
return impl->apm_controller;
}
const Service::APM::Controller& System::GetAPMController() const {
return *impl->apm_controller;
return impl->apm_controller;
}
Service::Account::ProfileManager& System::GetProfileManager() {
return *impl->profile_manager;
return impl->profile_manager;
}
const Service::Account::ProfileManager& System::GetProfileManager() const {
return *impl->profile_manager;
return impl->profile_manager;
}
void System::SetExitLocked(bool locked) {
+2 -2
View File
@@ -304,10 +304,10 @@ std::optional<s64> CoreTiming::Advance() {
void CoreTiming::Reset() {
paused = true;
pause_event.Set();
event.Set();
if (timer_thread.joinable()) {
timer_thread.request_stop();
pause_event.Set();
event.Set();
timer_thread.join();
}
has_started = false;
+4 -7
View File
@@ -35,20 +35,17 @@ static IPSFileType IdentifyMagic(std::span<const u8> magic) {
}
static bool IsEOF(IPSFileType type, std::span<const u8> magic) {
return (type == IPSFileType::IPS && magic.size() >= 3 && std::memcmp(magic.data(), "EOF", 3) == 0)
|| (type == IPSFileType::IPS32 && magic.size() >= 4 && std::memcmp(magic.data(), "EEOF", 4) == 0);
return (type == IPSFileType::IPS && magic.size() > 3 && std::memcmp(magic.data(), "EOF", 3) == 0)
|| (type == IPSFileType::IPS32 && magic.size() > 4 && std::memcmp(magic.data(), "EEOF", 4) == 0);
}
VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
if (in == nullptr || ips == nullptr)
return nullptr;
const auto type = IdentifyMagic(ips->ReadBytes(0x5));
if (type == IPSFileType::Error)
return nullptr;
auto in_data = in->ReadAllBytes();
if (in_data.size() == 0)
auto const type = IdentifyMagic(in_data);
if (type == IPSFileType::Error)
return nullptr;
std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
+14 -8
View File
@@ -9,6 +9,7 @@
#include "common/logging.h"
#include "common/uuid.h"
#include "core/core.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/vfs/vfs.h"
@@ -61,17 +62,24 @@ SaveDataFactory::SaveDataFactory(Core::System& system_, ProgramId program_id_,
SaveDataFactory::~SaveDataFactory() = default;
std::string SaveDataFactory::GetSaveDataPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id, u64 save_id) const {
if (type == SaveDataType::Account || type == SaveDataType::Device) {
const auto requested_id = title_id != 0 ? title_id : program_id;
const auto parent_id = system.GetContentProvider().GetParentApplicationId(requested_id);
title_id = parent_id.value_or(requested_id);
}
return GetFullPath(program_id, dir, space, type, title_id, user_id, save_id);
}
VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const {
const auto save_directory = GetFullPath(program_id, dir, space, meta.type, meta.program_id,
meta.user_id, meta.system_save_data_id);
const auto save_directory = GetSaveDataPath(space, meta.type, meta.program_id, meta.user_id, meta.system_save_data_id);
return dir->CreateDirectoryRelative(save_directory);
}
VirtualDir SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const {
const auto save_directory = GetFullPath(program_id, dir, space, meta.type, meta.program_id,
meta.user_id, meta.system_save_data_id);
const auto save_directory = GetSaveDataPath(space, meta.type, meta.program_id, meta.user_id, meta.system_save_data_id);
auto out = dir->GetDirectoryRelative(save_directory);
@@ -154,8 +162,7 @@ std::string SaveDataFactory::GetUserGameSaveDataRoot(u128 user_id, bool future)
SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
u128 user_id) const {
const auto path =
GetFullPath(program_id, dir, SaveDataSpaceId::User, type, title_id, user_id, 0);
const auto path = GetSaveDataPath(SaveDataSpaceId::User, type, title_id, user_id, 0);
const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
const auto size_file = relative_dir->GetFile(GetSaveDataSizeFileName());
@@ -173,8 +180,7 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
SaveDataSize new_value) const {
const auto path =
GetFullPath(program_id, dir, SaveDataSpaceId::User, type, title_id, user_id, 0);
const auto path = GetSaveDataPath(SaveDataSpaceId::User, type, title_id, user_id, 0);
const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
const auto size_file = relative_dir->CreateFile(GetSaveDataSizeFileName());
+1
View File
@@ -50,6 +50,7 @@ public:
void SetAutoCreate(bool state);
private:
std::string GetSaveDataPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id, u64 save_id) const;
Core::System& system;
ProgramId program_id;
VirtualDir dir;
@@ -72,9 +72,11 @@ void GlobalSchedulerContext::UnregisterDummyThreadForWakeup(KThread* thread) noe
void GlobalSchedulerContext::WakeupWaitingDummyThreads(KernelCore& kernel) noexcept {
ASSERT(this->IsLocked());
for (auto* thread : m_woken_dummy_threads)
thread->DummyThreadEndWait(kernel);
m_woken_dummy_threads.clear();
if (m_woken_dummy_threads.size() > 0) {
for (auto* thread : m_woken_dummy_threads)
thread->DummyThreadEndWait(kernel);
m_woken_dummy_threads.clear();
}
}
} // namespace Kernel
+1
View File
@@ -59,6 +59,7 @@ Result TerminateChildren(KernelCore& kernel, KProcess* process, const KThread* t
KThread* cur_child = nullptr;
{
KScopedLightLock proc_lk(process->GetListLock());
auto& thread_list = process->GetThreadList();
for (auto it = thread_list.begin(); it != thread_list.end(); ++it) {
if (KThread* thread = std::addressof(*it); thread != thread_to_not_terminate) {
+10 -4
View File
@@ -1236,13 +1236,18 @@ namespace Kernel {
KScopedSchedulerLock sl{kernel};
// Determine if this is the first termination request.
// Perform an atomic compare-and-swap from false to true.
bool expected = false;
const bool first_request = [&]() -> bool {
// Perform an atomic compare-and-swap from false to true.
bool expected = false;
return m_termination_requested.compare_exchange_strong(expected, true);
}();
// If this is the first request, start termination procedure.
if (m_termination_requested.compare_exchange_strong(expected, true)) {
if (first_request) {
// If the thread is in initialized state, just change state to terminated.
if (this->GetState() == ThreadState::Initialized) {
return m_thread_state = ThreadState::Terminated;
m_thread_state = ThreadState::Terminated;
return ThreadState::Terminated;
}
// Register the terminating dpc.
@@ -1276,6 +1281,7 @@ namespace Kernel {
m_wait_queue->CancelWait(kernel, this, ResultTerminationRequested, true);
}
}
return this->GetState();
}
+5 -34
View File
@@ -4,9 +4,7 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <thread>
#include "common/assert.h"
#include "common/thread.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_worker_task.h"
@@ -28,33 +26,7 @@ void KWorkerTask::DoWorkerTask(KernelCore& kernel) {
}
}
KWorkerTaskManager::KWorkerTaskManager(KernelCore& kernel) {
m_waiting_thread = std::jthread([&kernel, this](std::stop_token stop_token) {
Common::SetCurrentThreadName("KWorkerManager");
while (!stop_token.stop_requested()) {
KWorkerTask* t;
{
std::unique_lock lk{m_task_mutex};
m_task_cv.wait(lk, m_waiting_thread.get_stop_token(), [&]() {
return !m_task_queue.empty();
});
if (stop_token.stop_requested())
break;
t = m_task_queue.back();
m_task_queue.pop_back();
}
t->DoWorkerTask(kernel);
}
});
}
KWorkerTaskManager::~KWorkerTaskManager() {
if (m_waiting_thread.joinable()) {
m_waiting_thread.request_stop();
m_task_cv.notify_one();
m_waiting_thread.join();
}
}
KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "KWorkerTaskManager") {}
void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) {
ASSERT(type <= WorkerType::Count);
@@ -63,11 +35,10 @@ void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTas
void KWorkerTaskManager::AddTask(KernelCore& kernel, KWorkerTask* task) {
KScopedSchedulerLock sl(kernel);
{
std::scoped_lock lk{m_task_mutex};
m_task_queue.emplace_back(task);
}
m_task_cv.notify_one();
m_waiting_thread.QueueWork([&kernel, task]() {
// Do the task.
task->DoWorkerTask(kernel);
});
}
} // namespace Kernel
+4 -11
View File
@@ -1,13 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <condition_variable>
#include <thread>
#include "common/common_types.h"
#include "common/thread_worker.h"
@@ -23,17 +18,15 @@ public:
Count,
};
KWorkerTaskManager(KernelCore& kernel);
~KWorkerTaskManager();
KWorkerTaskManager();
static void AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task);
private:
void AddTask(KernelCore& kernel, KWorkerTask* task);
std::jthread m_waiting_thread;
std::mutex m_task_mutex;
std::condition_variable_any m_task_cv;
std::vector<KWorkerTask*> m_task_queue;
private:
Common::ThreadWorker m_waiting_thread;
};
} // namespace Kernel
+12 -17
View File
@@ -80,10 +80,7 @@ struct KernelCore::Impl {
// so it will be statically given a TLS slot anyways.
static inline thread_local ThreadLocalData tls_data = {};
explicit Impl(Core::System& system_, KernelCore& kernel_)
: worker_task_manager{kernel_}
, system{system_}
{
explicit Impl(Core::System& system_, KernelCore& kernel_) : system{system_} {
tls_data.lock = true;
}
@@ -970,16 +967,12 @@ Kernel::KHardwareTimer& KernelCore::HardwareTimer() {
return *impl->hardware_timer;
}
KAutoObjectWithListContainer* KernelCore::ObjectListContainer() {
if (!impl->global_object_list_container)
return nullptr;
return std::addressof(*impl->global_object_list_container);
KAutoObjectWithListContainer& KernelCore::ObjectListContainer() {
return *impl->global_object_list_container;
}
const KAutoObjectWithListContainer* KernelCore::ObjectListContainer() const {
if (!impl->global_object_list_container)
return nullptr;
return std::addressof(*impl->global_object_list_container);
const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const {
return *impl->global_object_list_container;
}
void KernelCore::PrepareReschedule(std::size_t id) {
@@ -1008,10 +1001,16 @@ void KernelCore::UnregisterInUseObject(KAutoObject* object) {
void KernelCore::RunServer(std::unique_ptr<Service::ServerManager>&& server_manager) {
auto* manager = server_manager.get();
if (!impl->is_shutting_down) {
{
std::scoped_lock lk{impl->server_lock};
if (impl->is_shutting_down) {
return;
}
impl->server_managers.emplace_back(std::move(server_manager));
}
manager->LoopProcess();
}
@@ -1256,10 +1255,6 @@ void KernelCore::SuspendEmulation(bool suspended) {
}
void KernelCore::ShutdownCores() {
// Notify shutdown (pre-emptively)
for (auto& sm : impl->server_managers) {
sm->NotifyShutdown();
}
impl->TerminateAllProcesses();
KScopedSchedulerLock lk{*this};
for (auto* thread : impl->shutdown_threads)
+3 -2
View File
@@ -175,8 +175,9 @@ public:
/// Stops execution of 'id' core, in order to reschedule a new thread.
void PrepareReschedule(std::size_t id);
KAutoObjectWithListContainer* ObjectListContainer();
const KAutoObjectWithListContainer* ObjectListContainer() const;
KAutoObjectWithListContainer& ObjectListContainer();
const KAutoObjectWithListContainer& ObjectListContainer() const;
/// Registers all kernel objects with the global emulation state, this is purely for tracking
/// leaks after emulation has been shutdown.
+3 -4
View File
@@ -151,8 +151,7 @@ public:
const bool is_initialized = this->IsInitialized();
uintptr_t arg = 0;
if (is_initialized) {
if (auto const olc = kernel.ObjectListContainer(); olc)
olc->Unregister(this);
kernel.ObjectListContainer().Unregister(this);
arg = this->GetPostDestroyArgument();
this->Finalize(kernel);
}
@@ -176,7 +175,7 @@ public:
public:
static void InitializeSlabHeap(KernelCore& kernel, void* memory, size_t memory_size) {
kernel.SlabHeap<Derived>().Initialize(memory, memory_size);
kernel.ObjectListContainer()->Initialize();
kernel.ObjectListContainer().Initialize();
}
static Derived* Create(KernelCore& kernel) {
@@ -187,7 +186,7 @@ public:
}
static void Register(KernelCore& kernel, Derived* obj) {
return kernel.ObjectListContainer()->Register(obj);
return kernel.ObjectListContainer().Register(obj);
}
static size_t GetObjectSize(KernelCore& kernel) {
+6 -2
View File
@@ -15,9 +15,13 @@ void LoopProcess(Core::System& system) {
WindowSystem window_system(system);
ButtonPoller button_poller(system, window_system);
EventObserver event_observer(system, window_system);
auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("appletAE", std::make_shared<IAllSystemAppletProxiesService>(system, window_system));
server_manager->RegisterNamedService("appletOE", std::make_shared<IApplicationProxyService>(system, window_system));
server_manager->RegisterNamedService(
"appletAE", std::make_shared<IAllSystemAppletProxiesService>(system, window_system));
server_manager->RegisterNamedService(
"appletOE", std::make_shared<IApplicationProxyService>(system, window_system));
ServerManager::RunServer(std::move(server_manager));
}
+8 -9
View File
@@ -26,25 +26,24 @@ EventObserver::EventObserver(Core::System& system, WindowSystem& window_system)
m_window_system.SetEventObserver(this);
m_wakeup_holder.SetUserData(static_cast<uintptr_t>(UserDataTag::WakeupEvent));
m_wakeup_holder.LinkToMultiWait(std::addressof(m_multi_wait));
system.Kernel().RunOnGuestCoreProcess("am:EventObserver", [this]() {
auto const stop_token = m_stop_source.get_token();
m_thread = std::jthread([this](std::stop_token stop_token) {
Common::SetCurrentThreadName("am:EventObserver");
while (!stop_token.stop_requested()) {
auto* signaled_holder = this->WaitSignaled(stop_token);
if (stop_token.stop_requested() || !signaled_holder)
if (!signaled_holder)
break;
this->Process(signaled_holder);
}
m_process_cv.notify_one();
});
}
EventObserver::~EventObserver() {
// Signal thread and wait for processing to finish.
m_stop_source.request_stop();
m_wakeup_event.Signal(m_system.Kernel());
{
std::unique_lock lk{m_process_mutex};
m_process_cv.wait(lk);
if (m_thread.joinable()) {
// Signal thread and wait for processing to finish.
m_thread.request_stop();
m_wakeup_event.Signal(m_system.Kernel());
m_thread.join();
}
// Free remaining owned sessions.
+1 -5
View File
@@ -6,8 +6,6 @@
#pragma once
#include <condition_variable>
#include <stop_token>
#include "common/polyfill_thread.h"
#include "common/thread.h"
#include "core/hle/service/kernel_helpers.h"
@@ -64,9 +62,7 @@ private:
MultiWait m_deferred_wait_list;
// Processing thread.
std::stop_source m_stop_source{};
std::mutex m_process_mutex;
std::condition_variable m_process_cv{};
std::jthread m_thread{};
};
} // namespace Service::AM
+4 -11
View File
@@ -29,7 +29,6 @@
#include "core/hle/service/am/frontend/applet_web_browser.h"
#include "core/hle/service/am/frontend/applets.h"
#include "core/hle/service/am/service/storage.h"
#include "core/hle/service/am/window_system.h"
#include "core/hle/service/sm/sm.h"
namespace Service::AM::Frontend {
@@ -73,16 +72,10 @@ void FrontendApplet::PushInteractiveOutData(std::shared_ptr<IStorage> storage) {
void FrontendApplet::Exit() {
auto applet_ = applet.lock();
{
std::scoped_lock lk{applet_->lock};
applet_->is_completed = true;
applet_->state_changed_event.Signal(system.Kernel());
}
if (auto caller_applet = applet_->caller_applet.lock()) {
std::scoped_lock lk{caller_applet->lock};
std::erase(caller_applet->child_applets, applet_);
}
if (auto* window_system = system.GetAppletManager().GetWindowSystem()) window_system->RequestUpdate();
std::scoped_lock lk{applet_->lock};
applet_->is_completed = true;
applet_->state_changed_event.Signal(system.Kernel());
}
FrontendAppletSet::FrontendAppletSet() = default;
@@ -122,10 +122,7 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
auto broker = std::make_shared<AppletDataBroker>(system);
applet->caller_applet = caller_applet;
applet->caller_applet_broker = broker;
{
std::scoped_lock lk{caller_applet->lock};
caller_applet->child_applets.push_back(applet);
}
caller_applet->child_applets.push_back(applet);
window_system.TrackApplet(applet, false);
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
}
@@ -151,10 +148,10 @@ std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& syste
applet->caller_applet = caller_applet;
applet->caller_applet_broker = storage;
applet->frontend = system.GetFrontendAppletHolder().GetApplet(applet, applet_id, mode);
{
std::scoped_lock lk{caller_applet->lock};
caller_applet->child_applets.push_back(applet);
}
caller_applet->child_applets.push_back(applet);
window_system.TrackApplet(applet, false);
return std::make_shared<ILibraryAppletAccessor>(system, storage, applet);
}
+1 -1
View File
@@ -191,7 +191,7 @@ void LoopProcess(Core::System& system) {
server_manager->RegisterNamedService("audout:d", std::make_shared<IAudioOutManagerForDebugger>(system), 30);
server_manager->RegisterNamedService("audin:d", std::make_shared<IAudioInManagerForDebugger>(system), 30);
server_manager->RegisterNamedService("audrec:d", std::make_shared<IFinalOutputRecorderManagerForDebugger>(system), 30);
server_manager->RegisterNamedService("audren:d", std::make_shared<IAudioRendererManagerForDebugger>(system), 30);
server_manager->RegisterNamedService("audren:d", std::make_shared<IAudioInManager>(system), 30);
server_manager->RegisterNamedService("audin:u", std::make_shared<IAudioInManager>(system), 30);
server_manager->RegisterNamedService("audin:a", std::make_shared<IAudioInManagerForApplet>(system), 30);
+5 -4
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -90,9 +90,10 @@ static Service::PSC::Time::LocationName GetTimeZoneString(
}
TimeManager::TimeManager(Core::System& system)
: m_steady_clock_resource{system}, m_time_zone_binary{system}
, m_worker{system, m_steady_clock_resource, m_file_timestamp_worker}
{
: m_steady_clock_resource{system}, m_time_zone_binary{system}, m_worker{
system,
m_steady_clock_resource,
m_file_timestamp_worker} {
m_time_m =
system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true);
+168 -168
View File
@@ -20,17 +20,15 @@
namespace Service::Glue::Time {
TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource, FileTimestampWorker& file_timestamp_worker)
: m_system{system}
, m_ctx{m_system, "Glue:TimeWorker"}
, m_event{m_ctx.CreateEvent("Glue:TimeWorker:Event")}
, m_steady_clock_resource{steady_clock_resource}
, m_file_timestamp_worker{file_timestamp_worker}
, m_timer_steady_clock{m_ctx.CreateEvent("Glue:TimeWorker:SteadyClockTimerEvent")}
, m_timer_file_system{m_ctx.CreateEvent("Glue:TimeWorker:FileTimeTimerEvent")}
, m_alarm_worker{m_system, m_steady_clock_resource}
, m_pm_state_change_handler{m_alarm_worker}
{
TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource,
FileTimestampWorker& file_timestamp_worker)
: m_system{system}, m_ctx{m_system, "Glue:TimeWorker"}, m_event{m_ctx.CreateEvent(
"Glue:TimeWorker:Event")},
m_steady_clock_resource{steady_clock_resource},
m_file_timestamp_worker{file_timestamp_worker}, m_timer_steady_clock{m_ctx.CreateEvent(
"Glue:TimeWorker:SteadyClockTimerEvent")},
m_timer_file_system{m_ctx.CreateEvent("Glue:TimeWorker:FileTimeTimerEvent")},
m_alarm_worker{m_system, m_steady_clock_resource}, m_pm_state_change_handler{m_alarm_worker} {
m_timer_steady_clock_timing_event = Core::Timing::CreateEvent(
"Time::SteadyClockEvent",
[this](s64 time,
@@ -49,16 +47,14 @@ TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady
}
TimeWorker::~TimeWorker() {
// Wait for processing to stop
m_stop_source.request_stop();
m_local_clock_event->Signal(m_system.Kernel());
m_network_clock_event->Signal(m_system.Kernel());
m_ephemeral_clock_event->Signal(m_system.Kernel());
std::this_thread::sleep_for(std::chrono::milliseconds(16));
m_thread.request_stop();
m_event->Signal(m_system.Kernel());
{
std::unique_lock lk{m_process_mutex};
m_process_cv.wait(lk);
}
m_thread.join();
m_ctx.CloseEvent(m_event);
m_system.CoreTiming().UnscheduleEvent(m_timer_steady_clock_timing_event);
@@ -126,162 +122,166 @@ void TimeWorker::Initialize(std::shared_ptr<Service::PSC::Time::StaticService> t
}
void TimeWorker::StartThread() {
m_system.Kernel().RunOnGuestCoreProcess("TimeWorker", [this]() {
auto const stop_token = m_stop_source.get_token();
while (!stop_token.stop_requested()) {
enum class EventType : s32 {
Exit = 0,
PowerStateChange = 1,
SignalAlarms = 2,
UpdateLocalSystemClock = 3,
UpdateNetworkSystemClock = 4,
UpdateEphemeralSystemClock = 5,
UpdateSteadyClock = 6,
UpdateFileTimestamp = 7,
AutoCorrect = 8,
};
s32 index{};
if (m_pm_state_change_handler.m_priority != 0) {
// TODO: gIPmModuleService::GetEvent() 1
index = WaitAny(
m_system.Kernel(),
&m_event->GetReadableEvent(), // 0
&m_alarm_worker.GetEvent() // 1
);
} else {
// TODO: gIPmModuleService::GetEvent() 1
index = WaitAny(
m_system.Kernel(),
&m_event->GetReadableEvent(), // 0
&m_alarm_worker.GetEvent(), // 1
&m_alarm_worker.GetTimerEvent().GetReadableEvent(), // 2
m_local_clock_event, // 3
m_network_clock_event, // 4
m_ephemeral_clock_event, // 5
&m_timer_steady_clock->GetReadableEvent(), // 6
&m_timer_file_system->GetReadableEvent(), // 7
m_standard_user_auto_correct_clock_event // 8
);
}
m_thread = std::jthread(std::bind_front(&TimeWorker::ThreadFunc, this));
}
if (stop_token.stop_requested())
break;
void TimeWorker::ThreadFunc(std::stop_token stop_token) {
Common::SetCurrentThreadName("TimeWorker");
Common::SetCurrentThreadPriority(Common::ThreadPriority::Low);
switch (EventType(index)) {
case EventType::Exit:
return;
while (!stop_token.stop_requested()) {
enum class EventType : s32 {
Exit = 0,
PowerStateChange = 1,
SignalAlarms = 2,
UpdateLocalSystemClock = 3,
UpdateNetworkSystemClock = 4,
UpdateEphemeralSystemClock = 5,
UpdateSteadyClock = 6,
UpdateFileTimestamp = 7,
AutoCorrect = 8,
};
case EventType::PowerStateChange:
m_alarm_worker.GetEvent().Clear(m_system.Kernel());
if (m_pm_state_change_handler.m_priority <= 1) {
m_alarm_worker.OnPowerStateChanged();
}
break;
s32 index{};
case EventType::SignalAlarms:
m_alarm_worker.GetTimerEvent().Clear(m_system.Kernel());
m_time_m->CheckAndSignalAlarms();
break;
case EventType::UpdateLocalSystemClock: {
m_local_clock_event->Clear(m_system.Kernel());
Service::PSC::Time::SystemClockContext context{};
R_ASSERT(m_local_clock->GetSystemClockContext(&context));
m_set_sys->SetUserSystemClockContext(context);
m_file_timestamp_worker.SetFilesystemPosixTime();
break;
}
case EventType::UpdateNetworkSystemClock: {
m_network_clock_event->Clear(m_system.Kernel());
Service::PSC::Time::SystemClockContext context{};
R_ASSERT(m_network_clock->GetSystemClockContext(&context));
m_set_sys->SetNetworkSystemClockContext(context);
s64 time{};
if (m_network_clock->GetCurrentTime(&time) != ResultSuccess) {
break;
}
[[maybe_unused]] auto offset_before{
m_ig_report_network_clock_context_set ? m_report_network_clock_context.offset : 0};
// TODO system report "standard_netclock_operation"
// "clock_time" = time
// "context_offset_before" = offset_before
// "context_offset_after" = context.offset
m_report_network_clock_context = context;
if (!m_ig_report_network_clock_context_set) {
m_ig_report_network_clock_context_set = true;
}
m_file_timestamp_worker.SetFilesystemPosixTime();
break;
}
case EventType::UpdateEphemeralSystemClock: {
m_ephemeral_clock_event->Clear(m_system.Kernel());
Service::PSC::Time::SystemClockContext context{};
auto res = m_ephemeral_clock->GetSystemClockContext(&context);
if (res != ResultSuccess) {
break;
}
s64 time{};
res = m_ephemeral_clock->GetCurrentTime(&time);
if (res != ResultSuccess) {
break;
}
[[maybe_unused]] auto offset_before{m_ig_report_ephemeral_clock_context_set
? m_report_ephemeral_clock_context.offset
: 0};
// TODO system report "ephemeral_netclock_operation"
// "clock_time" = time
// "context_offset_before" = offset_before
// "context_offset_after" = context.offset
m_report_ephemeral_clock_context = context;
if (!m_ig_report_ephemeral_clock_context_set) {
m_ig_report_ephemeral_clock_context_set = true;
}
break;
}
case EventType::UpdateSteadyClock:
m_timer_steady_clock->Clear(m_system.Kernel());
m_steady_clock_resource.UpdateTime();
m_time_m->SetStandardSteadyClockBaseTime(m_steady_clock_resource.GetTime());
break;
case EventType::UpdateFileTimestamp:
m_timer_file_system->Clear(m_system.Kernel());
m_file_timestamp_worker.SetFilesystemPosixTime();
break;
case EventType::AutoCorrect: {
m_standard_user_auto_correct_clock_event->Clear(m_system.Kernel());
bool automatic_correction{};
R_ASSERT(m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(&automatic_correction));
Service::PSC::Time::SteadyClockTimePoint time_point{};
R_ASSERT(m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point));
m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
break;
}
default:
UNREACHABLE();
}
if (m_pm_state_change_handler.m_priority != 0) {
// TODO: gIPmModuleService::GetEvent() 1
index = WaitAny(m_system.Kernel(),
&m_event->GetReadableEvent(), // 0
&m_alarm_worker.GetEvent() // 1
);
} else {
// TODO: gIPmModuleService::GetEvent() 1
index = WaitAny(m_system.Kernel(),
&m_event->GetReadableEvent(), // 0
&m_alarm_worker.GetEvent(), // 1
&m_alarm_worker.GetTimerEvent().GetReadableEvent(), // 2
m_local_clock_event, // 3
m_network_clock_event, // 4
m_ephemeral_clock_event, // 5
&m_timer_steady_clock->GetReadableEvent(), // 6
&m_timer_file_system->GetReadableEvent(), // 7
m_standard_user_auto_correct_clock_event // 8
);
}
m_process_cv.notify_one();
});
switch (static_cast<EventType>(index)) {
case EventType::Exit:
return;
case EventType::PowerStateChange:
m_alarm_worker.GetEvent().Clear(m_system.Kernel());
if (m_pm_state_change_handler.m_priority <= 1) {
m_alarm_worker.OnPowerStateChanged();
}
break;
case EventType::SignalAlarms:
m_alarm_worker.GetTimerEvent().Clear(m_system.Kernel());
m_time_m->CheckAndSignalAlarms();
break;
case EventType::UpdateLocalSystemClock: {
m_local_clock_event->Clear(m_system.Kernel());
Service::PSC::Time::SystemClockContext context{};
R_ASSERT(m_local_clock->GetSystemClockContext(&context));
m_set_sys->SetUserSystemClockContext(context);
m_file_timestamp_worker.SetFilesystemPosixTime();
break;
}
case EventType::UpdateNetworkSystemClock: {
m_network_clock_event->Clear(m_system.Kernel());
Service::PSC::Time::SystemClockContext context{};
R_ASSERT(m_network_clock->GetSystemClockContext(&context));
m_set_sys->SetNetworkSystemClockContext(context);
s64 time{};
if (m_network_clock->GetCurrentTime(&time) != ResultSuccess) {
break;
}
[[maybe_unused]] auto offset_before{
m_ig_report_network_clock_context_set ? m_report_network_clock_context.offset : 0};
// TODO system report "standard_netclock_operation"
// "clock_time" = time
// "context_offset_before" = offset_before
// "context_offset_after" = context.offset
m_report_network_clock_context = context;
if (!m_ig_report_network_clock_context_set) {
m_ig_report_network_clock_context_set = true;
}
m_file_timestamp_worker.SetFilesystemPosixTime();
break;
}
case EventType::UpdateEphemeralSystemClock: {
m_ephemeral_clock_event->Clear(m_system.Kernel());
Service::PSC::Time::SystemClockContext context{};
auto res = m_ephemeral_clock->GetSystemClockContext(&context);
if (res != ResultSuccess) {
break;
}
s64 time{};
res = m_ephemeral_clock->GetCurrentTime(&time);
if (res != ResultSuccess) {
break;
}
[[maybe_unused]] auto offset_before{m_ig_report_ephemeral_clock_context_set
? m_report_ephemeral_clock_context.offset
: 0};
// TODO system report "ephemeral_netclock_operation"
// "clock_time" = time
// "context_offset_before" = offset_before
// "context_offset_after" = context.offset
m_report_ephemeral_clock_context = context;
if (!m_ig_report_ephemeral_clock_context_set) {
m_ig_report_ephemeral_clock_context_set = true;
}
break;
}
case EventType::UpdateSteadyClock:
m_timer_steady_clock->Clear(m_system.Kernel());
m_steady_clock_resource.UpdateTime();
m_time_m->SetStandardSteadyClockBaseTime(m_steady_clock_resource.GetTime());
break;
case EventType::UpdateFileTimestamp:
m_timer_file_system->Clear(m_system.Kernel());
m_file_timestamp_worker.SetFilesystemPosixTime();
break;
case EventType::AutoCorrect: {
m_standard_user_auto_correct_clock_event->Clear(m_system.Kernel());
bool automatic_correction{};
R_ASSERT(m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(
&automatic_correction));
Service::PSC::Time::SteadyClockTimePoint time_point{};
R_ASSERT(
m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point));
m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
break;
}
default:
UNREACHABLE();
}
}
}
} // namespace Service::Glue::Time
+4 -8
View File
@@ -1,12 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <condition_variable>
#include "common/common_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/glue/time/alarm_worker.h"
@@ -37,17 +33,17 @@ public:
void StartThread();
private:
template <typename T>
T GetSettingsItemValue(const std::string& category, const std::string& name);
void ThreadFunc(std::stop_token stop_token);
Core::System& m_system;
KernelHelpers::ServiceContext m_ctx;
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
std::mutex m_process_mutex;
std::condition_variable m_process_cv;
std::stop_source m_stop_source;
std::jthread m_thread;
Kernel::KEvent* m_event{};
std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;
std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm;
+37 -31
View File
@@ -8,9 +8,9 @@
#include <map>
#include <span>
#include <boost/icl/interval_set.hpp>
#include <dynarmic/interface/A64/a64.h>
#include <dynarmic/interface/A64/config.h>
#include <dynarmic/interface/code_page.h>
#include "dynarmic/interface/A64/a64.h"
#include "dynarmic/interface/A64/config.h"
#include "dynarmic/interface/code_page.h"
#include "common/alignment.h"
#include "common/common_funcs.h"
@@ -71,14 +71,17 @@ public:
void InstructionSynchronizationBarrierRaised() override {
last_code_addr = u64(-1); //reset back, force refetch
}
u64 MemoryRead(u64 vaddr, size_t size) override {
switch (size) {
case sizeof(u64): return ReadMemory<u64>(vaddr);
case sizeof(u32): return ReadMemory<u32>(vaddr);
case sizeof(u16): return ReadMemory<u16>(vaddr);
case sizeof(u8): return ReadMemory<u8>(vaddr);
default: UNREACHABLE();
}
u8 MemoryRead8(u64 vaddr) override {
return ReadMemory<u8>(vaddr);
}
u16 MemoryRead16(u64 vaddr) override {
return ReadMemory<u16>(vaddr);
}
u32 MemoryRead32(u64 vaddr) override {
return ReadMemory<u32>(vaddr);
}
u64 MemoryRead64(u64 vaddr) override {
return ReadMemory<u64>(vaddr);
}
u128 MemoryRead128(u64 vaddr) override {
return ReadMemory<u128>(vaddr);
@@ -86,19 +89,22 @@ public:
std::string MemoryReadCString(u64 vaddr) {
std::string result{};
u8 next;
while ((next = u8(MemoryRead(vaddr++, sizeof(u8)))) != 0)
while ((next = MemoryRead8(vaddr++)) != 0)
result += char(next);
return result;
}
void MemoryWrite(u64 vaddr, u64 value, size_t size) override {
switch (size) {
case sizeof(u64): WriteMemory<u64>(vaddr, u64(value)); break;
case sizeof(u32): WriteMemory<u32>(vaddr, u32(value)); break;
case sizeof(u16): WriteMemory<u16>(vaddr, u16(value)); break;
case sizeof(u8): WriteMemory<u8>(vaddr, u8(value)); break;
default: UNREACHABLE();
}
void MemoryWrite8(u64 vaddr, u8 value) override {
WriteMemory<u8>(vaddr, value);
}
void MemoryWrite16(u64 vaddr, u16 value) override {
WriteMemory<u16>(vaddr, value);
}
void MemoryWrite32(u64 vaddr, u32 value) override {
WriteMemory<u32>(vaddr, value);
}
void MemoryWrite64(u64 vaddr, u64 value) override {
WriteMemory<u64>(vaddr, value);
}
void MemoryWrite128(u64 vaddr, u128 value) override {
WriteMemory<u128>(vaddr, value);
@@ -187,14 +193,14 @@ public:
// The loaded NRO file has ELF relocations that must be processed before it can run.
// Normally this would be processed by RTLD, but in HLE context, we don't have
// the linker available, so we have to do it ourselves.
const VAddr mod_offset{callbacks->MemoryRead(4, sizeof(u32))};
if (callbacks->MemoryRead(mod_offset, sizeof(u32)) != Common::MakeMagic('M', 'O', 'D', '0'))
const VAddr mod_offset{callbacks->MemoryRead32(4)};
if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0'))
return false;
// For more info about dynamic entries, see the ELF ABI specification:
// https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html
// https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html
VAddr dynamic_offset{mod_offset + callbacks->MemoryRead(mod_offset + 4, sizeof(u32))};
VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)};
VAddr rela_dyn = 0, relr_dyn = 0;
size_t num_rela = 0, num_relr = 0;
while (true) {
@@ -216,8 +222,8 @@ public:
for (size_t i = 0; i < num_rela; i++) {
const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
if (Elf64RelType(rela.r_info) == ElfAArch64Relative) {
const VAddr contents{callbacks->MemoryRead(rela.r_offset, sizeof(u64))};
callbacks->MemoryWrite(rela.r_offset, contents + rela.r_addend, sizeof(u64));
const VAddr contents{callbacks->MemoryRead64(rela.r_offset)};
callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend);
}
}
@@ -225,7 +231,7 @@ public:
for (size_t i = 0; i < num_relr; i++) {
const auto relr = callbacks->ReadMemory<Elf64_Relr>(relr_dyn + i * sizeof(Elf64_Relr));
const auto incr = [&](VAddr where) {
callbacks->MemoryWrite(where, callbacks->MemoryRead(where, sizeof(u64)) + relocbase, sizeof(u64));
callbacks->MemoryWrite64(where, callbacks->MemoryRead64(where) + relocbase);
};
if ((relr & 1) == 0) {
// where pointer
@@ -288,7 +294,7 @@ public:
if (argument_stack.size() > 8) {
const VAddr new_sp = Common::AlignDown(top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN);
for (size_t i = 8; i < argument_stack.size(); i++)
callbacks->MemoryWrite(new_sp + (i - 8) * sizeof(u64), argument_stack[i], sizeof(u64));
callbacks->MemoryWrite64(new_sp + (i - 8) * sizeof(u64), argument_stack[i]);
jit->SetSP(new_sp);
}
// Reset the call state for the next invocation
@@ -379,17 +385,17 @@ void DynarmicCallbacks64::CallSVC(u32 swi) {
if (dest < src) {
for (size_t i = 0; i < n; i++)
MemoryWrite(dest + i, u8(MemoryRead(src + i, sizeof(u8))), sizeof(u8));
MemoryWrite8(dest + i, MemoryRead8(src + i));
} else {
for (size_t i = n; i > 0; i--)
MemoryWrite(dest + i - 1, u8(MemoryRead(src + i - 1, sizeof(u8))), sizeof(u8));
MemoryWrite8(dest + i - 1, MemoryRead8(src + i - 1));
}
} else if (pc == parent.helpers[size_t(HelperFn::Memset)]) {
const VAddr dest{parent.jit->GetRegister(0)};
const u64 c{parent.jit->GetRegister(1)};
const size_t n{parent.jit->GetRegister(2)};
for (size_t i = 0; i < n; i++)
MemoryWrite(dest + i, u8(c), sizeof(u8));
MemoryWrite8(dest + i, u8(c));
} else if (pc == parent.helpers[size_t(HelperFn::Resolve)]) {
// X0 contains a char* for a symbol to resolve
const auto name{MemoryReadCString(parent.jit->GetRegister(0))};
@@ -416,7 +422,7 @@ void DynarmicCallbacks64::CallSVC(u32 swi) {
}
void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) {
auto const inst = MemoryRead(pc, sizeof(u32));
auto const inst = MemoryRead32(pc);
LOG_CRITICAL(Service_JIT, "{} PC @ {:08x}, data = {:08x}", exception, pc, inst);
parent.jit->HaltExecution();
}
+3 -12
View File
@@ -98,10 +98,8 @@ ServerManager::~ServerManager() {
// Signal stop.
m_stop_source.request_stop();
m_wakeup_event->Signal(m_system.Kernel());
if (m_deferral_event) m_deferral_event->Signal(m_system.Kernel()); //for sm: and ports
// Wait for processing to stop. Schedule
// Wait for processing to stop.
m_stopped.Wait();
m_threads.clear();
@@ -255,14 +253,6 @@ void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_thre
}
}
/// @brief Notifies that the system is shutting down (pre-emptively terminate threads)
void ServerManager::NotifyShutdown() {
m_stop_source.request_stop();
// Wake them up regardless
m_wakeup_event->Signal(m_system.Kernel());
if (m_deferral_event) m_deferral_event->Signal(m_system.Kernel()); //for sm: and ports
}
Result ServerManager::LoopProcess() {
SCOPE_EXIT {
m_stopped.Set();
@@ -295,8 +285,9 @@ MultiWaitHolder* ServerManager::WaitSignaled() {
this->LinkDeferred();
// If we're done, return before we start waiting.
if (m_stop_source.stop_requested())
if (m_stop_source.stop_requested()) {
return nullptr;
}
auto* selected = m_multi_wait.WaitAny(m_system.Kernel());
if (selected == std::addressof(*m_wakeup_holder)) {
-4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -52,7 +49,6 @@ public:
Result LoopProcess();
void StartAdditionalHostThreads(const char* name, size_t num_threads);
void NotifyShutdown();
static void RunServer(std::unique_ptr<ServerManager>&& server);
+1 -1
View File
@@ -117,6 +117,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
{"friends", &Friend::LoopProcess},
{"settings", &Set::LoopProcess},
{"psc", &PSC::LoopProcess},
{"glue", &Glue::LoopProcess},
{"grc", &GRC::LoopProcess},
{"hid", &HID::LoopProcess},
{"jit", &JIT::LoopProcess},
@@ -152,7 +153,6 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
{"usb", &USB::LoopProcess},
{"i2c", &I2C::LoopProcess},
{"gpio", &GPIO::LoopProcess},
{"glue", &Glue::LoopProcess},
})
kernel.RunOnGuestCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); });
}
+6 -4
View File
@@ -305,14 +305,16 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_)
SM::~SM() = default;
void LoopProcess(Core::System& system) {
auto& service_manager = system.ServiceManager();
auto server_manager = std::make_unique<ServerManager>(system);
Kernel::KEvent* deferral_event{};
server_manager->ManageDeferral(&deferral_event);
system.ServiceManager().SetDeferralEvent(deferral_event);
service_manager.SetDeferralEvent(deferral_event);
auto sm_service = std::make_shared<SM>(system.ServiceManager(), system);
server_manager->ManageNamedPort("sm:", [sm_service] {
return sm_service;
});
server_manager->ManageNamedPort("sm:", [sm_service] { return sm_service; });
ServerManager::RunServer(std::move(server_manager));
}
+33 -23
View File
@@ -11,7 +11,6 @@
#include "common/thread.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/vi/conductor.h"
#include "core/hle/service/vi/container.h"
#include "core/hle/service/vi/display_list.h"
@@ -30,29 +29,24 @@ Conductor::Conductor(Core::System& system, Container& container, DisplayList& di
});
if (system.IsMulticore()) {
m_event = Core::Timing::CreateEvent("ScreenComposition", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
m_signal.Set();
return std::chrono::nanoseconds(this->GetNextTicks());
});
m_event = Core::Timing::CreateEvent(
"ScreenComposition",
[this](s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
m_signal.Set();
return std::chrono::nanoseconds(this->GetNextTicks());
});
system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
m_thread = system.Kernel().RunOnHostCoreThread("VSyncThread", [this]() {
auto const stop_token = m_thread.get_stop_token();
Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh);
Common::SetCurrentThreadToPerformanceCores();
while (!stop_token.stop_requested()) {
m_signal.Wait();
if (stop_token.stop_requested() || m_system.IsShuttingDown()) {
break;
}
this->ProcessVsync();
}
});
m_thread = std::jthread([this](std::stop_token token) { this->VsyncThread(token); });
} else {
m_event = Core::Timing::CreateEvent("ScreenComposition", [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
this->ProcessVsync();
return std::chrono::nanoseconds(this->GetNextTicks());
});
m_event = Core::Timing::CreateEvent(
"ScreenComposition",
[this](s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
this->ProcessVsync();
return std::chrono::nanoseconds(this->GetNextTicks());
});
system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
}
@@ -60,10 +54,10 @@ Conductor::Conductor(Core::System& system, Container& container, DisplayList& di
Conductor::~Conductor() {
m_system.CoreTiming().UnscheduleEvent(m_event);
if (m_thread.joinable()) {
if (m_system.IsMulticore()) {
m_thread.request_stop();
m_signal.Set();
m_thread.join();
}
}
@@ -89,6 +83,22 @@ void Conductor::ProcessVsync() {
}
}
void Conductor::VsyncThread(std::stop_token token) {
Common::SetCurrentThreadName("VSyncThread");
Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh);
Common::SetCurrentThreadToPerformanceCores();
while (!token.stop_requested()) {
m_signal.Wait();
if (m_system.IsShuttingDown()) {
return;
}
this->ProcessVsync();
}
}
s64 Conductor::GetNextTicks() const {
const auto& settings = Settings::values;
auto speed_scale = 1.f;

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