Compare commits

..

2 commits

Author SHA1 Message Date
e11f4f3171 fix license
All checks were successful
eden-license / license-header (pull_request) Successful in 23s
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-10-08 02:23:58 +02:00
3bc144cb5e [dynarmic] get rid of mcl intrusive list
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-10-08 02:23:58 +02:00
233 changed files with 1891 additions and 3228 deletions

View file

@ -1,13 +0,0 @@
diff --git a/library/aesni.h b/library/aesni.h
index 754c984c79..59e27afd3e 100644
--- a/library/aesni.h
+++ b/library/aesni.h
@@ -35,7 +35,7 @@
/* GCC-like compilers: currently, we only support intrinsics if the requisite
* target flag is enabled when building the library (e.g. `gcc -mpclmul -msse2`
* or `clang -maes -mpclmul`). */
-#if (defined(__GNUC__) || defined(__clang__)) && defined(__AES__) && defined(__PCLMUL__)
+#if defined(__GNUC__) || defined(__clang__)
#define MBEDTLS_AESNI_HAVE_INTRINSICS
#endif
/* For 32-bit, we only support intrinsics */

View file

@ -1,22 +0,0 @@
diff --git a/library/aesni.c b/library/aesni.c
index 2857068..3e104ab 100644
--- a/library/aesni.c
+++ b/library/aesni.c
@@ -31,16 +31,14 @@
#include <immintrin.h>
#endif
-#if defined(MBEDTLS_ARCH_IS_X86)
#if defined(MBEDTLS_COMPILER_IS_GCC)
#pragma GCC push_options
#pragma GCC target ("pclmul,sse2,aes")
#define MBEDTLS_POP_TARGET_PRAGMA
-#elif defined(__clang__) && (__clang_major__ >= 5)
+#elif defined(__clang__)
#pragma clang attribute push (__attribute__((target("pclmul,sse2,aes"))), apply_to=function)
#define MBEDTLS_POP_TARGET_PRAGMA
#endif
-#endif
#if !defined(MBEDTLS_AES_USE_HARDWARE_ONLY)
/*

View file

@ -1,55 +0,0 @@
diff --git a/include/mcl/assert.hpp b/include/mcl/assert.hpp
index f77dbe7..9ec0b9c 100644
--- a/include/mcl/assert.hpp
+++ b/include/mcl/assert.hpp
@@ -23,8 +23,11 @@ template<typename... Ts>
} // namespace mcl::detail
+#ifndef UNREACHABLE
#define UNREACHABLE() ASSERT_FALSE("Unreachable code!")
+#endif
+#ifndef ASSERT
#define ASSERT(expr) \
[&] { \
if (std::is_constant_evaluated()) { \
@@ -37,7 +40,9 @@ template<typename... Ts>
} \
} \
}()
+#endif
+#ifndef ASSERT_MSG
#define ASSERT_MSG(expr, ...) \
[&] { \
if (std::is_constant_evaluated()) { \
@@ -50,13 +55,24 @@ template<typename... Ts>
} \
} \
}()
+#endif
+#ifndef ASSERT_FALSE
#define ASSERT_FALSE(...) ::mcl::detail::assert_terminate("false", __VA_ARGS__)
+#endif
#if defined(NDEBUG) || defined(MCL_IGNORE_ASSERTS)
-# define DEBUG_ASSERT(expr) ASSUME(expr)
-# define DEBUG_ASSERT_MSG(expr, ...) ASSUME(expr)
+# ifndef DEBUG_ASSERT
+# define DEBUG_ASSERT(expr) ASSUME(expr)
+# endif
+# ifndef DEBUG_ASSERT_MSG
+# define DEBUG_ASSERT_MSG(expr, ...) ASSUME(expr)
+# endif
#else
-# define DEBUG_ASSERT(expr) ASSERT(expr)
-# define DEBUG_ASSERT_MSG(expr, ...) ASSERT_MSG(expr, __VA_ARGS__)
+# ifndef DEBUG_ASSERT
+# define DEBUG_ASSERT(expr) ASSERT(expr)
+# endif
+# ifndef DEBUG_ASSERT_MSG
+# define DEBUG_ASSERT_MSG(expr, ...) ASSERT_MSG(expr, __VA_ARGS__)
+# endif
#endif

View file

@ -13,13 +13,12 @@ Copyright: yuzu Emulator Project
License: GPL-2.0-or-later License: GPL-2.0-or-later
Files: dist/qt_themes/default/icons/256x256/eden.png Files: dist/qt_themes/default/icons/256x256/eden.png
dist/qt_themes/default/icons/256x256/eden_named.png
dist/yuzu.bmp dist/yuzu.bmp
dist/eden.icns dist/yuzu.icns
dist/eden.ico dist/eden.ico
dist/dev.eden_emu.eden.svg dist/eden.svg
Copyright: 2025 Eden Emulator Project Copyright: yuzu Emulator Project
License: GPL-3.0-or-later License: GPL-2.0-or-later
Files: dist/qt_themes/qdarkstyle*/LICENSE.* Files: dist/qt_themes/qdarkstyle*/LICENSE.*
dist/qt_themes/qdarkstyle*/style.qrc dist/qt_themes/qdarkstyle*/style.qrc
@ -156,39 +155,3 @@ License: BSD-3-Clause
Files: src/android/app/debug.keystore Files: src/android/app/debug.keystore
Copyright: 2023 yuzu Emulator Project Copyright: 2023 yuzu Emulator Project
License: GPL-3.0-or-later License: GPL-3.0-or-later
Files: dist/qt_themes/colorful/icons/48x48/user-trash.png
dist/qt_themes/colorful/icons/48x48/upload.png
dist/qt_themes/colorful/icons/48x48/download.png
Copyright: 2014 Uri Herrera
1996-2025 KDE Software Foundation
License: LGPL-2.0-or-later
Files: dist/qt_themes/default/icons/48x48/user-trash.png
dist/qt_themes/default/icons/48x48/upload.png
dist/qt_themes/default/icons/48x48/download.png
dist/qt_themes/default_dark/icons/48x48/user-trash.png
dist/qt_themes/default_dark/icons/48x48/upload.png
dist/qt_themes/default_dark/icons/48x48/download.png
Copyright: 2025 Fonticons, Inc.
License: CC-BY-4.0
Comment: All of these icons have been modified by crueter <crueter@crueter.xyz>
Files: CMakeModules/CPM.cmake
Copyright: 2019-2023 Lars Melchior
License: MIT
Files: CMakeModules/CPMUtil.cmake
CMakeModules/CPM.cmake
CMakeModules/GetSCMRev.cmake
CMakeModules/DetectArchitecture.cmake
tools/cpm/*
tools/update-cpm.sh
tools/shellcheck.sh
docs/CPMUtil.md
**cpmfile.json
Copyright: 2025 crueter <crueter@crueter.xyz>
License: GPL-3.0-or-later
Comment: CPM.cmake has had additional modifications from crueter to better work with CPMUtil
https://git.crueter.xyz/CMake/CPMUtil
https://git.crueter.xyz/CMake/Modules

View file

@ -52,10 +52,6 @@ if (PLATFORM_SUN)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
endif() endif()
if (CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2")
endif()
endif() endif()
# Needed for FFmpeg w/ VAAPI and DRM # Needed for FFmpeg w/ VAAPI and DRM
@ -577,12 +573,11 @@ if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
find_package(xbyak) find_package(xbyak)
endif() endif()
if (ENABLE_WEB_SERVICE OR ENABLE_QT_UPDATE_CHECKER) if (ENABLE_WEB_SERVICE)
# Workaround: httplib will kill itself if you attempt to do a find_package propagation find_package(httplib)
# find_package(httplib CONFIG)
endif() endif()
if (ENABLE_WEB_SERVICE) if (ENABLE_WEB_SERVICE OR ENABLE_QT_UPDATE_CHECKER)
find_package(cpp-jwt) find_package(cpp-jwt)
endif() endif()

View file

@ -1,33 +1,27 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2022 Alexandre Bouvier <contact@amb.tf> # SPDX-FileCopyrightText: 2022 Alexandre Bouvier <contact@amb.tf>
# #
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
find_package(DiscordRPC CONFIG QUIET) find_path(DiscordRPC_INCLUDE_DIR discord_rpc.h)
if (NOT DiscordRPC_FOUND) find_library(DiscordRPC_LIBRARY discord-rpc)
find_path(DiscordRPC_INCLUDE_DIR discord_rpc.h)
find_library(DiscordRPC_LIBRARY discord-rpc)
include(FindPackageHandleStandardArgs) include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(DiscordRPC find_package_handle_standard_args(DiscordRPC
REQUIRED_VARS REQUIRED_VARS
DiscordRPC_LIBRARY DiscordRPC_LIBRARY
DiscordRPC_INCLUDE_DIR DiscordRPC_INCLUDE_DIR
) )
if (DiscordRPC_FOUND AND NOT TARGET DiscordRPC::discord-rpc) if (DiscordRPC_FOUND AND NOT TARGET DiscordRPC::discord-rpc)
add_library(DiscordRPC::discord-rpc UNKNOWN IMPORTED) add_library(DiscordRPC::discord-rpc UNKNOWN IMPORTED)
set_target_properties(DiscordRPC::discord-rpc PROPERTIES set_target_properties(DiscordRPC::discord-rpc PROPERTIES
IMPORTED_LOCATION "${DiscordRPC_LIBRARY}" IMPORTED_LOCATION "${DiscordRPC_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${DiscordRPC_INCLUDE_DIR}" INTERFACE_INCLUDE_DIRECTORIES "${DiscordRPC_INCLUDE_DIR}"
) )
endif() endif()
mark_as_advanced( mark_as_advanced(
DiscordRPC_INCLUDE_DIR DiscordRPC_INCLUDE_DIR
DiscordRPC_LIBRARY DiscordRPC_LIBRARY
) )
endif()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -18,9 +18,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file> <file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">icons/48x48/chip.png</file> <file alias="48x48/chip.png">icons/48x48/chip.png</file>
<file alias="48x48/folder.png">icons/48x48/folder.png</file> <file alias="48x48/folder.png">icons/48x48/folder.png</file>
<file alias="48x48/user-trash.png">icons/48x48/user-trash.png</file>
<file alias="48x48/download.png">icons/48x48/download.png</file>
<file alias="48x48/upload.png">icons/48x48/upload.png</file>
<file alias="48x48/list-add.png">icons/48x48/list-add.png</file> <file alias="48x48/list-add.png">icons/48x48/list-add.png</file>
<file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file> <file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file>
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file> <file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>

View file

@ -11,9 +11,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
<file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file> <file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file> <file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
<file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file> <file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
<file alias="48x48/user-trash.png">../colorful/icons/48x48/user-trash.png</file>
<file alias="48x48/download.png">../colorful/icons/48x48/download.png</file>
<file alias="48x48/upload.png">../colorful/icons/48x48/upload.png</file>
<file alias="48x48/list-add.png">../colorful/icons/48x48/list-add.png</file> <file alias="48x48/list-add.png">../colorful/icons/48x48/list-add.png</file>
<file alias="48x48/sd_card.png">../colorful/icons/48x48/sd_card.png</file> <file alias="48x48/sd_card.png">../colorful/icons/48x48/sd_card.png</file>
<file alias="256x256/plus_folder.png">../colorful/icons/256x256/plus_folder.png</file> <file alias="256x256/plus_folder.png">../colorful/icons/256x256/plus_folder.png</file>

View file

@ -14,9 +14,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file> <file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">icons/48x48/chip.png</file> <file alias="48x48/chip.png">icons/48x48/chip.png</file>
<file alias="48x48/folder.png">icons/48x48/folder.png</file> <file alias="48x48/folder.png">icons/48x48/folder.png</file>
<file alias="48x48/user-trash.png">icons/48x48/user-trash.png</file>
<file alias="48x48/download.png">icons/48x48/download.png</file>
<file alias="48x48/upload.png">icons/48x48/upload.png</file>
<file alias="48x48/list-add.png">icons/48x48/list-add.png</file> <file alias="48x48/list-add.png">icons/48x48/list-add.png</file>
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file> <file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
<file alias="48x48/star.png">icons/48x48/star.png</file> <file alias="48x48/star.png">icons/48x48/star.png</file>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 853 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 820 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 584 B

View file

@ -13,9 +13,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
<file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file> <file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file> <file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
<file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file> <file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
<file alias="48x48/user-trash.png">../colorful/icons/48x48/user-trash.png</file>
<file alias="48x48/download.png">../colorful/icons/48x48/download.png</file>
<file alias="48x48/upload.png">../colorful/icons/48x48/upload.png</file>
<file alias="48x48/no_avatar.png">../qdarkstyle/icons/48x48/no_avatar.png</file> <file alias="48x48/no_avatar.png">../qdarkstyle/icons/48x48/no_avatar.png</file>
<file alias="48x48/list-add.png">../colorful/icons/48x48/list-add.png</file> <file alias="48x48/list-add.png">../colorful/icons/48x48/list-add.png</file>
<file alias="48x48/sd_card.png">../colorful/icons/48x48/sd_card.png</file> <file alias="48x48/sd_card.png">../colorful/icons/48x48/sd_card.png</file>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 883 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 853 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 584 B

View file

@ -9,9 +9,6 @@
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file> <file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">icons/48x48/chip.png</file> <file alias="48x48/chip.png">icons/48x48/chip.png</file>
<file alias="48x48/folder.png">icons/48x48/folder.png</file> <file alias="48x48/folder.png">icons/48x48/folder.png</file>
<file alias="48x48/user-trash.png">icons/48x48/user-trash.png</file>
<file alias="48x48/download.png">icons/48x48/download.png</file>
<file alias="48x48/upload.png">icons/48x48/upload.png</file>
<file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file> <file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file>
<file alias="48x48/list-add.png">icons/48x48/list-add.png</file> <file alias="48x48/list-add.png">icons/48x48/list-add.png</file>
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file> <file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>

View file

@ -6,9 +6,6 @@
<file alias="48x48/bad_folder.png">../qdarkstyle/icons/48x48/bad_folder.png</file> <file alias="48x48/bad_folder.png">../qdarkstyle/icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">../qdarkstyle/icons/48x48/chip.png</file> <file alias="48x48/chip.png">../qdarkstyle/icons/48x48/chip.png</file>
<file alias="48x48/folder.png">../qdarkstyle/icons/48x48/folder.png</file> <file alias="48x48/folder.png">../qdarkstyle/icons/48x48/folder.png</file>
<file alias="48x48/user-trash.png">../qdarkstyle/icons/48x48/user-trash.png</file>
<file alias="48x48/download.png">../qdarkstyle/icons/48x48/download.png</file>
<file alias="48x48/upload.png">../qdarkstyle/icons/48x48/upload.png</file>
<file alias="48x48/no_avatar.png">../qdarkstyle/icons/48x48/no_avatar.png</file> <file alias="48x48/no_avatar.png">../qdarkstyle/icons/48x48/no_avatar.png</file>
<file alias="48x48/list-add.png">../qdarkstyle/icons/48x48/list-add.png</file> <file alias="48x48/list-add.png">../qdarkstyle/icons/48x48/list-add.png</file>
<file alias="48x48/sd_card.png">../qdarkstyle/icons/48x48/sd_card.png</file> <file alias="48x48/sd_card.png">../qdarkstyle/icons/48x48/sd_card.png</file>

View file

@ -29,8 +29,7 @@
"repo": "yhirose/cpp-httplib", "repo": "yhirose/cpp-httplib",
"tag": "v%VERSION%", "tag": "v%VERSION%",
"hash": "b364500f76e2ecb0fe21b032d831272e3f1dfeea71af74e325f8fc4ce9dcdb3c941b97a5b422bdeafb9facd058597b90f8bfc284fb9afe3c33fefa15dd5a010b", "hash": "b364500f76e2ecb0fe21b032d831272e3f1dfeea71af74e325f8fc4ce9dcdb3c941b97a5b422bdeafb9facd058597b90f8bfc284fb9afe3c33fefa15dd5a010b",
"git_version": "0.26.0", "git_version": "0.26.0"
"find_args": "MODULE GLOBAL"
}, },
"cpp-jwt": { "cpp-jwt": {
"version": "1.4", "version": "1.4",
@ -97,11 +96,7 @@
"version": "3", "version": "3",
"git_version": "3.6.4", "git_version": "3.6.4",
"artifact": "%TAG%.tar.bz2", "artifact": "%TAG%.tar.bz2",
"skip_updates": true, "skip_updates": true
"patches": [
"0002-aesni-fix.patch",
"0003-aesni-fix.patch"
]
}, },
"enet": { "enet": {
"repo": "lsalzman/enet", "repo": "lsalzman/enet",

View file

@ -35,14 +35,17 @@ endif()
if(NOT YUZU_TZDB_PATH STREQUAL "") if(NOT YUZU_TZDB_PATH STREQUAL "")
set(NX_TZDB_BASE_DIR "${YUZU_TZDB_PATH}") set(NX_TZDB_BASE_DIR "${YUZU_TZDB_PATH}")
elseif (MSVC AND NOT CXX_CLANG AND YUZU_ENABLE_LTO) set(NX_TZDB_TZ_DIR "${NX_TZDB_BASE_DIR}/zoneinfo")
# TODO(crueter): boot up the windows vm elseif (MSVC)
# TODO(crueter): This is a terrible solution, but MSVC fails to link without it
# Need to investigate further but I still can't reproduce...
set(NX_TZDB_VERSION "250725") set(NX_TZDB_VERSION "250725")
set(NX_TZDB_ARCHIVE "${CPM_SOURCE_CACHE}/nx_tzdb/${NX_TZDB_VERSION}.zip") set(NX_TZDB_ARCHIVE "${CPM_SOURCE_CACHE}/nx_tzdb/${NX_TZDB_VERSION}.zip")
set(NX_TZDB_BASE_DIR "${CPM_SOURCE_CACHE}/nx_tzdb/tz") set(NX_TZDB_BASE_DIR "${CPM_SOURCE_CACHE}/nx_tzdb/tz")
set(NX_TZDB_TZ_DIR "${NX_TZDB_BASE_DIR}/zoneinfo")
set(NX_TZDB_DOWNLOAD_URL "https://git.crueter.xyz/misc/tzdb_to_nx/releases/download/${NX_TZDB_VERSION}/${NX_TZDB_VERSION}.zip") set(NX_TZDB_DOWNLOAD_URL "https://github.com/crueter/tzdb_to_nx/releases/download/${NX_TZDB_VERSION}/${NX_TZDB_VERSION}.zip")
message(STATUS "Downloading time zone data from ${NX_TZDB_DOWNLOAD_URL}...") message(STATUS "Downloading time zone data from ${NX_TZDB_DOWNLOAD_URL}...")
file(DOWNLOAD ${NX_TZDB_DOWNLOAD_URL} ${NX_TZDB_ARCHIVE} file(DOWNLOAD ${NX_TZDB_DOWNLOAD_URL} ${NX_TZDB_ARCHIVE}
@ -62,10 +65,13 @@ else()
message(STATUS "Downloading time zone data...") message(STATUS "Downloading time zone data...")
AddJsonPackage(tzdb) AddJsonPackage(tzdb)
set(NX_TZDB_BASE_DIR "${nx_tzdb_SOURCE_DIR}") target_include_directories(nx_tzdb
endif() INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include
INTERFACE ${NX_TZDB_INCLUDE_DIR})
set(NX_TZDB_TZ_DIR "${NX_TZDB_BASE_DIR}/zoneinfo") set(NX_TZDB_BASE_DIR "${CPM_SOURCE_CACHE}/nx_tzdb")
set(NX_TZDB_TZ_DIR "${nx_tzdb_SOURCE_DIR}")
endif()
target_include_directories(nx_tzdb target_include_directories(nx_tzdb
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include

View file

@ -3,9 +3,9 @@
"package": "nx_tzdb", "package": "nx_tzdb",
"repo": "misc/tzdb_to_nx", "repo": "misc/tzdb_to_nx",
"git_host": "git.crueter.xyz", "git_host": "git.crueter.xyz",
"artifact": "%VERSION%.tar.gz", "artifact": "%VERSION%.zip",
"tag": "%VERSION%", "tag": "%VERSION%",
"hash": "87abb2aeca716d5d77b05317086dbc2f8acfc2f3f76ce4778345ee3df19973e6cd8ecbf16cfab5ad94c9636a6c44fd3588f9aadd3cba89403cfd56c8bec645c5", "hash": "8f60b4b29f285e39c0443f3d5572a73780f3dbfcfd5b35004451fadad77f3a215b2e2aa8d0fffe7e348e2a7b0660882b35228b6178dda8804a14ce44509fd2ca",
"version": "091025" "version": "250725"
} }
} }

View file

@ -58,7 +58,8 @@ android {
defaultConfig { defaultConfig {
applicationId = "dev.eden.eden_emulator" applicationId = "dev.eden.eden_emulator"
minSdk = 24
minSdk = 28
targetSdk = 36 targetSdk = 36
versionName = getGitVersion() versionName = getGitVersion()

View file

@ -230,9 +230,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
override fun dispatchKeyEvent(event: KeyEvent): Boolean { override fun dispatchKeyEvent(event: KeyEvent): Boolean {
if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK && if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK &&
event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD && event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD
event.source and InputDevice.SOURCE_KEYBOARD != InputDevice.SOURCE_KEYBOARD &&
event.source and InputDevice.SOURCE_MOUSE != InputDevice.SOURCE_MOUSE
) { ) {
return super.dispatchKeyEvent(event) return super.dispatchKeyEvent(event)
} }
@ -246,9 +244,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
override fun dispatchGenericMotionEvent(event: MotionEvent): Boolean { override fun dispatchGenericMotionEvent(event: MotionEvent): Boolean {
if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK && if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK &&
event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD && event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD
event.source and InputDevice.SOURCE_KEYBOARD != InputDevice.SOURCE_KEYBOARD &&
event.source and InputDevice.SOURCE_MOUSE != InputDevice.SOURCE_MOUSE
) { ) {
return super.dispatchGenericMotionEvent(event) return super.dispatchGenericMotionEvent(event)
} }

View file

@ -51,6 +51,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
SOC_OVERLAY_BACKGROUND("soc_overlay_background"), SOC_OVERLAY_BACKGROUND("soc_overlay_background"),
ENABLE_RAII("enable_raii"),
FRAME_INTERPOLATION("frame_interpolation"), FRAME_INTERPOLATION("frame_interpolation"),
// FRAME_SKIPPING("frame_skipping"), // FRAME_SKIPPING("frame_skipping"),
@ -70,6 +71,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
DEBUG_FLUSH_BY_LINE("flush_line"), DEBUG_FLUSH_BY_LINE("flush_line"),
USE_LRU_CACHE("use_lru_cache"); USE_LRU_CACHE("use_lru_cache");
external fun isRaiiEnabled(): Boolean
// external fun isFrameSkippingEnabled(): Boolean // external fun isFrameSkippingEnabled(): Boolean
external fun isFrameInterpolationEnabled(): Boolean external fun isFrameInterpolationEnabled(): Boolean

View file

@ -229,6 +229,13 @@ abstract class SettingsItem(
override fun reset() = BooleanSetting.USE_DOCKED_MODE.reset() override fun reset() = BooleanSetting.USE_DOCKED_MODE.reset()
} }
put(
SwitchSetting(
BooleanSetting.ENABLE_RAII,
titleId = R.string.enable_raii,
descriptionId = R.string.enable_raii_description
)
)
put( put(
SwitchSetting( SwitchSetting(
BooleanSetting.FRAME_INTERPOLATION, BooleanSetting.FRAME_INTERPOLATION,
@ -826,4 +833,3 @@ abstract class SettingsItem(
} }
} }
} }

View file

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project // SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -152,9 +149,7 @@ class InputDialogFragment : DialogFragment() {
private fun onKeyEvent(event: KeyEvent): Boolean { private fun onKeyEvent(event: KeyEvent): Boolean {
if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK && if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK &&
event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD && event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD
event.source and InputDevice.SOURCE_KEYBOARD != InputDevice.SOURCE_KEYBOARD &&
event.source and InputDevice.SOURCE_MOUSE != InputDevice.SOURCE_MOUSE
) { ) {
return false return false
} }
@ -178,9 +173,7 @@ class InputDialogFragment : DialogFragment() {
private fun onMotionEvent(event: MotionEvent): Boolean { private fun onMotionEvent(event: MotionEvent): Boolean {
if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK && if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK &&
event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD && event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD
event.source and InputDevice.SOURCE_KEYBOARD != InputDevice.SOURCE_KEYBOARD &&
event.source and InputDevice.SOURCE_MOUSE != InputDevice.SOURCE_MOUSE
) { ) {
return false return false
} }

View file

@ -462,6 +462,7 @@ class SettingsFragmentPresenter(
add(IntSetting.RENDERER_SAMPLE_SHADING_FRACTION.key) add(IntSetting.RENDERER_SAMPLE_SHADING_FRACTION.key)
add(HeaderSetting(R.string.veil_renderer)) add(HeaderSetting(R.string.veil_renderer))
add(BooleanSetting.ENABLE_RAII.key)
add(BooleanSetting.RENDERER_EARLY_RELEASE_FENCES.key) add(BooleanSetting.RENDERER_EARLY_RELEASE_FENCES.key)
add(IntSetting.DMA_ACCURACY.key) add(IntSetting.DMA_ACCURACY.key)
add(BooleanSetting.BUFFER_REORDER_DISABLE.key) add(BooleanSetting.BUFFER_REORDER_DISABLE.key)

View file

@ -1039,7 +1039,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
scale /= 100f scale /= 100f
// Apply individual scale // Apply individual scale
scale *= overlayControlData.individualScale.let { if (it > 0f) it else 1f } scale *= overlayControlData.individualScale
// Initialize the InputOverlayDrawableButton. // Initialize the InputOverlayDrawableButton.
val defaultStateBitmap = getBitmap(context, defaultResId, scale) val defaultStateBitmap = getBitmap(context, defaultResId, scale)
@ -1114,7 +1114,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
// Apply individual scale // Apply individual scale
if (dpadData != null) { if (dpadData != null) {
scale *= dpadData.individualScale.let { if (it > 0f) it else 1f } scale *= dpadData.individualScale
} }
// Initialize the InputOverlayDrawableDpad. // Initialize the InputOverlayDrawableDpad.
@ -1191,7 +1191,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
scale /= 100f scale /= 100f
// Apply individual scale // Apply individual scale
scale *= overlayControlData.individualScale.let { if (it > 0f) it else 1f } scale *= overlayControlData.individualScale
// Initialize the InputOverlayDrawableJoystick. // Initialize the InputOverlayDrawableJoystick.
val bitmapOuter = getBitmap(context, resOuter, scale) val bitmapOuter = getBitmap(context, resOuter, scale)

View file

@ -1,11 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.utils package org.yuzu.yuzu_emu.utils
object AddonUtil { object AddonUtil {
val validAddonDirectories = listOf("cheats", "exefs", "romfs", "romfslite") val validAddonDirectories = listOf("cheats", "exefs", "romfs")
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 244 KiB

After

Width:  |  Height:  |  Size: 438 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

View file

@ -64,6 +64,8 @@
<!-- Eden\'s Veil --> <!-- Eden\'s Veil -->
<string name="veil_extensions">امتدادات GPU</string> <string name="veil_extensions">امتدادات GPU</string>
<string name="veil_renderer">العارض</string> <string name="veil_renderer">العارض</string>
<string name="enable_raii">RAII</string>
<string name="enable_raii_description">طريقة لإدارة الموارد تلقائيًا في فولكان تضمن الإفراج الصحيح عن الموارد عندما لا تكون هناك حاجة إليها، ولكن قد تسبب تعطل الألعاب المجمعة.</string>
<string name="veil_misc">وحدة المعالجة المركزية والذاكرة</string> <string name="veil_misc">وحدة المعالجة المركزية والذاكرة</string>
<string name="eden_veil">حجاب عدن</string> <string name="eden_veil">حجاب عدن</string>
<string name="eden_veil_description">إعدادات تجريبية لتحسين الأداء والقدرة. قد تسبب هذه الإعدادات شاشات سوداء أو مشاكل أخرى في اللعبة.</string> <string name="eden_veil_description">إعدادات تجريبية لتحسين الأداء والقدرة. قد تسبب هذه الإعدادات شاشات سوداء أو مشاكل أخرى في اللعبة.</string>

View file

@ -65,6 +65,8 @@
<!-- Eden\'s Veil --> <!-- Eden\'s Veil -->
<string name="veil_extensions">پاشکۆکانی GPU</string> <string name="veil_extensions">پاشکۆکانی GPU</string>
<string name="veil_renderer">رێندرەر</string> <string name="veil_renderer">رێندرەر</string>
<string name="enable_raii">RAII</string>
<string name="enable_raii_description">ڕێگایەکی بەڕێوەبردنی سەرچاوەکان بە خۆکار لە ڤولکان کە دڵنیای دەکاتەوە لە ئازادکردنی گونجاوی سەرچاوەکان کاتێک کە چیتر پێویستیان نییە، بەڵام لەوانەیە ببێتە هۆی کەوتنی یارییە کۆکراوەکان.</string>
<string name="veil_misc">CPU و بیرگە</string> <string name="veil_misc">CPU و بیرگە</string>
<string name="eden_veil">حجاب عدن</string> <string name="eden_veil">حجاب عدن</string>
<string name="eden_veil_description">ڕێکخستنە تاقیکارییەکان بۆ باشترکردنی کارایی و توانا. ئەم ڕێکخستنانە لەوانەیە ببێتە هۆی شاشە ڕەشەکان یان کێشەیتری یاری.</string> <string name="eden_veil_description">ڕێکخستنە تاقیکارییەکان بۆ باشترکردنی کارایی و توانا. ئەم ڕێکخستنانە لەوانەیە ببێتە هۆی شاشە ڕەشەکان یان کێشەیتری یاری.</string>

View file

@ -64,6 +64,8 @@
<!-- Eden\'s Veil --> <!-- Eden\'s Veil -->
<string name="veil_extensions">Rozšíření GPU</string> <string name="veil_extensions">Rozšíření GPU</string>
<string name="veil_renderer">Renderer</string> <string name="veil_renderer">Renderer</string>
<string name="enable_raii">RAII</string>
<string name="enable_raii_description">Metoda automatické správy prostředků ve Vulkanu, která zajišťuje správné uvolnění prostředků, když již nejsou potřeba, ale může způsobit pády v balených hrách.</string>
<string name="veil_misc">CPU a paměť</string> <string name="veil_misc">CPU a paměť</string>
<string name="eden_veil">Edenův závoj</string> <string name="eden_veil">Edenův závoj</string>
<string name="eden_veil_description">Experimentální nastavení pro zlepšení výkonu a schopností. Tato nastavení mohou způsobit černé obrazovky nebo další herní problémy.</string> <string name="eden_veil_description">Experimentální nastavení pro zlepšení výkonu a schopností. Tato nastavení mohou způsobit černé obrazovky nebo další herní problémy.</string>

View file

@ -65,6 +65,8 @@
<!-- Eden\'s Veil --> <!-- Eden\'s Veil -->
<string name="veil_extensions">GPU-Erweiterungen</string> <string name="veil_extensions">GPU-Erweiterungen</string>
<string name="veil_renderer">Renderer</string> <string name="veil_renderer">Renderer</string>
<string name="enable_raii">RAII</string>
<string name="enable_raii_description">Eine Methode zur automatischen Ressourcenverwaltung in Vulkan, die eine ordnungsgemäße Freigabe von Ressourcen gewährleistet, wenn sie nicht mehr benötigt werden, aber bei gebündelten Spielen Abstürze verursachen kann.</string>
<string name="veil_misc">CPU und Speicher</string> <string name="veil_misc">CPU und Speicher</string>
<string name="eden_veil">Edens Schleier</string> <string name="eden_veil">Edens Schleier</string>
<string name="eden_veil_description">Experimentelle Einstellungen zur Verbesserung der Leistung und Funktionalität. Diese Einstellungen können schwarze Bildschirme oder andere Spielprobleme verursachen.</string> <string name="eden_veil_description">Experimentelle Einstellungen zur Verbesserung der Leistung und Funktionalität. Diese Einstellungen können schwarze Bildschirme oder andere Spielprobleme verursachen.</string>

View file

@ -65,6 +65,8 @@
<!-- Eden\'s Veil --> <!-- Eden\'s Veil -->
<string name="veil_extensions">Extensiones de GPU</string> <string name="veil_extensions">Extensiones de GPU</string>
<string name="veil_renderer">Renderizador</string> <string name="veil_renderer">Renderizador</string>
<string name="enable_raii">RAII</string>
<string name="enable_raii_description">Un método de gestión automática de recursos en Vulkan que garantiza la liberación adecuada de recursos cuando ya no son necesarios, pero puede causar fallos en juegos empaquetados.</string>
<string name="veil_misc">CPU y memoria</string> <string name="veil_misc">CPU y memoria</string>
<string name="eden_veil">Velo de Edén</string> <string name="eden_veil">Velo de Edén</string>
<string name="eden_veil_description">Configuraciones experimentales para mejorar el rendimiento y la capacidad. Estas configuraciones pueden causar pantallas negras u otros problemas en el juego.</string> <string name="eden_veil_description">Configuraciones experimentales para mejorar el rendimiento y la capacidad. Estas configuraciones pueden causar pantallas negras u otros problemas en el juego.</string>

View file

@ -65,6 +65,8 @@
<!-- Eden\'s Veil --> <!-- Eden\'s Veil -->
<string name="veil_extensions">افزونه‌های GPU</string> <string name="veil_extensions">افزونه‌های GPU</string>
<string name="veil_renderer">رندرر</string> <string name="veil_renderer">رندرر</string>
<string name="enable_raii">RAII</string>
<string name="enable_raii_description">روشی برای مدیریت خودکار منابع در ولکان که تضمین می‌کند منابع به درستی آزاد شوند وقتی دیگر مورد نیاز نیستند، اما ممکن است باعث کرش شدن بازی‌های بسته‌بندی شده شود.</string>
<string name="veil_misc">پردازنده و حافظه</string> <string name="veil_misc">پردازنده و حافظه</string>
<string name="eden_veil">پرده عدن</string> <string name="eden_veil">پرده عدن</string>
<string name="eden_veil_description">تنظیمات آزمایشی برای بهبود عملکرد و قابلیت. این تنظیمات ممکن است باعث نمایش صفحه سیاه یا سایر مشکلات بازی شود.</string> <string name="eden_veil_description">تنظیمات آزمایشی برای بهبود عملکرد و قابلیت. این تنظیمات ممکن است باعث نمایش صفحه سیاه یا سایر مشکلات بازی شود.</string>

View file

@ -65,6 +65,8 @@
<!-- Eden\'s Veil --> <!-- Eden\'s Veil -->
<string name="veil_extensions">Extensions GPU</string> <string name="veil_extensions">Extensions GPU</string>
<string name="veil_renderer">Rendu</string> <string name="veil_renderer">Rendu</string>
<string name="enable_raii">RAII</string>
<string name="enable_raii_description">Une méthode de gestion automatique des ressources dans Vulkan qui assure la libération correcte des ressources lorsqu\'elles ne sont plus nécessaires, mais peut provoquer des plantages dans les jeux regroupés.</string>
<string name="veil_misc">CPU et mémoire</string> <string name="veil_misc">CPU et mémoire</string>
<string name="eden_veil">Voile d\'Eden</string> <string name="eden_veil">Voile d\'Eden</string>
<string name="eden_veil_description">Paramètres expérimentaux pour améliorer les performances et les capacités. Ces paramètres peuvent causer des écrans noirs ou d\'autres problèmes de jeu.</string> <string name="eden_veil_description">Paramètres expérimentaux pour améliorer les performances et les capacités. Ces paramètres peuvent causer des écrans noirs ou d\'autres problèmes de jeu.</string>

View file

@ -65,6 +65,8 @@
<!-- Eden\'s Veil --> <!-- Eden\'s Veil -->
<string name="veil_extensions">הרחבות GPU</string> <string name="veil_extensions">הרחבות GPU</string>
<string name="veil_renderer">רנדרר</string> <string name="veil_renderer">רנדרר</string>
<string name="enable_raii">RAII</string>
<string name="enable_raii_description">שיטה לניהול אוטומטי של משאבים ב-Vulkan המבטיחה שחרור נכון של משאבים כאשר הם כבר לא נחוצים, אך עלולה לגרום לקריסות במשחקים מאוגדים.</string>
<string name="veil_misc">מעבד וזיכרון</string> <string name="veil_misc">מעבד וזיכרון</string>
<string name="eden_veil">עדן וייל</string> <string name="eden_veil">עדן וייל</string>
<string name="eden_veil_description">הגדרות ניסיוניות לשיפור ביצועים ויכולות. הגדרות אלו עלולות לגרום למסכים שחורים או לבעיות אחרות במשחק.</string> <string name="eden_veil_description">הגדרות ניסיוניות לשיפור ביצועים ויכולות. הגדרות אלו עלולות לגרום למסכים שחורים או לבעיות אחרות במשחק.</string>

View file

@ -65,6 +65,8 @@
<!-- Eden\'s Veil --> <!-- Eden\'s Veil -->
<string name="veil_extensions">GPU kiterjesztések</string> <string name="veil_extensions">GPU kiterjesztések</string>
<string name="veil_renderer">Megjelenítő</string> <string name="veil_renderer">Megjelenítő</string>
<string name="enable_raii">RAII</string>
<string name="enable_raii_description">A Vulkan erőforrás-kezelési módszere, amely biztosítja az erőforrások megfelelő felszabadítását, ha már nincs rájuk szükség, de csomagolt játékok összeomlását okozhatja.</string>
<string name="veil_misc">CPU és memória</string> <string name="veil_misc">CPU és memória</string>
<string name="eden_veil">Eden Fátyla</string> <string name="eden_veil">Eden Fátyla</string>
<string name="eden_veil_description">Kísérleti beállítások a teljesítmény és képesség javításához. Ezek a beállítások fekete képernyőket vagy más játékproblémákat okozhatnak.</string> <string name="eden_veil_description">Kísérleti beállítások a teljesítmény és képesség javításához. Ezek a beállítások fekete képernyőket vagy más játékproblémákat okozhatnak.</string>

View file

@ -65,6 +65,8 @@
<!-- Eden\'s Veil --> <!-- Eden\'s Veil -->
<string name="veil_extensions">Ekstensi GPU</string> <string name="veil_extensions">Ekstensi GPU</string>
<string name="veil_renderer">Renderer</string> <string name="veil_renderer">Renderer</string>
<string name="enable_raii">RAII</string>
<string name="enable_raii_description">Metode manajemen sumber daya otomatis di Vulkan yang memastikan pelepasan sumber daya yang tepat ketika tidak lagi diperlukan, tetapi dapat menyebabkan crash pada game yang dibundel.</string>
<string name="veil_misc">CPU dan Memori</string> <string name="veil_misc">CPU dan Memori</string>
<string name="eden_veil">Eden\'s Veil</string> <string name="eden_veil">Eden\'s Veil</string>
<string name="eden_veil_description">Pengaturan eksperimental untuk meningkatkan kinerja dan kemampuan. Pengaturan ini dapat menyebabkan layar hitam atau masalah game lainnya.</string> <string name="eden_veil_description">Pengaturan eksperimental untuk meningkatkan kinerja dan kemampuan. Pengaturan ini dapat menyebabkan layar hitam atau masalah game lainnya.</string>

View file

@ -65,6 +65,8 @@
<!-- Eden\'s Veil --> <!-- Eden\'s Veil -->
<string name="veil_extensions">Estensioni GPU</string> <string name="veil_extensions">Estensioni GPU</string>
<string name="veil_renderer">Renderer</string> <string name="veil_renderer">Renderer</string>
<string name="enable_raii">RAII</string>
<string name="enable_raii_description">Un metodo di gestione automatica delle risorse in Vulkan che garantisce il corretto rilascio delle risorse quando non sono più necessarie, ma può causare crash nei giochi in bundle.</string>
<string name="veil_misc">CPU e Memoria</string> <string name="veil_misc">CPU e Memoria</string>
<string name="eden_veil">Velo di Eden</string> <string name="eden_veil">Velo di Eden</string>
<string name="eden_veil_description">Impostazioni sperimentali per migliorare prestazioni e capacità. Queste impostazioni possono causare schermate nere o altri problemi di gioco.</string> <string name="eden_veil_description">Impostazioni sperimentali per migliorare prestazioni e capacità. Queste impostazioni possono causare schermate nere o altri problemi di gioco.</string>

View file

@ -65,6 +65,8 @@
<!-- Eden\'s Veil --> <!-- Eden\'s Veil -->
<string name="veil_extensions">GPU拡張機能</string> <string name="veil_extensions">GPU拡張機能</string>
<string name="veil_renderer">レンダラー</string> <string name="veil_renderer">レンダラー</string>
<string name="enable_raii">RAII</string>
<string name="enable_raii_description">Vulkanにおける自動リソース管理の方法で、不要になったリソースを適切に解放しますが、バンドルされたゲームでクラッシュを引き起こす可能性があります。</string>
<string name="veil_misc">CPUとメモリ</string> <string name="veil_misc">CPUとメモリ</string>
<string name="eden_veil">エデンのベール</string> <string name="eden_veil">エデンのベール</string>
<string name="eden_veil_description">パフォーマンスと機能を向上させる実験的な設定。これらの設定は黒画面やその他のゲームの問題を引き起こす可能性があります。</string> <string name="eden_veil_description">パフォーマンスと機能を向上させる実験的な設定。これらの設定は黒画面やその他のゲームの問題を引き起こす可能性があります。</string>

View file

@ -65,6 +65,8 @@
<!-- Eden\'s Veil --> <!-- Eden\'s Veil -->
<string name="veil_extensions">GPU 확장 기능</string> <string name="veil_extensions">GPU 확장 기능</string>
<string name="veil_renderer">렌더러</string> <string name="veil_renderer">렌더러</string>
<string name="enable_raii">RAII</string>
<string name="enable_raii_description">Vulkan에서 자동 리소스 관리를 위한 방법으로, 더 이상 필요하지 않은 리소스를 적절히 해제하지만 번들된 게임에서 충돌을 일으킬 수 있습니다.</string>
<string name="veil_misc">CPU 및 메모리</string> <string name="veil_misc">CPU 및 메모리</string>
<string name="eden_veil">에덴의 베일</string> <string name="eden_veil">에덴의 베일</string>
<string name="eden_veil_description">성능 및 기능을 향상시키기 위한 실험적 설정. 이 설정은 검은 화면 또는 기타 게임 문제를 일으킬 수 있습니다.</string> <string name="eden_veil_description">성능 및 기능을 향상시키기 위한 실험적 설정. 이 설정은 검은 화면 또는 기타 게임 문제를 일으킬 수 있습니다.</string>

View file

@ -65,6 +65,8 @@
<!-- Eden\'s Veil --> <!-- Eden\'s Veil -->
<string name="veil_extensions">GPU-utvidelser</string> <string name="veil_extensions">GPU-utvidelser</string>
<string name="veil_renderer">Renderer</string> <string name="veil_renderer">Renderer</string>
<string name="enable_raii">RAII</string>
<string name="enable_raii_description">En metode for automatisk ressurshåndtering i Vulkan som sikrer riktig frigjøring av ressurser når de ikke lenger trengs, men kan føre til krasj i bundlede spill.</string>
<string name="veil_misc">CPU og minne</string> <string name="veil_misc">CPU og minne</string>
<string name="eden_veil">Edens slør</string> <string name="eden_veil">Edens slør</string>
<string name="eden_veil_description">Eksperimentelle innstillinger for å forbedre ytelse og funksjonalitet. Disse innstillingene kan forårsake svarte skjermer eller andre spillproblemer.</string> <string name="eden_veil_description">Eksperimentelle innstillinger for å forbedre ytelse og funksjonalitet. Disse innstillingene kan forårsake svarte skjermer eller andre spillproblemer.</string>

View file

@ -65,6 +65,8 @@
<!-- Eden\'s Veil --> <!-- Eden\'s Veil -->
<string name="veil_extensions">Rozszerzenia GPU</string> <string name="veil_extensions">Rozszerzenia GPU</string>
<string name="veil_renderer">Renderer</string> <string name="veil_renderer">Renderer</string>
<string name="enable_raii">RAII</string>
<string name="enable_raii_description">Metoda automatycznego zarządzania zasobami w Vulkanie, która zapewnia prawidłowe zwalnianie zasobów, gdy nie są już potrzebne, ale może powodować awarie w pakietowych grach.</string>
<string name="veil_misc">Procesor i pamięć</string> <string name="veil_misc">Procesor i pamięć</string>
<string name="eden_veil">Zasłona Edenu</string> <string name="eden_veil">Zasłona Edenu</string>
<string name="eden_veil_description">Eksperymentalne ustawienia poprawiające wydajność i możliwości. Te ustawienia mogą powodować czarne ekrany lub inne problemy z grą.</string> <string name="eden_veil_description">Eksperymentalne ustawienia poprawiające wydajność i możliwości. Te ustawienia mogą powodować czarne ekrany lub inne problemy z grą.</string>

View file

@ -65,6 +65,8 @@
<!-- Eden\'s Veil --> <!-- Eden\'s Veil -->
<string name="veil_extensions">Extensões da GPU</string> <string name="veil_extensions">Extensões da GPU</string>
<string name="veil_renderer">Renderizador</string> <string name="veil_renderer">Renderizador</string>
<string name="enable_raii">RAII</string>
<string name="enable_raii_description">Um método de gerenciamento automático de recursos no Vulkan que garante a liberação adequada de recursos quando não são mais necessários, mas pode causar falhas em jogos empacotados.</string>
<string name="veil_misc">CPU e Memória</string> <string name="veil_misc">CPU e Memória</string>
<string name="eden_veil">Véu do Éden</string> <string name="eden_veil">Véu do Éden</string>
<string name="eden_veil_description">Configurações experimentais para melhorar desempenho e capacidade. Essas configurações podem causar telas pretas ou outros problemas no jogo.</string> <string name="eden_veil_description">Configurações experimentais para melhorar desempenho e capacidade. Essas configurações podem causar telas pretas ou outros problemas no jogo.</string>

View file

@ -65,6 +65,8 @@
<!-- Eden\'s Veil --> <!-- Eden\'s Veil -->
<string name="veil_extensions">Extensões da GPU</string> <string name="veil_extensions">Extensões da GPU</string>
<string name="veil_renderer">Renderizador</string> <string name="veil_renderer">Renderizador</string>
<string name="enable_raii">RAII</string>
<string name="enable_raii_description">Um método de gestão automática de recursos no Vulkan que garante a libertação adequada de recursos quando já não são necessários, mas pode causar falhas em jogos empacotados.</string>
<string name="veil_misc">CPU e Memória</string> <string name="veil_misc">CPU e Memória</string>
<string name="eden_veil">Véu do Éden</string> <string name="eden_veil">Véu do Éden</string>
<string name="eden_veil_description">Definições experimentais para melhorar o desempenho e capacidade. Estas definições podem causar ecrãs pretos ou outros problemas no jogo.</string> <string name="eden_veil_description">Definições experimentais para melhorar o desempenho e capacidade. Estas definições podem causar ecrãs pretos ou outros problemas no jogo.</string>

View file

@ -72,6 +72,8 @@
<string name="eden_veil_warning_description">Настройки в Покров Эдема являются экспериментальными и могут вызывать проблемы. Если ваша игра не запускается, отключите все расширения.</string> <string name="eden_veil_warning_description">Настройки в Покров Эдема являются экспериментальными и могут вызывать проблемы. Если ваша игра не запускается, отключите все расширения.</string>
<string name="frame_skipping">В разработке: Пропуск кадров</string> <string name="frame_skipping">В разработке: Пропуск кадров</string>
<string name="frame_skipping_description">Включите или отключите пропуск кадров для повышения производительности за счет уменьшения количества отображаемых кадров. Эта функция находится в разработке и будет включена в будущих версиях.</string> <string name="frame_skipping_description">Включите или отключите пропуск кадров для повышения производительности за счет уменьшения количества отображаемых кадров. Эта функция находится в разработке и будет включена в будущих версиях.</string>
<string name="enable_raii">RAII</string>
<string name="enable_raii_description">Метод автоматического управления ресурсами в Vulkan, который обеспечивает правильное освобождение ресурсов при их ненадобности, но может вызывать сбои в бандл-играх.</string>
<string name="frame_interpolation">Улучшенная синхронизация кадров</string> <string name="frame_interpolation">Улучшенная синхронизация кадров</string>
<string name="frame_interpolation_description">Обеспечивает плавную и стабильную подачу кадров за счет синхронизации их времени, уменьшая подтормаживания и неравномерную анимацию. Идеально для игр с нестабильным временем кадров или микро-подтормаживаниями во время игры.</string> <string name="frame_interpolation_description">Обеспечивает плавную и стабильную подачу кадров за счет синхронизации их времени, уменьшая подтормаживания и неравномерную анимацию. Идеально для игр с нестабильным временем кадров или микро-подтормаживаниями во время игры.</string>
<string name="renderer_early_release_fences">Ранний релиз ограждений</string> <string name="renderer_early_release_fences">Ранний релиз ограждений</string>

View file

@ -81,6 +81,8 @@
<string name="descriptor_indexing_description">Побољшава текстуру и руковање међуспремника, као и преводилачки слој Маквелл. Подржани од стране неких Вулкана 1.1 ГПУ-а и сви Вулкан 1.2+ ГПУ.</string> <string name="descriptor_indexing_description">Побољшава текстуру и руковање међуспремника, као и преводилачки слој Маквелл. Подржани од стране неких Вулкана 1.1 ГПУ-а и сви Вулкан 1.2+ ГПУ.</string>
<string name="veil_renderer">Рендерер</string> <string name="veil_renderer">Рендерер</string>
<string name="enable_raii">RAII</string>
<string name="enable_raii_description">Метод аутоматског управљања ресурсима у Vulkan-у који осигурава правилно ослобађање ресурса када више нису потребни, али може изазвати падове у пакованим играма.</string>
<string name="frame_interpolation">Побољшани оквирни пејсинг</string> <string name="frame_interpolation">Побољшани оквирни пејсинг</string>
<string name="frame_interpolation_description">Осигурава глатку и доследан испоруку оквира синхронизацијом времена између оквира, смањење муцања и неуједначене анимације. Идеално за игре које доживљавају временски оквир нестабилност или микро-штитнике током играња.</string> <string name="frame_interpolation_description">Осигурава глатку и доследан испоруку оквира синхронизацијом времена између оквира, смањење муцања и неуједначене анимације. Идеално за игре које доживљавају временски оквир нестабилност или микро-штитнике током играња.</string>
<string name="renderer_early_release_fences">Ranije oslobađanje ograda</string> <string name="renderer_early_release_fences">Ranije oslobađanje ograda</string>

View file

@ -70,6 +70,8 @@
<string name="eden_veil_description">Експериментальні налаштування для покращення продуктивності та сумісності. Ці налаштування можуть викликати збої, зокрема чорний екран.</string> <string name="eden_veil_description">Експериментальні налаштування для покращення продуктивності та сумісності. Ці налаштування можуть викликати збої, зокрема чорний екран.</string>
<string name="eden_veil_warning_title">Експериментальні налаштування</string> <string name="eden_veil_warning_title">Експериментальні налаштування</string>
<string name="eden_veil_warning_description">Налаштування Завіси Eden є експериментальними та можуть спричинити проблеми. Якщо ваша гра не запускається — вимкніть усі розширення.</string> <string name="eden_veil_warning_description">Налаштування Завіси Eden є експериментальними та можуть спричинити проблеми. Якщо ваша гра не запускається — вимкніть усі розширення.</string>
<string name="enable_raii">RAII</string>
<string name="enable_raii_description">Метод автоматичного керування ресурсами у Vulkan, який забезпечує правильне звільнення ресурсів після завершення їх використання, проте він може спричинити збої в ігрових збірниках.</string>
<string name="frame_skipping">В розробці: Пропуск кадрів</string> <string name="frame_skipping">В розробці: Пропуск кадрів</string>
<string name="frame_skipping_description">Увімкніть або вимкніть пропуск кадрів для покращення продуктивності за рахунок зменшення кількості візуалізованих кадрів. Ця функція ще розробляється та буде доступна у майбутніх версіях.</string> <string name="frame_skipping_description">Увімкніть або вимкніть пропуск кадрів для покращення продуктивності за рахунок зменшення кількості візуалізованих кадрів. Ця функція ще розробляється та буде доступна у майбутніх версіях.</string>
<string name="frame_interpolation">Покращена синхронізація кадрів</string> <string name="frame_interpolation">Покращена синхронізація кадрів</string>

View file

@ -65,6 +65,8 @@
<!-- Eden\'s Veil --> <!-- Eden\'s Veil -->
<string name="veil_extensions">Tiện ích mở rộng GPU</string> <string name="veil_extensions">Tiện ích mở rộng GPU</string>
<string name="veil_renderer">Trình kết xuất</string> <string name="veil_renderer">Trình kết xuất</string>
<string name="enable_raii">RAII</string>
<string name="enable_raii_description">Phương pháp quản lý tài nguyên tự động trong Vulkan đảm bảo giải phóng tài nguyên đúng cách khi không còn cần thiết, nhưng có thể gây ra sự cố trong các trò chơi được đóng gói.</string>
<string name="veil_misc">CPU và Bộ nhớ</string> <string name="veil_misc">CPU và Bộ nhớ</string>
<string name="eden_veil">Mành che của Eden</string> <string name="eden_veil">Mành che của Eden</string>
<string name="eden_veil_description">Cài đặt thử nghiệm để cải thiện hiệu suất và khả năng. Những cài đặt này có thể gây ra màn hình đen hoặc các vấn đề khác trong trò chơi.</string> <string name="eden_veil_description">Cài đặt thử nghiệm để cải thiện hiệu suất và khả năng. Những cài đặt này có thể gây ra màn hình đen hoặc các vấn đề khác trong trò chơi.</string>

View file

@ -65,6 +65,8 @@
<!-- Eden\'s Veil --> <!-- Eden\'s Veil -->
<string name="veil_extensions">GPU扩展</string> <string name="veil_extensions">GPU扩展</string>
<string name="veil_renderer">渲染器</string> <string name="veil_renderer">渲染器</string>
<string name="enable_raii">RAII</string>
<string name="enable_raii_description">Vulkan中的一种自动资源管理方法确保在不再需要时正确释放资源但可能导致捆绑游戏崩溃。</string>
<string name="veil_misc">CPU和内存</string> <string name="veil_misc">CPU和内存</string>
<string name="eden_veil">伊甸之幕</string> <string name="eden_veil">伊甸之幕</string>
<string name="eden_veil_description">实验性设置以提高性能和能力。这些设置可能会导致黑屏或其他游戏问题。</string> <string name="eden_veil_description">实验性设置以提高性能和能力。这些设置可能会导致黑屏或其他游戏问题。</string>

View file

@ -65,6 +65,8 @@
<!-- Eden\'s Veil --> <!-- Eden\'s Veil -->
<string name="veil_extensions">GPU擴充功能</string> <string name="veil_extensions">GPU擴充功能</string>
<string name="veil_renderer">渲染器</string> <string name="veil_renderer">渲染器</string>
<string name="enable_raii">RAII</string>
<string name="enable_raii_description">Vulkan中的一種自動資源管理方法確保在不再需要時正確釋放資源但可能導致捆綁遊戲崩潰。</string>
<string name="veil_misc">CPU與記憶體</string> <string name="veil_misc">CPU與記憶體</string>
<string name="eden_veil">伊甸之幕</string> <string name="eden_veil">伊甸之幕</string>
<string name="eden_veil_description">實驗性設定以提高效能和能力。這些設定可能會導致黑屏或其他遊戲問題。</string> <string name="eden_veil_description">實驗性設定以提高效能和能力。這些設定可能會導致黑屏或其他遊戲問題。</string>

View file

@ -253,16 +253,16 @@
<item>@string/scaling_filter_nearest_neighbor</item> <item>@string/scaling_filter_nearest_neighbor</item>
<item>@string/scaling_filter_bilinear</item> <item>@string/scaling_filter_bilinear</item>
<item>@string/scaling_filter_bicubic</item> <item>@string/scaling_filter_bicubic</item>
<item>@string/scaling_filter_zero_tangent</item>
<item>@string/scaling_filter_bspline</item>
<item>@string/scaling_filter_mitchell</item>
<item>@string/scaling_filter_spline1</item>
<item>@string/scaling_filter_gaussian</item> <item>@string/scaling_filter_gaussian</item>
<item>@string/scaling_filter_lanczos</item> <item>@string/scaling_filter_lanczos</item>
<item>@string/scaling_filter_scale_force</item> <item>@string/scaling_filter_scale_force</item>
<item>@string/scaling_filter_fsr</item> <item>@string/scaling_filter_fsr</item>
<item>@string/scaling_filter_area</item> <item>@string/scaling_filter_area</item>
<item>@string/scaling_filter_mmpx</item> <item>@string/scaling_filter_mmpx</item>
<item>@string/scaling_filter_zero_tangent</item>
<item>@string/scaling_filter_bspline</item>
<item>@string/scaling_filter_mitchell</item>
<item>@string/scaling_filter_spline1</item>
</string-array> </string-array>
<integer-array name="rendererScalingFilterValues"> <integer-array name="rendererScalingFilterValues">

View file

@ -109,6 +109,8 @@
<string name="sample_shading_fraction_description">The intensity of the sample shading pass. Higher values improve quality more but also reduce performance to a greater extent.</string> <string name="sample_shading_fraction_description">The intensity of the sample shading pass. Higher values improve quality more but also reduce performance to a greater extent.</string>
<string name="veil_renderer">Renderer</string> <string name="veil_renderer">Renderer</string>
<string name="enable_raii">RAII</string>
<string name="enable_raii_description">A method of automatic resource management in Vulkan that ensures proper release of resources when they are no longer needed, but may cause crashes in bundled games.</string>
<string name="frame_interpolation">Enhanced Frame Pacing</string> <string name="frame_interpolation">Enhanced Frame Pacing</string>
<string name="frame_interpolation_description">Ensures smooth and consistent frame delivery by synchronizing the timing between frames, reducing stuttering and uneven animation. Ideal for games that experience frame timing instability or micro-stutters during gameplay.</string> <string name="frame_interpolation_description">Ensures smooth and consistent frame delivery by synchronizing the timing between frames, reducing stuttering and uneven animation. Ideal for games that experience frame timing instability or micro-stutters during gameplay.</string>
<string name="renderer_early_release_fences">Release Fences Early</string> <string name="renderer_early_release_fences">Release Fences Early</string>

View file

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -15,6 +12,7 @@
#include "audio_core/adsp/mailbox.h" #include "audio_core/adsp/mailbox.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/polyfill_thread.h" #include "common/polyfill_thread.h"
#include "common/reader_writer_queue.h"
#include "common/thread.h" #include "common/thread.h"
namespace Core { namespace Core {

View file

@ -16,7 +16,7 @@
#include <ranges> #include <ranges>
namespace AudioCore { namespace AudioCore {
constexpr u32 CurrentRevision = 15; constexpr u32 CurrentRevision = 16;
enum class SupportTags { enum class SupportTags {
CommandProcessingTimeEstimatorVersion4, CommandProcessingTimeEstimatorVersion4,
@ -47,10 +47,6 @@ enum class SupportTags {
DelayChannelMappingChange, DelayChannelMappingChange,
ReverbChannelMappingChange, ReverbChannelMappingChange,
I3dl2ReverbChannelMappingChange, I3dl2ReverbChannelMappingChange,
SplitterPrevVolumeReset,
SplitterBiquadFilterParameter,
SplitterDestinationV2b,
VoiceInParameterV2,
// Not a real tag, just here to get the count. // Not a real tag, just here to get the count.
Size Size
@ -95,10 +91,6 @@ constexpr bool CheckFeatureSupported(SupportTags tag, u32 user_revision) {
{SupportTags::DelayChannelMappingChange, 11}, {SupportTags::DelayChannelMappingChange, 11},
{SupportTags::ReverbChannelMappingChange, 11}, {SupportTags::ReverbChannelMappingChange, 11},
{SupportTags::I3dl2ReverbChannelMappingChange, 11}, {SupportTags::I3dl2ReverbChannelMappingChange, 11},
{SupportTags::SplitterBiquadFilterParameter, 12},
{SupportTags::SplitterPrevVolumeReset, 13},
{SupportTags::SplitterDestinationV2b, 15},
{SupportTags::VoiceInParameterV2, 15},
}}; }};
const auto& feature = const auto& feature =

View file

@ -193,20 +193,4 @@ bool BehaviorInfo::IsI3dl2ReverbChannelMappingChanged() const {
return CheckFeatureSupported(SupportTags::I3dl2ReverbChannelMappingChange, user_revision); return CheckFeatureSupported(SupportTags::I3dl2ReverbChannelMappingChange, user_revision);
} }
bool BehaviorInfo::IsSplitterPrevVolumeResetSupported() const {
return CheckFeatureSupported(SupportTags::SplitterPrevVolumeReset, user_revision);
}
bool BehaviorInfo::IsSplitterDestinationV2bSupported() const {
return CheckFeatureSupported(SupportTags::SplitterDestinationV2b, user_revision);
}
bool BehaviorInfo::IsVoiceInParameterV2Supported() const {
return CheckFeatureSupported(SupportTags::VoiceInParameterV2, user_revision);
}
bool BehaviorInfo::IsBiquadFilterParameterForSplitterEnabled() const {
return CheckFeatureSupported(SupportTags::SplitterBiquadFilterParameter, user_revision);
}
} // namespace AudioCore::Renderer } // namespace AudioCore::Renderer

View file

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -364,38 +361,6 @@ public:
*/ */
bool IsI3dl2ReverbChannelMappingChanged() const; bool IsI3dl2ReverbChannelMappingChanged() const;
/**
* Check if explicit previous mix volume reset is supported for splitters.
* This allows splitters to explicitly reset their previous mix volumes instead of
* doing so implicitly on first use.
*
* @return True if supported, otherwise false.
*/
bool IsSplitterPrevVolumeResetSupported() const;
/**
* Check if splitter destination v2b parameter format is supported (revision 15+).
* This uses the extended parameter format with biquad filter fields.
*
* @return True if supported, otherwise false.
*/
bool IsSplitterDestinationV2bSupported() const;
/**
* Check if voice input parameter v2 format is supported (revision 15+).
* This uses the extended parameter format with float biquad filters.
*
* @return True if supported, otherwise false.
*/
bool IsVoiceInParameterV2Supported() const;
/**
* Check if splitter destinations can carry biquad filter parameters (revision 12+).
*
* @return True if supported, otherwise false.
*/
bool IsBiquadFilterParameterForSplitterEnabled() const;
/// Host version /// Host version
u32 process_revision; u32 process_revision;
/// User version /// User version

View file

@ -64,6 +64,8 @@ Result InfoUpdater::UpdateVoices(VoiceContext& voice_context,
const PoolMapper pool_mapper(process_handle, memory_pools, memory_pool_count, const PoolMapper pool_mapper(process_handle, memory_pools, memory_pool_count,
behaviour.IsMemoryForceMappingEnabled()); behaviour.IsMemoryForceMappingEnabled());
const auto voice_count{voice_context.GetCount()}; const auto voice_count{voice_context.GetCount()};
std::span<const VoiceInfo::InParameter> in_params{
reinterpret_cast<const VoiceInfo::InParameter*>(input), voice_count};
std::span<VoiceInfo::OutStatus> out_params{reinterpret_cast<VoiceInfo::OutStatus*>(output), std::span<VoiceInfo::OutStatus> out_params{reinterpret_cast<VoiceInfo::OutStatus*>(output),
voice_count}; voice_count};
@ -74,104 +76,8 @@ Result InfoUpdater::UpdateVoices(VoiceContext& voice_context,
u32 new_voice_count{0}; u32 new_voice_count{0};
// Two input formats exist: legacy (0x170) and v2 with float biquad (0x188).
const bool use_v2 = behaviour.IsVoiceInParameterV2Supported();
const u32 in_stride = use_v2 ? 0x188u : static_cast<u32>(sizeof(VoiceInfo::InParameter));
for (u32 i = 0; i < voice_count; i++) { for (u32 i = 0; i < voice_count; i++) {
VoiceInfo::InParameter local_in{}; const auto& in_param{in_params[i]};
std::array<VoiceInfo::BiquadFilterParameter2, MaxBiquadFilters> float_biquads{};
if (!use_v2) {
const auto* in_param_ptr = reinterpret_cast<const VoiceInfo::InParameter*>(
input + i * sizeof(VoiceInfo::InParameter));
local_in = *in_param_ptr;
} else {
struct VoiceInParameterV2 {
u32 id;
u32 node_id;
bool is_new;
bool in_use;
PlayState play_state;
SampleFormat sample_format;
u32 sample_rate;
u32 priority;
u32 sort_order;
u32 channel_count;
f32 pitch;
f32 volume;
// Two BiquadFilterParameter2 (0x18 each) -> ignored/converted
struct BiquadV2 {
bool enable;
u8 r1;
u8 r2;
u8 r3;
std::array<f32, 3> b;
std::array<f32, 2> a;
} biquads[2];
u32 wave_buffer_count;
u32 wave_buffer_index;
u32 reserved1;
u64 src_data_address;
u64 src_data_size;
s32 mix_id;
u32 splitter_id;
std::array<VoiceInfo::WaveBufferInternal, MaxWaveBuffers> wavebuffers;
std::array<u32, MaxChannels> channel_resource_ids;
bool clear_voice_drop;
u8 flush_wave_buffer_count;
u16 reserved2;
VoiceInfo::Flags flags;
SrcQuality src_quality;
u32 external_ctx;
u32 external_ctx_size;
u32 reserved3[2];
};
const auto* vin = reinterpret_cast<const VoiceInParameterV2*>(input + i * in_stride);
local_in.id = vin->id;
local_in.node_id = vin->node_id;
local_in.is_new = vin->is_new;
local_in.in_use = vin->in_use;
local_in.play_state = vin->play_state;
local_in.sample_format = vin->sample_format;
local_in.sample_rate = vin->sample_rate;
local_in.priority = static_cast<s32>(vin->priority);
local_in.sort_order = static_cast<s32>(vin->sort_order);
local_in.channel_count = vin->channel_count;
local_in.pitch = vin->pitch;
local_in.volume = vin->volume;
// For REV15+, we keep float coefficients separate and only convert for compatibility
for (size_t filter_idx = 0; filter_idx < MaxBiquadFilters; filter_idx++) {
const auto& src = vin->biquads[filter_idx];
auto& dst = local_in.biquads[filter_idx];
dst.enabled = src.enable;
// Convert float coefficients to fixed-point Q2.14 for legacy path
dst.b[0] = static_cast<s16>(std::clamp(src.b[0] * 16384.0f, -32768.0f, 32767.0f));
dst.b[1] = static_cast<s16>(std::clamp(src.b[1] * 16384.0f, -32768.0f, 32767.0f));
dst.b[2] = static_cast<s16>(std::clamp(src.b[2] * 16384.0f, -32768.0f, 32767.0f));
dst.a[0] = static_cast<s16>(std::clamp(src.a[0] * 16384.0f, -32768.0f, 32767.0f));
dst.a[1] = static_cast<s16>(std::clamp(src.a[1] * 16384.0f, -32768.0f, 32767.0f));
// Also store the native float version
float_biquads[filter_idx].enabled = src.enable;
float_biquads[filter_idx].numerator = src.b;
float_biquads[filter_idx].denominator = src.a;
}
local_in.wave_buffer_count = vin->wave_buffer_count;
local_in.wave_buffer_index = static_cast<u16>(vin->wave_buffer_index);
local_in.src_data_address = static_cast<CpuAddr>(vin->src_data_address);
local_in.src_data_size = vin->src_data_size;
local_in.mix_id = static_cast<u32>(vin->mix_id);
local_in.splitter_id = vin->splitter_id;
local_in.wave_buffer_internal = vin->wavebuffers;
local_in.channel_resource_ids = vin->channel_resource_ids;
local_in.clear_voice_drop = vin->clear_voice_drop;
local_in.flush_buffer_count = vin->flush_wave_buffer_count;
local_in.flags = vin->flags;
local_in.src_quality = vin->src_quality;
}
const auto& in_param = local_in;
std::array<VoiceState*, MaxChannels> voice_states{}; std::array<VoiceState*, MaxChannels> voice_states{};
if (!in_param.in_use) { if (!in_param.in_use) {
@ -195,14 +101,6 @@ Result InfoUpdater::UpdateVoices(VoiceContext& voice_context,
BehaviorInfo::ErrorInfo update_error{}; BehaviorInfo::ErrorInfo update_error{};
voice_info.UpdateParameters(update_error, in_param, pool_mapper, behaviour); voice_info.UpdateParameters(update_error, in_param, pool_mapper, behaviour);
// For REV15+, store the native float biquad coefficients
if (use_v2) {
voice_info.use_float_biquads = true;
voice_info.biquads_float = float_biquads;
} else {
voice_info.use_float_biquads = false;
}
if (!update_error.error_code.IsSuccess()) { if (!update_error.error_code.IsSuccess()) {
behaviour.AppendError(update_error); behaviour.AppendError(update_error);
} }
@ -223,7 +121,7 @@ Result InfoUpdater::UpdateVoices(VoiceContext& voice_context,
new_voice_count += in_param.channel_count; new_voice_count += in_param.channel_count;
} }
auto consumed_input_size{voice_count * in_stride}; auto consumed_input_size{voice_count * static_cast<u32>(sizeof(VoiceInfo::InParameter))};
auto consumed_output_size{voice_count * static_cast<u32>(sizeof(VoiceInfo::OutStatus))}; auto consumed_output_size{voice_count * static_cast<u32>(sizeof(VoiceInfo::OutStatus))};
if (consumed_input_size != in_header->voices_size) { if (consumed_input_size != in_header->voices_size) {
LOG_ERROR(Service_Audio, "Consumed an incorrect voices size, header size={}, consumed={}", LOG_ERROR(Service_Audio, "Consumed an incorrect voices size, header size={}, consumed={}",
@ -359,31 +257,18 @@ Result InfoUpdater::UpdateMixes(MixContext& mix_context, const u32 mix_buffer_co
EffectContext& effect_context, SplitterContext& splitter_context) { EffectContext& effect_context, SplitterContext& splitter_context) {
s32 mix_count{0}; s32 mix_count{0};
u32 consumed_input_size{0}; u32 consumed_input_size{0};
u32 input_mix_size{0};
if (behaviour.IsMixInParameterDirtyOnlyUpdateSupported()) { if (behaviour.IsMixInParameterDirtyOnlyUpdateSupported()) {
auto in_dirty_params{reinterpret_cast<const MixInfo::InDirtyParameter*>(input)}; auto in_dirty_params{reinterpret_cast<const MixInfo::InDirtyParameter*>(input)};
mix_count = in_dirty_params->count; mix_count = in_dirty_params->count;
// Validate against expected header size to ensure structure is correct
if (mix_count < 0 || mix_count > 0x100) {
LOG_ERROR(
Service_Audio,
"Invalid mix count from dirty parameter: count={}, magic=0x{:X}, expected_size={}",
mix_count, in_dirty_params->magic, in_header->mix_size);
return Service::Audio::ResultInvalidUpdateInfo;
}
consumed_input_size += static_cast<u32>(sizeof(MixInfo::InDirtyParameter));
input += sizeof(MixInfo::InDirtyParameter); input += sizeof(MixInfo::InDirtyParameter);
consumed_input_size = static_cast<u32>(sizeof(MixInfo::InDirtyParameter) +
mix_count * sizeof(MixInfo::InParameter));
} else { } else {
mix_count = mix_context.GetCount(); mix_count = mix_context.GetCount();
consumed_input_size = static_cast<u32>(mix_count * sizeof(MixInfo::InParameter));
} }
input_mix_size = static_cast<u32>(mix_count * sizeof(MixInfo::InParameter));
consumed_input_size += input_mix_size;
if (mix_buffer_count == 0) { if (mix_buffer_count == 0) {
return Service::Audio::ResultInvalidUpdateInfo; return Service::Audio::ResultInvalidUpdateInfo;
} }

View file

@ -237,13 +237,6 @@ void CommandBuffer::GenerateBiquadFilterCommand(const s32 node_id, VoiceInfo& vo
cmd.biquad = voice_info.biquads[biquad_index]; cmd.biquad = voice_info.biquads[biquad_index];
if (voice_info.use_float_biquads) {
cmd.biquad_float = voice_info.biquads_float[biquad_index];
cmd.use_float_coefficients = true;
} else {
cmd.use_float_coefficients = false;
}
cmd.state = memory_pool->Translate(CpuAddr(voice_state.biquad_states[biquad_index].data()), cmd.state = memory_pool->Translate(CpuAddr(voice_state.biquad_states[biquad_index].data()),
MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState)); MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState));
@ -270,9 +263,6 @@ void CommandBuffer::GenerateBiquadFilterCommand(const s32 node_id, EffectInfoBas
cmd.biquad.b = parameter.b; cmd.biquad.b = parameter.b;
cmd.biquad.a = parameter.a; cmd.biquad.a = parameter.a;
// Effects use legacy fixed-point format
cmd.use_float_coefficients = false;
cmd.state = memory_pool->Translate(CpuAddr(state), cmd.state = memory_pool->Translate(CpuAddr(state),
MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState)); MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState));
@ -668,13 +658,6 @@ void CommandBuffer::GenerateMultitapBiquadFilterCommand(const s32 node_id, Voice
cmd.output = buffer_count + channel; cmd.output = buffer_count + channel;
cmd.biquads = voice_info.biquads; cmd.biquads = voice_info.biquads;
if (voice_info.use_float_biquads) {
cmd.biquads_float = voice_info.biquads_float;
cmd.use_float_coefficients = true;
} else {
cmd.use_float_coefficients = false;
}
cmd.states[0] = cmd.states[0] =
memory_pool->Translate(CpuAddr(voice_state.biquad_states[0].data()), memory_pool->Translate(CpuAddr(voice_state.biquad_states[0].data()),
MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState)); MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState));

View file

@ -51,40 +51,6 @@ void ApplyBiquadFilterFloat(std::span<s32> output, std::span<const s32> input,
state.s3 = Common::BitCast<s64>(s[3]); state.s3 = Common::BitCast<s64>(s[3]);
} }
/**
* Biquad filter float implementation with native float coefficients.
*/
void ApplyBiquadFilterFloat2(std::span<s32> output, std::span<const s32> input,
std::array<f32, 3>& b, std::array<f32, 2>& a,
VoiceState::BiquadFilterState& state, const u32 sample_count) {
constexpr f64 min{std::numeric_limits<s32>::min()};
constexpr f64 max{std::numeric_limits<s32>::max()};
std::array<f64, 3> b_double{static_cast<f64>(b[0]), static_cast<f64>(b[1]),
static_cast<f64>(b[2])};
std::array<f64, 2> a_double{static_cast<f64>(a[0]), static_cast<f64>(a[1])};
std::array<f64, 4> s{Common::BitCast<f64>(state.s0), Common::BitCast<f64>(state.s1),
Common::BitCast<f64>(state.s2), Common::BitCast<f64>(state.s3)};
for (u32 i = 0; i < sample_count; i++) {
f64 in_sample{static_cast<f64>(input[i])};
auto sample{in_sample * b_double[0] + s[0] * b_double[1] + s[1] * b_double[2] +
s[2] * a_double[0] + s[3] * a_double[1]};
output[i] = static_cast<s32>(std::clamp(sample, min, max));
s[1] = s[0];
s[0] = in_sample;
s[3] = s[2];
s[2] = sample;
}
state.s0 = Common::BitCast<s64>(s[0]);
state.s1 = Common::BitCast<s64>(s[1]);
state.s2 = Common::BitCast<s64>(s[2]);
state.s3 = Common::BitCast<s64>(s[3]);
}
/** /**
* Biquad filter s32 implementation. * Biquad filter s32 implementation.
* *
@ -132,14 +98,8 @@ void BiquadFilterCommand::Process(const AudioRenderer::CommandListProcessor& pro
processor.mix_buffers.subspan(output * processor.sample_count, processor.sample_count)}; processor.mix_buffers.subspan(output * processor.sample_count, processor.sample_count)};
if (use_float_processing) { if (use_float_processing) {
// REV15+: Use native float coefficients if available
if (use_float_coefficients) {
ApplyBiquadFilterFloat2(output_buffer, input_buffer, biquad_float.numerator,
biquad_float.denominator, *state_, processor.sample_count);
} else {
ApplyBiquadFilterFloat(output_buffer, input_buffer, biquad.b, biquad.a, *state_, ApplyBiquadFilterFloat(output_buffer, input_buffer, biquad.b, biquad.a, *state_,
processor.sample_count); processor.sample_count);
}
} else { } else {
ApplyBiquadFilterInt(output_buffer, input_buffer, biquad.b, biquad.a, *state_, ApplyBiquadFilterInt(output_buffer, input_buffer, biquad.b, biquad.a, *state_,
processor.sample_count); processor.sample_count);

View file

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -53,16 +50,12 @@ struct BiquadFilterCommand : ICommand {
s16 output; s16 output;
/// Input parameters for biquad /// Input parameters for biquad
VoiceInfo::BiquadFilterParameter biquad; VoiceInfo::BiquadFilterParameter biquad;
/// Input parameters for biquad (REV15+ native float)
VoiceInfo::BiquadFilterParameter2 biquad_float;
/// Biquad state, updated each call /// Biquad state, updated each call
CpuAddr state; CpuAddr state;
/// If true, reset the state /// If true, reset the state
bool needs_init; bool needs_init;
/// If true, use float processing rather than int /// If true, use float processing rather than int
bool use_float_processing; bool use_float_processing;
/// If true, use native float coefficients (REV15+)
bool use_float_coefficients;
}; };
/** /**
@ -79,18 +72,4 @@ void ApplyBiquadFilterFloat(std::span<s32> output, std::span<const s32> input,
std::array<s16, 3>& b, std::array<s16, 2>& a, std::array<s16, 3>& b, std::array<s16, 2>& a,
VoiceState::BiquadFilterState& state, const u32 sample_count); VoiceState::BiquadFilterState& state, const u32 sample_count);
/**
* Biquad filter float implementation with native float coefficients (SDK REV15+).
*
* @param output - Output container for filtered samples.
* @param input - Input container for samples to be filtered.
* @param b - Feedforward coefficients (float).
* @param a - Feedback coefficients (float).
* @param state - State to track previous samples.
* @param sample_count - Number of samples to process.
*/
void ApplyBiquadFilterFloat2(std::span<s32> output, std::span<const s32> input,
std::array<f32, 3>& b, std::array<f32, 2>& a,
VoiceState::BiquadFilterState& state, const u32 sample_count);
} // namespace AudioCore::Renderer } // namespace AudioCore::Renderer

View file

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -36,15 +33,9 @@ void MultiTapBiquadFilterCommand::Process(const AudioRenderer::CommandListProces
*state = {}; *state = {};
} }
// REV15+: Use native float coefficients if available
if (use_float_coefficients) {
ApplyBiquadFilterFloat2(output_buffer, input_buffer, biquads_float[i].numerator,
biquads_float[i].denominator, *state, processor.sample_count);
} else {
ApplyBiquadFilterFloat(output_buffer, input_buffer, biquads[i].b, biquads[i].a, *state, ApplyBiquadFilterFloat(output_buffer, input_buffer, biquads[i].b, biquads[i].a, *state,
processor.sample_count); processor.sample_count);
} }
}
} }
bool MultiTapBiquadFilterCommand::Verify(const AudioRenderer::CommandListProcessor& processor) { bool MultiTapBiquadFilterCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {

View file

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -52,16 +49,12 @@ struct MultiTapBiquadFilterCommand : ICommand {
s16 output; s16 output;
/// Biquad parameters /// Biquad parameters
std::array<VoiceInfo::BiquadFilterParameter, MaxBiquadFilters> biquads; std::array<VoiceInfo::BiquadFilterParameter, MaxBiquadFilters> biquads;
/// Biquad parameters (REV15+ native float)
std::array<VoiceInfo::BiquadFilterParameter2, MaxBiquadFilters> biquads_float;
/// Biquad states, updated each call /// Biquad states, updated each call
std::array<CpuAddr, MaxBiquadFilters> states; std::array<CpuAddr, MaxBiquadFilters> states;
/// If each biquad needs initialisation /// If each biquad needs initialisation
std::array<bool, MaxBiquadFilters> needs_init; std::array<bool, MaxBiquadFilters> needs_init;
/// Number of active biquads /// Number of active biquads
u8 filter_tap_count; u8 filter_tap_count;
/// If true, use native float coefficients (REV15+)
bool use_float_coefficients;
}; };
} // namespace AudioCore::Renderer } // namespace AudioCore::Renderer

View file

@ -35,16 +35,12 @@ SplitterDestinationData& SplitterContext::GetData(const u32 index) {
void SplitterContext::Setup(std::span<SplitterInfo> splitter_infos_, const u32 splitter_info_count_, void SplitterContext::Setup(std::span<SplitterInfo> splitter_infos_, const u32 splitter_info_count_,
SplitterDestinationData* splitter_destinations_, SplitterDestinationData* splitter_destinations_,
const u32 destination_count_, const bool splitter_bug_fixed_, const u32 destination_count_, const bool splitter_bug_fixed_) {
const BehaviorInfo& behavior) {
splitter_infos = splitter_infos_; splitter_infos = splitter_infos_;
info_count = splitter_info_count_; info_count = splitter_info_count_;
splitter_destinations = splitter_destinations_; splitter_destinations = splitter_destinations_;
destinations_count = destination_count_; destinations_count = destination_count_;
splitter_bug_fixed = splitter_bug_fixed_; splitter_bug_fixed = splitter_bug_fixed_;
splitter_prev_volume_reset_supported = behavior.IsSplitterPrevVolumeResetSupported();
splitter_biquad_param_supported = behavior.IsBiquadFilterParameterForSplitterEnabled();
splitter_float_coeff_supported = behavior.IsSplitterDestinationV2bSupported();
} }
bool SplitterContext::UsingSplitter() const { bool SplitterContext::UsingSplitter() const {
@ -88,7 +84,7 @@ bool SplitterContext::Initialize(const BehaviorInfo& behavior,
} }
Setup(splitter_infos, params.splitter_infos, splitter_destinations, Setup(splitter_infos, params.splitter_infos, splitter_destinations,
params.splitter_destinations, behavior.IsSplitterBugFixed(), behavior); params.splitter_destinations, behavior.IsSplitterBugFixed());
} }
return true; return true;
} }
@ -141,104 +137,19 @@ u32 SplitterContext::UpdateInfo(const u8* input, u32 offset, const u32 splitter_
u32 SplitterContext::UpdateData(const u8* input, u32 offset, const u32 count) { u32 SplitterContext::UpdateData(const u8* input, u32 offset, const u32 count) {
for (u32 i = 0; i < count; i++) { for (u32 i = 0; i < count; i++) {
// Version selection based on feature flags: auto data_header{
// - REV12: integer biquad params (Version2a) reinterpret_cast<const SplitterDestinationData::InParameter*>(input + offset)};
// - REV15: float coeff/biquad v2b
// - older: no biquad fields
if (!splitter_biquad_param_supported) {
const auto* data_header =
reinterpret_cast<const SplitterDestinationData::InParameter*>(input + offset);
if (data_header->magic != GetSplitterSendDataMagic()) { if (data_header->magic != GetSplitterSendDataMagic()) {
continue; continue;
} }
if (data_header->id < 0 || data_header->id > destinations_count) { if (data_header->id < 0 || data_header->id > destinations_count) {
continue; continue;
} }
auto modified_params = *data_header; splitter_destinations[data_header->id].Update(*data_header);
if (!splitter_prev_volume_reset_supported) {
modified_params.reset_prev_volume = false;
}
splitter_destinations[data_header->id].Update(modified_params);
offset += sizeof(SplitterDestinationData::InParameter); offset += sizeof(SplitterDestinationData::InParameter);
} else if (!splitter_float_coeff_supported) {
// Version 2a: struct contains legacy fixed-point biquad filter fields (REV12+)
const auto* data_header_v2a =
reinterpret_cast<const SplitterDestinationData::InParameterVersion2a*>(input +
offset);
if (data_header_v2a->magic != GetSplitterSendDataMagic()) {
continue;
}
if (data_header_v2a->id < 0 || data_header_v2a->id > destinations_count) {
continue;
}
// Map common fields to the base format
SplitterDestinationData::InParameter mapped{};
mapped.magic = data_header_v2a->magic;
mapped.id = data_header_v2a->id;
mapped.mix_volumes = data_header_v2a->mix_volumes;
mapped.mix_id = data_header_v2a->mix_id;
mapped.in_use = data_header_v2a->in_use;
mapped.reset_prev_volume =
splitter_prev_volume_reset_supported ? data_header_v2a->reset_prev_volume : false;
auto& destination = splitter_destinations[data_header_v2a->id];
destination.Update(mapped);
// Convert legacy fixed-point biquad params into float representation
auto biquad_filters = destination.GetBiquadFilters();
for (size_t filter_idx = 0; filter_idx < MaxBiquadFilters; filter_idx++) {
const auto& legacy = data_header_v2a->biquad_filters[filter_idx];
auto& out = biquad_filters[filter_idx];
out.enabled = legacy.enabled;
// s16 fixed-point scale: use Q14 like voices (b and a are s16, 1.0 ~= 1<<14)
constexpr float scale = 1.0f / static_cast<float>(1 << 14);
out.numerator[0] = static_cast<float>(legacy.b[0]) * scale;
out.numerator[1] = static_cast<float>(legacy.b[1]) * scale;
out.numerator[2] = static_cast<float>(legacy.b[2]) * scale;
out.denominator[0] = static_cast<float>(legacy.a[0]) * scale;
out.denominator[1] = static_cast<float>(legacy.a[1]) * scale;
}
offset += static_cast<u32>(sizeof(SplitterDestinationData::InParameterVersion2a));
} else {
// Version 2b: struct contains extra biquad filter fields with float coeffs
const auto* data_header_v2b =
reinterpret_cast<const SplitterDestinationData::InParameterVersion2b*>(input +
offset);
if (data_header_v2b->magic != GetSplitterSendDataMagic()) {
continue;
}
if (data_header_v2b->id < 0 || data_header_v2b->id > destinations_count) {
continue;
}
// Map common fields to the old format
SplitterDestinationData::InParameter mapped{};
mapped.magic = data_header_v2b->magic;
mapped.id = data_header_v2b->id;
mapped.mix_volumes = data_header_v2b->mix_volumes;
mapped.mix_id = data_header_v2b->mix_id;
mapped.in_use = data_header_v2b->in_use;
mapped.reset_prev_volume =
splitter_prev_volume_reset_supported ? data_header_v2b->reset_prev_volume : false;
// Store biquad filters from V2b (REV15+)
auto& destination = splitter_destinations[data_header_v2b->id];
destination.Update(mapped);
// Copy biquad filter parameters
auto biquad_filters = destination.GetBiquadFilters();
for (size_t filter_idx = 0; filter_idx < MaxBiquadFilters; filter_idx++) {
biquad_filters[filter_idx] = data_header_v2b->biquad_filters[filter_idx];
}
offset += static_cast<u32>(sizeof(SplitterDestinationData::InParameterVersion2b));
}
} }
return offset; return offset;

View file

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -171,11 +168,10 @@ private:
* @param splitter_destinations - Workbuffer for splitter destinations. * @param splitter_destinations - Workbuffer for splitter destinations.
* @param destination_count - Number of destinations in the workbuffer. * @param destination_count - Number of destinations in the workbuffer.
* @param splitter_bug_fixed - Is the splitter bug fixed? * @param splitter_bug_fixed - Is the splitter bug fixed?
* @param behavior - Behavior info for feature support.
*/ */
void Setup(std::span<SplitterInfo> splitter_infos, u32 splitter_info_count, void Setup(std::span<SplitterInfo> splitter_infos, u32 splitter_info_count,
SplitterDestinationData* splitter_destinations, u32 destination_count, SplitterDestinationData* splitter_destinations, u32 destination_count,
bool splitter_bug_fixed, const BehaviorInfo& behavior); bool splitter_bug_fixed);
/// Workbuffer for splitters /// Workbuffer for splitters
std::span<SplitterInfo> splitter_infos{}; std::span<SplitterInfo> splitter_infos{};
@ -187,12 +183,6 @@ private:
s32 destinations_count{}; s32 destinations_count{};
/// Is the splitter bug fixed? /// Is the splitter bug fixed?
bool splitter_bug_fixed{}; bool splitter_bug_fixed{};
/// Is explicit previous mix volume reset supported?
bool splitter_prev_volume_reset_supported{};
/// Is biquad filter parameter for splitter (REV12) supported?
bool splitter_biquad_param_supported{};
/// Is float coefficient/biquad filter v2b parameter supported?
bool splitter_float_coeff_supported{};
}; };
} // namespace Renderer } // namespace Renderer

View file

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -87,14 +84,4 @@ void SplitterDestinationData::SetNext(SplitterDestinationData* next_) {
next = next_; next = next_;
} }
std::span<SplitterDestinationData::BiquadFilterParameter2>
SplitterDestinationData::GetBiquadFilters() {
return biquad_filters;
}
std::span<const SplitterDestinationData::BiquadFilterParameter2>
SplitterDestinationData::GetBiquadFilters() const {
return biquad_filters;
}
} // namespace AudioCore::Renderer } // namespace AudioCore::Renderer

View file

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -19,72 +16,16 @@ namespace AudioCore::Renderer {
*/ */
class SplitterDestinationData { class SplitterDestinationData {
public: public:
/**
* Biquad filter parameter with float coefficients (SDK REV15+).
* Defined here to avoid circular dependency with VoiceInfo.
*/
struct BiquadFilterParameter2 {
/* 0x00 */ bool enabled;
/* 0x01 */ u8 reserved1;
/* 0x02 */ u8 reserved2;
/* 0x03 */ u8 reserved3;
/* 0x04 */ std::array<f32, 3> numerator; // b0, b1, b2
/* 0x10 */ std::array<f32, 2> denominator; // a1, a2 (a0 = 1)
};
static_assert(sizeof(BiquadFilterParameter2) == 0x18,
"BiquadFilterParameter2 has the wrong size!");
/**
* Legacy biquad filter parameter with fixed-point coefficients (SDK REV12+ for splitters).
* Matches the old voice biquad format.
*/
struct BiquadFilterParameterLegacy {
/* 0x00 */ bool enabled;
/* 0x02 */ std::array<s16, 3> b; // numerator
/* 0x08 */ std::array<s16, 2> a; // denominator (a0 = 1)
};
static_assert(sizeof(BiquadFilterParameterLegacy) == 0xC,
"BiquadFilterParameterLegacy has the wrong size!");
struct InParameter { struct InParameter {
/* 0x00 */ u32 magic; // 'SNDD' /* 0x00 */ u32 magic; // 'SNDD'
/* 0x04 */ s32 id; /* 0x04 */ s32 id;
/* 0x08 */ std::array<f32, MaxMixBuffers> mix_volumes; /* 0x08 */ std::array<f32, MaxMixBuffers> mix_volumes;
/* 0x68 */ u32 mix_id; /* 0x68 */ u32 mix_id;
/* 0x6C */ bool in_use; /* 0x6C */ bool in_use;
/* 0x6D */ bool reset_prev_volume;
}; };
static_assert(sizeof(InParameter) == 0x70, static_assert(sizeof(InParameter) == 0x70,
"SplitterDestinationData::InParameter has the wrong size!"); "SplitterDestinationData::InParameter has the wrong size!");
struct InParameterVersion2a {
/* 0x00 */ u32 magic; // 'SNDD'
/* 0x04 */ s32 id;
/* 0x08 */ std::array<f32, MaxMixBuffers> mix_volumes;
/* 0x68 */ u32 mix_id;
/* 0x6C */ std::array<SplitterDestinationData::BiquadFilterParameterLegacy, MaxBiquadFilters>
biquad_filters;
/* 0x84 */ bool in_use;
/* 0x85 */ bool reset_prev_volume; // only effective if supported
/* 0x86 */ u8 reserved[10];
};
static_assert(sizeof(InParameterVersion2a) == 0x90,
"SplitterDestinationData::InParameterVersion2a has the wrong size!");
struct InParameterVersion2b {
/* 0x00 */ u32 magic; // 'SNDD'
/* 0x04 */ s32 id;
/* 0x08 */ std::array<f32, MaxMixBuffers> mix_volumes;
/* 0x68 */ u32 mix_id;
/* 0x6C */ std::array<SplitterDestinationData::BiquadFilterParameter2, MaxBiquadFilters>
biquad_filters;
/* 0x9C */ bool in_use;
/* 0x9D */ bool reset_prev_volume;
/* 0x9E */ u8 reserved[10];
};
static_assert(sizeof(InParameterVersion2b) == 0xA8,
"SplitterDestinationData::InParameterVersion2b has the wrong size!");
SplitterDestinationData(s32 id); SplitterDestinationData(s32 id);
/** /**
@ -137,7 +78,7 @@ public:
f32 GetMixVolumePrev(u32 index) const; f32 GetMixVolumePrev(u32 index) const;
/** /**
* Get the previous mix volumes for all mix buffers. * Get the previous mix volumes for all mix buffers in this destination.
* *
* @return Span of previous mix buffer volumes. * @return Span of previous mix buffer volumes.
*/ */
@ -174,20 +115,6 @@ public:
*/ */
void SetNext(SplitterDestinationData* next); void SetNext(SplitterDestinationData* next);
/**
* Get biquad filter parameters for this destination (REV15+ or mapped from REV12).
*
* @return Span of biquad filter parameters.
*/
std::span<BiquadFilterParameter2> GetBiquadFilters();
/**
* Get const biquad filter parameters for this destination (REV15+ or mapped from REV12).
*
* @return Const span of biquad filter parameters.
*/
std::span<const BiquadFilterParameter2> GetBiquadFilters() const;
private: private:
/// Id of this destination /// Id of this destination
const s32 id; const s32 id;
@ -197,8 +124,6 @@ private:
std::array<f32, MaxMixBuffers> mix_volumes{0.0f}; std::array<f32, MaxMixBuffers> mix_volumes{0.0f};
/// Previous mix volumes /// Previous mix volumes
std::array<f32, MaxMixBuffers> prev_mix_volumes{0.0f}; std::array<f32, MaxMixBuffers> prev_mix_volumes{0.0f};
/// Biquad filter parameters (REV15+ or mapped from REV12)
std::array<BiquadFilterParameter2, MaxBiquadFilters> biquad_filters{};
/// Next destination in the mix chain /// Next destination in the mix chain
SplitterDestinationData* next{}; SplitterDestinationData* next{};
/// Is this destination in use? /// Is this destination in use?

View file

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -138,17 +135,6 @@ public:
static_assert(sizeof(BiquadFilterParameter) == 0xC, static_assert(sizeof(BiquadFilterParameter) == 0xC,
"VoiceInfo::BiquadFilterParameter has the wrong size!"); "VoiceInfo::BiquadFilterParameter has the wrong size!");
struct BiquadFilterParameter2 {
/* 0x00 */ bool enabled;
/* 0x01 */ u8 reserved1;
/* 0x02 */ u8 reserved2;
/* 0x03 */ u8 reserved3;
/* 0x04 */ std::array<f32, 3> numerator; // b0, b1, b2
/* 0x10 */ std::array<f32, 2> denominator; // a1, a2 (a0 = 1)
};
static_assert(sizeof(BiquadFilterParameter2) == 0x18,
"VoiceInfo::BiquadFilterParameter2 has the wrong size!");
struct InParameter { struct InParameter {
/* 0x000 */ u32 id; /* 0x000 */ u32 id;
/* 0x004 */ u32 node_id; /* 0x004 */ u32 node_id;
@ -182,43 +168,6 @@ public:
}; };
static_assert(sizeof(InParameter) == 0x170, "VoiceInfo::InParameter has the wrong size!"); static_assert(sizeof(InParameter) == 0x170, "VoiceInfo::InParameter has the wrong size!");
struct InParameter2 {
/* 0x000 */ u32 id;
/* 0x004 */ u32 node_id;
/* 0x008 */ bool is_new;
/* 0x009 */ bool in_use;
/* 0x00A */ PlayState play_state;
/* 0x00B */ SampleFormat sample_format;
/* 0x00C */ u32 sample_rate;
/* 0x010 */ s32 priority;
/* 0x014 */ s32 sort_order;
/* 0x018 */ u32 channel_count;
/* 0x01C */ f32 pitch;
/* 0x020 */ f32 volume;
/* 0x024 */ std::array<BiquadFilterParameter2, MaxBiquadFilters> biquads;
/* 0x054 */ u32 wave_buffer_count;
/* 0x058 */ u32 wave_buffer_index;
/* 0x05C */ u32 reserved1;
/* 0x060 */ CpuAddr src_data_address;
/* 0x068 */ u64 src_data_size;
/* 0x070 */ u32 mix_id;
/* 0x074 */ u32 splitter_id;
/* 0x078 */ std::array<WaveBufferInternal, MaxWaveBuffers> wave_buffer_internal;
/* 0x158 */ std::array<s32, MaxChannels> channel_resource_ids;
/* 0x170 */ bool clear_voice_drop;
/* 0x171 */ u8 flush_buffer_count;
/* 0x172 */ u16 reserved2;
/* 0x174 */ Flags flags;
/* 0x175 */ u8 reserved3;
/* 0x176 */ SrcQuality src_quality;
/* 0x177 */ u8 reserved4;
/* 0x178 */ u32 external_context;
/* 0x17C */ u32 external_context_size;
/* 0x180 */ u32 reserved5;
/* 0x184 */ u32 reserved6;
};
static_assert(sizeof(InParameter2) == 0x188, "VoiceInfo::InParameter2 has the wrong size!");
struct OutStatus { struct OutStatus {
/* 0x00 */ u64 played_sample_count; /* 0x00 */ u64 played_sample_count;
/* 0x08 */ u32 wave_buffers_consumed; /* 0x08 */ u32 wave_buffers_consumed;
@ -400,10 +349,6 @@ public:
f32 prev_volume{}; f32 prev_volume{};
/// Biquad filters for generating filter commands on this voice /// Biquad filters for generating filter commands on this voice
std::array<BiquadFilterParameter, MaxBiquadFilters> biquads{}; std::array<BiquadFilterParameter, MaxBiquadFilters> biquads{};
/// Float biquad filters for REV15+ (native float coefficients)
std::array<BiquadFilterParameter2, MaxBiquadFilters> biquads_float{};
/// Use float biquad coefficients (REV15+)
bool use_float_biquads{};
/// Number of active wavebuffers /// Number of active wavebuffers
u32 wave_buffer_count{}; u32 wave_buffer_count{};
/// Current playing wavebuffer index /// Current playing wavebuffer index

View file

@ -23,7 +23,7 @@ namespace AudioCore::Sink {
void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) { void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) {
SCOPE_EXIT { SCOPE_EXIT {
queue.EmplaceWait(buffer); queue.enqueue(buffer);
++queued_buffers; ++queued_buffers;
}; };
@ -147,8 +147,7 @@ std::vector<s16> SinkStream::ReleaseBuffer(u64 num_samples) {
void SinkStream::ClearQueue() { void SinkStream::ClearQueue() {
samples_buffer.Pop(); samples_buffer.Pop();
SinkBuffer tmp; while (queue.pop()) {
while (queue.TryPop(tmp)) {
} }
queued_buffers = 0; queued_buffers = 0;
playing_buffer = {}; playing_buffer = {};
@ -170,7 +169,7 @@ void SinkStream::ProcessAudioIn(std::span<const s16> input_buffer, std::size_t n
while (frames_written < num_frames) { while (frames_written < num_frames) {
// If the playing buffer has been consumed or has no frames, we need a new one // If the playing buffer has been consumed or has no frames, we need a new one
if (playing_buffer.consumed || playing_buffer.frames == 0) { if (playing_buffer.consumed || playing_buffer.frames == 0) {
if (!queue.TryPop(playing_buffer)) { if (!queue.try_dequeue(playing_buffer)) {
// If no buffer was available we've underrun, just push the samples and // If no buffer was available we've underrun, just push the samples and
// continue. // continue.
samples_buffer.Push(&input_buffer[frames_written * frame_size], samples_buffer.Push(&input_buffer[frames_written * frame_size],
@ -231,7 +230,7 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
while (frames_written < num_frames) { while (frames_written < num_frames) {
// If the playing buffer has been consumed or has no frames, we need a new one // If the playing buffer has been consumed or has no frames, we need a new one
if (playing_buffer.consumed || playing_buffer.frames == 0) { if (playing_buffer.consumed || playing_buffer.frames == 0) {
if (!queue.TryPop(playing_buffer)) { if (!queue.try_dequeue(playing_buffer)) {
// If no buffer was available we've underrun, fill the remaining buffer with // If no buffer was available we've underrun, fill the remaining buffer with
// the last written frame and continue. // the last written frame and continue.
for (size_t i = frames_written; i < num_frames; i++) { for (size_t i = frames_written; i < num_frames; i++) {

View file

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -17,8 +14,8 @@
#include "audio_core/common/common.h" #include "audio_core/common/common.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/polyfill_thread.h" #include "common/polyfill_thread.h"
#include "common/reader_writer_queue.h"
#include "common/ring_buffer.h" #include "common/ring_buffer.h"
#include "common/bounded_threadsafe_queue.h"
#include "common/thread.h" #include "common/thread.h"
namespace Core { namespace Core {
@ -240,7 +237,7 @@ private:
/// Ring buffer of the samples waiting to be played or consumed /// Ring buffer of the samples waiting to be played or consumed
Common::RingBuffer<s16, 0x10000> samples_buffer; Common::RingBuffer<s16, 0x10000> samples_buffer;
/// Audio buffers queued and waiting to play /// Audio buffers queued and waiting to play
Common::SPSCQueue<SinkBuffer, 0x10000> queue; Common::ReaderWriterQueue<SinkBuffer> queue;
/// The currently-playing audio buffer /// The currently-playing audio buffer
SinkBuffer playing_buffer{}; SinkBuffer playing_buffer{};
/// The last played (or received) frame of audio, used when the callback underruns /// The last played (or received) frame of audio, used when the callback underruns

View file

@ -109,6 +109,7 @@ add_library(
range_mutex.h range_mutex.h
range_sets.h range_sets.h
range_sets.inc range_sets.inc
reader_writer_queue.h
ring_buffer.h ring_buffer.h
${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp
scm_rev.h scm_rev.h

View file

@ -40,22 +40,22 @@ void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsig
#endif #endif
#define LOG_DEBUG(log_class, ...) \ #define LOG_DEBUG(log_class, ...) \
::Common::Log::FmtLogMessage(::Common::Log::Class::log_class, ::Common::Log::Level::Debug, \ Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Debug, \
__FILE__, __LINE__, __func__, \ __FILE__, __LINE__, __func__, \
__VA_ARGS__) __VA_ARGS__)
#define LOG_INFO(log_class, ...) \ #define LOG_INFO(log_class, ...) \
::Common::Log::FmtLogMessage(::Common::Log::Class::log_class, ::Common::Log::Level::Info, \ Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Info, \
__FILE__, __LINE__, __func__, \ __FILE__, __LINE__, __func__, \
__VA_ARGS__) __VA_ARGS__)
#define LOG_WARNING(log_class, ...) \ #define LOG_WARNING(log_class, ...) \
::Common::Log::FmtLogMessage(::Common::Log::Class::log_class, ::Common::Log::Level::Warning, \ Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Warning, \
__FILE__, __LINE__, __func__, \ __FILE__, __LINE__, __func__, \
__VA_ARGS__) __VA_ARGS__)
#define LOG_ERROR(log_class, ...) \ #define LOG_ERROR(log_class, ...) \
::Common::Log::FmtLogMessage(::Common::Log::Class::log_class, ::Common::Log::Level::Error, \ Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Error, \
__FILE__, __LINE__, __func__, \ __FILE__, __LINE__, __func__, \
__VA_ARGS__) __VA_ARGS__)
#define LOG_CRITICAL(log_class, ...) \ #define LOG_CRITICAL(log_class, ...) \
::Common::Log::FmtLogMessage(::Common::Log::Class::log_class, ::Common::Log::Level::Critical, \ Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Critical, \
__FILE__, __LINE__, __func__, \ __FILE__, __LINE__, __func__, \
__VA_ARGS__) __VA_ARGS__)

View file

@ -0,0 +1,940 @@
// SPDX-FileCopyrightText: 2013-2020 Cameron Desrochers
// SPDX-License-Identifier: BSD-2-Clause
#pragma once
#include <cassert>
#include <cstdint>
#include <cstdlib> // For malloc/free/abort & size_t
#include <memory>
#include <new>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include "common/atomic_helpers.h"
#if __cplusplus > 199711L || _MSC_VER >= 1700 // C++11 or VS2012
#include <chrono>
#endif
// A lock-free queue for a single-consumer, single-producer architecture.
// The queue is also wait-free in the common path (except if more memory
// needs to be allocated, in which case malloc is called).
// Allocates memory sparingly, and only once if the original maximum size
// estimate is never exceeded.
// Tested on x86/x64 processors, but semantics should be correct for all
// architectures (given the right implementations in atomicops.h), provided
// that aligned integer and pointer accesses are naturally atomic.
// Note that there should only be one consumer thread and producer thread;
// Switching roles of the threads, or using multiple consecutive threads for
// one role, is not safe unless properly synchronized.
// Using the queue exclusively from one thread is fine, though a bit silly.
#ifndef MOODYCAMEL_CACHE_LINE_SIZE
#define MOODYCAMEL_CACHE_LINE_SIZE 64
#endif
#ifndef MOODYCAMEL_EXCEPTIONS_ENABLED
#if (defined(_MSC_VER) && defined(_CPPUNWIND)) || (defined(__GNUC__) && defined(__EXCEPTIONS)) || \
(!defined(_MSC_VER) && !defined(__GNUC__))
#define MOODYCAMEL_EXCEPTIONS_ENABLED
#endif
#endif
#ifndef MOODYCAMEL_HAS_EMPLACE
#if !defined(_MSC_VER) || \
_MSC_VER >= 1800 // variadic templates: either a non-MS compiler or VS >= 2013
#define MOODYCAMEL_HAS_EMPLACE 1
#endif
#endif
#ifndef MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE
#if defined(__APPLE__) && defined(__MACH__) && __cplusplus >= 201703L
// This is required to find out what deployment target we are using
#include <CoreFoundation/CoreFoundation.h>
#if !defined(MAC_OS_X_VERSION_MIN_REQUIRED) || \
MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_14
// C++17 new(size_t, align_val_t) is not backwards-compatible with older versions of macOS, so we
// can't support over-alignment in this case
#define MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE
#endif
#endif
#endif
#ifndef MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE
#define MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE AE_ALIGN(MOODYCAMEL_CACHE_LINE_SIZE)
#endif
#ifdef AE_VCPP
#pragma warning(push)
#pragma warning(disable : 4324) // structure was padded due to __declspec(align())
#pragma warning(disable : 4820) // padding was added
#pragma warning(disable : 4127) // conditional expression is constant
#endif
namespace Common {
template <typename T, size_t MAX_BLOCK_SIZE = 512>
class MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE ReaderWriterQueue {
// Design: Based on a queue-of-queues. The low-level queues are just
// circular buffers with front and tail indices indicating where the
// next element to dequeue is and where the next element can be enqueued,
// respectively. Each low-level queue is called a "block". Each block
// wastes exactly one element's worth of space to keep the design simple
// (if front == tail then the queue is empty, and can't be full).
// The high-level queue is a circular linked list of blocks; again there
// is a front and tail, but this time they are pointers to the blocks.
// The front block is where the next element to be dequeued is, provided
// the block is not empty. The back block is where elements are to be
// enqueued, provided the block is not full.
// The producer thread owns all the tail indices/pointers. The consumer
// thread owns all the front indices/pointers. Both threads read each
// other's variables, but only the owning thread updates them. E.g. After
// the consumer reads the producer's tail, the tail may change before the
// consumer is done dequeuing an object, but the consumer knows the tail
// will never go backwards, only forwards.
// If there is no room to enqueue an object, an additional block (of
// equal size to the last block) is added. Blocks are never removed.
public:
typedef T value_type;
// Constructs a queue that can hold at least `size` elements without further
// allocations. If more than MAX_BLOCK_SIZE elements are requested,
// then several blocks of MAX_BLOCK_SIZE each are reserved (including
// at least one extra buffer block).
AE_NO_TSAN explicit ReaderWriterQueue(size_t size = 15)
#ifndef NDEBUG
: enqueuing(false), dequeuing(false)
#endif
{
assert(MAX_BLOCK_SIZE == ceilToPow2(MAX_BLOCK_SIZE) &&
"MAX_BLOCK_SIZE must be a power of 2");
assert(MAX_BLOCK_SIZE >= 2 && "MAX_BLOCK_SIZE must be at least 2");
Block* firstBlock = nullptr;
largestBlockSize =
ceilToPow2(size + 1); // We need a spare slot to fit size elements in the block
if (largestBlockSize > MAX_BLOCK_SIZE * 2) {
// We need a spare block in case the producer is writing to a different block the
// consumer is reading from, and wants to enqueue the maximum number of elements. We
// also need a spare element in each block to avoid the ambiguity between front == tail
// meaning "empty" and "full". So the effective number of slots that are guaranteed to
// be usable at any time is the block size - 1 times the number of blocks - 1. Solving
// for size and applying a ceiling to the division gives us (after simplifying):
size_t initialBlockCount = (size + MAX_BLOCK_SIZE * 2 - 3) / (MAX_BLOCK_SIZE - 1);
largestBlockSize = MAX_BLOCK_SIZE;
Block* lastBlock = nullptr;
for (size_t i = 0; i != initialBlockCount; ++i) {
auto block = make_block(largestBlockSize);
if (block == nullptr) {
#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
throw std::bad_alloc();
#else
abort();
#endif
}
if (firstBlock == nullptr) {
firstBlock = block;
} else {
lastBlock->next = block;
}
lastBlock = block;
block->next = firstBlock;
}
} else {
firstBlock = make_block(largestBlockSize);
if (firstBlock == nullptr) {
#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
throw std::bad_alloc();
#else
abort();
#endif
}
firstBlock->next = firstBlock;
}
frontBlock = firstBlock;
tailBlock = firstBlock;
// Make sure the reader/writer threads will have the initialized memory setup above:
fence(memory_order_sync);
}
// Note: The queue should not be accessed concurrently while it's
// being moved. It's up to the user to synchronize this.
AE_NO_TSAN ReaderWriterQueue(ReaderWriterQueue&& other)
: frontBlock(other.frontBlock.load()), tailBlock(other.tailBlock.load()),
largestBlockSize(other.largestBlockSize)
#ifndef NDEBUG
,
enqueuing(false), dequeuing(false)
#endif
{
other.largestBlockSize = 32;
Block* b = other.make_block(other.largestBlockSize);
if (b == nullptr) {
#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
throw std::bad_alloc();
#else
abort();
#endif
}
b->next = b;
other.frontBlock = b;
other.tailBlock = b;
}
// Note: The queue should not be accessed concurrently while it's
// being moved. It's up to the user to synchronize this.
ReaderWriterQueue& operator=(ReaderWriterQueue&& other) AE_NO_TSAN {
Block* b = frontBlock.load();
frontBlock = other.frontBlock.load();
other.frontBlock = b;
b = tailBlock.load();
tailBlock = other.tailBlock.load();
other.tailBlock = b;
std::swap(largestBlockSize, other.largestBlockSize);
return *this;
}
// Note: The queue should not be accessed concurrently while it's
// being deleted. It's up to the user to synchronize this.
AE_NO_TSAN ~ReaderWriterQueue() {
// Make sure we get the latest version of all variables from other CPUs:
fence(memory_order_sync);
// Destroy any remaining objects in queue and free memory
Block* frontBlock_ = frontBlock;
Block* block = frontBlock_;
do {
Block* nextBlock = block->next;
size_t blockFront = block->front;
size_t blockTail = block->tail;
for (size_t i = blockFront; i != blockTail; i = (i + 1) & block->sizeMask) {
auto element = reinterpret_cast<T*>(block->data + i * sizeof(T));
element->~T();
(void)element;
}
auto rawBlock = block->rawThis;
block->~Block();
std::free(rawBlock);
block = nextBlock;
} while (block != frontBlock_);
}
// Enqueues a copy of element if there is room in the queue.
// Returns true if the element was enqueued, false otherwise.
// Does not allocate memory.
AE_FORCEINLINE bool try_enqueue(T const& element) AE_NO_TSAN {
return inner_enqueue<CannotAlloc>(element);
}
// Enqueues a moved copy of element if there is room in the queue.
// Returns true if the element was enqueued, false otherwise.
// Does not allocate memory.
AE_FORCEINLINE bool try_enqueue(T&& element) AE_NO_TSAN {
return inner_enqueue<CannotAlloc>(std::forward<T>(element));
}
#if MOODYCAMEL_HAS_EMPLACE
// Like try_enqueue() but with emplace semantics (i.e. construct-in-place).
template <typename... Args>
AE_FORCEINLINE bool try_emplace(Args&&... args) AE_NO_TSAN {
return inner_enqueue<CannotAlloc>(std::forward<Args>(args)...);
}
#endif
// Enqueues a copy of element on the queue.
// Allocates an additional block of memory if needed.
// Only fails (returns false) if memory allocation fails.
AE_FORCEINLINE bool enqueue(T const& element) AE_NO_TSAN {
return inner_enqueue<CanAlloc>(element);
}
// Enqueues a moved copy of element on the queue.
// Allocates an additional block of memory if needed.
// Only fails (returns false) if memory allocation fails.
AE_FORCEINLINE bool enqueue(T&& element) AE_NO_TSAN {
return inner_enqueue<CanAlloc>(std::forward<T>(element));
}
#if MOODYCAMEL_HAS_EMPLACE
// Like enqueue() but with emplace semantics (i.e. construct-in-place).
template <typename... Args>
AE_FORCEINLINE bool emplace(Args&&... args) AE_NO_TSAN {
return inner_enqueue<CanAlloc>(std::forward<Args>(args)...);
}
#endif
// Attempts to dequeue an element; if the queue is empty,
// returns false instead. If the queue has at least one element,
// moves front to result using operator=, then returns true.
template <typename U>
bool try_dequeue(U& result) AE_NO_TSAN {
#ifndef NDEBUG
ReentrantGuard guard(this->dequeuing);
#endif
// High-level pseudocode:
// Remember where the tail block is
// If the front block has an element in it, dequeue it
// Else
// If front block was the tail block when we entered the function, return false
// Else advance to next block and dequeue the item there
// Note that we have to use the value of the tail block from before we check if the front
// block is full or not, in case the front block is empty and then, before we check if the
// tail block is at the front block or not, the producer fills up the front block *and
// moves on*, which would make us skip a filled block. Seems unlikely, but was consistently
// reproducible in practice.
// In order to avoid overhead in the common case, though, we do a double-checked pattern
// where we have the fast path if the front block is not empty, then read the tail block,
// then re-read the front block and check if it's not empty again, then check if the tail
// block has advanced.
Block* frontBlock_ = frontBlock.load();
size_t blockTail = frontBlock_->localTail;
size_t blockFront = frontBlock_->front.load();
if (blockFront != blockTail ||
blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
fence(memory_order_acquire);
non_empty_front_block:
// Front block not empty, dequeue from here
auto element = reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T));
result = std::move(*element);
element->~T();
blockFront = (blockFront + 1) & frontBlock_->sizeMask;
fence(memory_order_release);
frontBlock_->front = blockFront;
} else if (frontBlock_ != tailBlock.load()) {
fence(memory_order_acquire);
frontBlock_ = frontBlock.load();
blockTail = frontBlock_->localTail = frontBlock_->tail.load();
blockFront = frontBlock_->front.load();
fence(memory_order_acquire);
if (blockFront != blockTail) {
// Oh look, the front block isn't empty after all
goto non_empty_front_block;
}
// Front block is empty but there's another block ahead, advance to it
Block* nextBlock = frontBlock_->next;
// Don't need an acquire fence here since next can only ever be set on the tailBlock,
// and we're not the tailBlock, and we did an acquire earlier after reading tailBlock
// which ensures next is up-to-date on this CPU in case we recently were at tailBlock.
size_t nextBlockFront = nextBlock->front.load();
size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load();
fence(memory_order_acquire);
// Since the tailBlock is only ever advanced after being written to,
// we know there's for sure an element to dequeue on it
assert(nextBlockFront != nextBlockTail);
AE_UNUSED(nextBlockTail);
// We're done with this block, let the producer use it if it needs
fence(memory_order_release); // Expose possibly pending changes to frontBlock->front
// from last dequeue
frontBlock = frontBlock_ = nextBlock;
compiler_fence(memory_order_release); // Not strictly needed
auto element = reinterpret_cast<T*>(frontBlock_->data + nextBlockFront * sizeof(T));
result = std::move(*element);
element->~T();
nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask;
fence(memory_order_release);
frontBlock_->front = nextBlockFront;
} else {
// No elements in current block and no other block to advance to
return false;
}
return true;
}
// Returns a pointer to the front element in the queue (the one that
// would be removed next by a call to `try_dequeue` or `pop`). If the
// queue appears empty at the time the method is called, nullptr is
// returned instead.
// Must be called only from the consumer thread.
T* peek() const AE_NO_TSAN {
#ifndef NDEBUG
ReentrantGuard guard(this->dequeuing);
#endif
// See try_dequeue() for reasoning
Block* frontBlock_ = frontBlock.load();
size_t blockTail = frontBlock_->localTail;
size_t blockFront = frontBlock_->front.load();
if (blockFront != blockTail ||
blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
fence(memory_order_acquire);
non_empty_front_block:
return reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T));
} else if (frontBlock_ != tailBlock.load()) {
fence(memory_order_acquire);
frontBlock_ = frontBlock.load();
blockTail = frontBlock_->localTail = frontBlock_->tail.load();
blockFront = frontBlock_->front.load();
fence(memory_order_acquire);
if (blockFront != blockTail) {
goto non_empty_front_block;
}
Block* nextBlock = frontBlock_->next;
size_t nextBlockFront = nextBlock->front.load();
fence(memory_order_acquire);
assert(nextBlockFront != nextBlock->tail.load());
return reinterpret_cast<T*>(nextBlock->data + nextBlockFront * sizeof(T));
}
return nullptr;
}
// Removes the front element from the queue, if any, without returning it.
// Returns true on success, or false if the queue appeared empty at the time
// `pop` was called.
bool pop() AE_NO_TSAN {
#ifndef NDEBUG
ReentrantGuard guard(this->dequeuing);
#endif
// See try_dequeue() for reasoning
Block* frontBlock_ = frontBlock.load();
size_t blockTail = frontBlock_->localTail;
size_t blockFront = frontBlock_->front.load();
if (blockFront != blockTail ||
blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
fence(memory_order_acquire);
non_empty_front_block:
auto element = reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T));
element->~T();
blockFront = (blockFront + 1) & frontBlock_->sizeMask;
fence(memory_order_release);
frontBlock_->front = blockFront;
} else if (frontBlock_ != tailBlock.load()) {
fence(memory_order_acquire);
frontBlock_ = frontBlock.load();
blockTail = frontBlock_->localTail = frontBlock_->tail.load();
blockFront = frontBlock_->front.load();
fence(memory_order_acquire);
if (blockFront != blockTail) {
goto non_empty_front_block;
}
// Front block is empty but there's another block ahead, advance to it
Block* nextBlock = frontBlock_->next;
size_t nextBlockFront = nextBlock->front.load();
size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load();
fence(memory_order_acquire);
assert(nextBlockFront != nextBlockTail);
AE_UNUSED(nextBlockTail);
fence(memory_order_release);
frontBlock = frontBlock_ = nextBlock;
compiler_fence(memory_order_release);
auto element = reinterpret_cast<T*>(frontBlock_->data + nextBlockFront * sizeof(T));
element->~T();
nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask;
fence(memory_order_release);
frontBlock_->front = nextBlockFront;
} else {
// No elements in current block and no other block to advance to
return false;
}
return true;
}
// Returns the approximate number of items currently in the queue.
// Safe to call from both the producer and consumer threads.
inline size_t size_approx() const AE_NO_TSAN {
size_t result = 0;
Block* frontBlock_ = frontBlock.load();
Block* block = frontBlock_;
do {
fence(memory_order_acquire);
size_t blockFront = block->front.load();
size_t blockTail = block->tail.load();
result += (blockTail - blockFront) & block->sizeMask;
block = block->next.load();
} while (block != frontBlock_);
return result;
}
// Returns the total number of items that could be enqueued without incurring
// an allocation when this queue is empty.
// Safe to call from both the producer and consumer threads.
//
// NOTE: The actual capacity during usage may be different depending on the consumer.
// If the consumer is removing elements concurrently, the producer cannot add to
// the block the consumer is removing from until it's completely empty, except in
// the case where the producer was writing to the same block the consumer was
// reading from the whole time.
inline size_t max_capacity() const {
size_t result = 0;
Block* frontBlock_ = frontBlock.load();
Block* block = frontBlock_;
do {
fence(memory_order_acquire);
result += block->sizeMask;
block = block->next.load();
} while (block != frontBlock_);
return result;
}
private:
enum AllocationMode { CanAlloc, CannotAlloc };
#if MOODYCAMEL_HAS_EMPLACE
template <AllocationMode canAlloc, typename... Args>
bool inner_enqueue(Args&&... args) AE_NO_TSAN
#else
template <AllocationMode canAlloc, typename U>
bool inner_enqueue(U&& element) AE_NO_TSAN
#endif
{
#ifndef NDEBUG
ReentrantGuard guard(this->enqueuing);
#endif
// High-level pseudocode (assuming we're allowed to alloc a new block):
// If room in tail block, add to tail
// Else check next block
// If next block is not the head block, enqueue on next block
// Else create a new block and enqueue there
// Advance tail to the block we just enqueued to
Block* tailBlock_ = tailBlock.load();
size_t blockFront = tailBlock_->localFront;
size_t blockTail = tailBlock_->tail.load();
size_t nextBlockTail = (blockTail + 1) & tailBlock_->sizeMask;
if (nextBlockTail != blockFront ||
nextBlockTail != (tailBlock_->localFront = tailBlock_->front.load())) {
fence(memory_order_acquire);
// This block has room for at least one more element
char* location = tailBlock_->data + blockTail * sizeof(T);
#if MOODYCAMEL_HAS_EMPLACE
new (location) T(std::forward<Args>(args)...);
#else
new (location) T(std::forward<U>(element));
#endif
fence(memory_order_release);
tailBlock_->tail = nextBlockTail;
} else {
fence(memory_order_acquire);
if (tailBlock_->next.load() != frontBlock) {
// Note that the reason we can't advance to the frontBlock and start adding new
// entries there is because if we did, then dequeue would stay in that block,
// eventually reading the new values, instead of advancing to the next full block
// (whose values were enqueued first and so should be consumed first).
fence(memory_order_acquire); // Ensure we get latest writes if we got the latest
// frontBlock
// tailBlock is full, but there's a free block ahead, use it
Block* tailBlockNext = tailBlock_->next.load();
size_t nextBlockFront = tailBlockNext->localFront = tailBlockNext->front.load();
nextBlockTail = tailBlockNext->tail.load();
fence(memory_order_acquire);
// This block must be empty since it's not the head block and we
// go through the blocks in a circle
assert(nextBlockFront == nextBlockTail);
tailBlockNext->localFront = nextBlockFront;
char* location = tailBlockNext->data + nextBlockTail * sizeof(T);
#if MOODYCAMEL_HAS_EMPLACE
new (location) T(std::forward<Args>(args)...);
#else
new (location) T(std::forward<U>(element));
#endif
tailBlockNext->tail = (nextBlockTail + 1) & tailBlockNext->sizeMask;
fence(memory_order_release);
tailBlock = tailBlockNext;
} else if (canAlloc == CanAlloc) {
// tailBlock is full and there's no free block ahead; create a new block
auto newBlockSize =
largestBlockSize >= MAX_BLOCK_SIZE ? largestBlockSize : largestBlockSize * 2;
auto newBlock = make_block(newBlockSize);
if (newBlock == nullptr) {
// Could not allocate a block!
return false;
}
largestBlockSize = newBlockSize;
#if MOODYCAMEL_HAS_EMPLACE
new (newBlock->data) T(std::forward<Args>(args)...);
#else
new (newBlock->data) T(std::forward<U>(element));
#endif
assert(newBlock->front == 0);
newBlock->tail = newBlock->localTail = 1;
newBlock->next = tailBlock_->next.load();
tailBlock_->next = newBlock;
// Might be possible for the dequeue thread to see the new tailBlock->next
// *without* seeing the new tailBlock value, but this is OK since it can't
// advance to the next block until tailBlock is set anyway (because the only
// case where it could try to read the next is if it's already at the tailBlock,
// and it won't advance past tailBlock in any circumstance).
fence(memory_order_release);
tailBlock = newBlock;
} else if (canAlloc == CannotAlloc) {
// Would have had to allocate a new block to enqueue, but not allowed
return false;
} else {
assert(false && "Should be unreachable code");
return false;
}
}
return true;
}
// Disable copying
ReaderWriterQueue(ReaderWriterQueue const&) {}
// Disable assignment
ReaderWriterQueue& operator=(ReaderWriterQueue const&) {}
AE_FORCEINLINE static size_t ceilToPow2(size_t x) {
// From http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
--x;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
for (size_t i = 1; i < sizeof(size_t); i <<= 1) {
x |= x >> (i << 3);
}
++x;
return x;
}
template <typename U>
static AE_FORCEINLINE char* align_for(char* ptr) AE_NO_TSAN {
const std::size_t alignment = std::alignment_of<U>::value;
return ptr + (alignment - (reinterpret_cast<std::uintptr_t>(ptr) % alignment)) % alignment;
}
private:
#ifndef NDEBUG
struct ReentrantGuard {
AE_NO_TSAN ReentrantGuard(weak_atomic<bool>& _inSection) : inSection(_inSection) {
assert(!inSection &&
"Concurrent (or re-entrant) enqueue or dequeue operation detected (only one "
"thread at a time may hold the producer or consumer role)");
inSection = true;
}
AE_NO_TSAN ~ReentrantGuard() {
inSection = false;
}
private:
ReentrantGuard& operator=(ReentrantGuard const&);
private:
weak_atomic<bool>& inSection;
};
#endif
struct Block {
// Avoid false-sharing by putting highly contended variables on their own cache lines
weak_atomic<size_t> front; // (Atomic) Elements are read from here
size_t localTail; // An uncontended shadow copy of tail, owned by the consumer
char cachelineFiller0[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic<size_t>) -
sizeof(size_t)];
weak_atomic<size_t> tail; // (Atomic) Elements are enqueued here
size_t localFront;
char cachelineFiller1[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic<size_t>) -
sizeof(size_t)]; // next isn't very contended, but we don't want it on
// the same cache line as tail (which is)
weak_atomic<Block*> next; // (Atomic)
char* data; // Contents (on heap) are aligned to T's alignment
const size_t sizeMask;
// size must be a power of two (and greater than 0)
AE_NO_TSAN Block(size_t const& _size, char* _rawThis, char* _data)
: front(0UL), localTail(0), tail(0UL), localFront(0), next(nullptr), data(_data),
sizeMask(_size - 1), rawThis(_rawThis) {}
private:
// C4512 - Assignment operator could not be generated
Block& operator=(Block const&);
public:
char* rawThis;
};
static Block* make_block(size_t capacity) AE_NO_TSAN {
// Allocate enough memory for the block itself, as well as all the elements it will contain
auto size = sizeof(Block) + std::alignment_of<Block>::value - 1;
size += sizeof(T) * capacity + std::alignment_of<T>::value - 1;
auto newBlockRaw = static_cast<char*>(std::malloc(size));
if (newBlockRaw == nullptr) {
return nullptr;
}
auto newBlockAligned = align_for<Block>(newBlockRaw);
auto newBlockData = align_for<T>(newBlockAligned + sizeof(Block));
return new (newBlockAligned) Block(capacity, newBlockRaw, newBlockData);
}
private:
weak_atomic<Block*> frontBlock; // (Atomic) Elements are dequeued from this block
char cachelineFiller[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic<Block*>)];
weak_atomic<Block*> tailBlock; // (Atomic) Elements are enqueued to this block
size_t largestBlockSize;
#ifndef NDEBUG
weak_atomic<bool> enqueuing;
mutable weak_atomic<bool> dequeuing;
#endif
};
// Like ReaderWriterQueue, but also providees blocking operations
template <typename T, size_t MAX_BLOCK_SIZE = 512>
class BlockingReaderWriterQueue {
private:
typedef ::Common::ReaderWriterQueue<T, MAX_BLOCK_SIZE> ReaderWriterQueue;
public:
explicit BlockingReaderWriterQueue(size_t size = 15) AE_NO_TSAN
: inner(size),
sema(new spsc_sema::LightweightSemaphore()) {}
BlockingReaderWriterQueue(BlockingReaderWriterQueue&& other) AE_NO_TSAN
: inner(std::move(other.inner)),
sema(std::move(other.sema)) {}
BlockingReaderWriterQueue& operator=(BlockingReaderWriterQueue&& other) AE_NO_TSAN {
std::swap(sema, other.sema);
std::swap(inner, other.inner);
return *this;
}
// Enqueues a copy of element if there is room in the queue.
// Returns true if the element was enqueued, false otherwise.
// Does not allocate memory.
AE_FORCEINLINE bool try_enqueue(T const& element) AE_NO_TSAN {
if (inner.try_enqueue(element)) {
sema->signal();
return true;
}
return false;
}
// Enqueues a moved copy of element if there is room in the queue.
// Returns true if the element was enqueued, false otherwise.
// Does not allocate memory.
AE_FORCEINLINE bool try_enqueue(T&& element) AE_NO_TSAN {
if (inner.try_enqueue(std::forward<T>(element))) {
sema->signal();
return true;
}
return false;
}
#if MOODYCAMEL_HAS_EMPLACE
// Like try_enqueue() but with emplace semantics (i.e. construct-in-place).
template <typename... Args>
AE_FORCEINLINE bool try_emplace(Args&&... args) AE_NO_TSAN {
if (inner.try_emplace(std::forward<Args>(args)...)) {
sema->signal();
return true;
}
return false;
}
#endif
// Enqueues a copy of element on the queue.
// Allocates an additional block of memory if needed.
// Only fails (returns false) if memory allocation fails.
AE_FORCEINLINE bool enqueue(T const& element) AE_NO_TSAN {
if (inner.enqueue(element)) {
sema->signal();
return true;
}
return false;
}
// Enqueues a moved copy of element on the queue.
// Allocates an additional block of memory if needed.
// Only fails (returns false) if memory allocation fails.
AE_FORCEINLINE bool enqueue(T&& element) AE_NO_TSAN {
if (inner.enqueue(std::forward<T>(element))) {
sema->signal();
return true;
}
return false;
}
#if MOODYCAMEL_HAS_EMPLACE
// Like enqueue() but with emplace semantics (i.e. construct-in-place).
template <typename... Args>
AE_FORCEINLINE bool emplace(Args&&... args) AE_NO_TSAN {
if (inner.emplace(std::forward<Args>(args)...)) {
sema->signal();
return true;
}
return false;
}
#endif
// Attempts to dequeue an element; if the queue is empty,
// returns false instead. If the queue has at least one element,
// moves front to result using operator=, then returns true.
template <typename U>
bool try_dequeue(U& result) AE_NO_TSAN {
if (sema->tryWait()) {
bool success = inner.try_dequeue(result);
assert(success);
AE_UNUSED(success);
return true;
}
return false;
}
// Attempts to dequeue an element; if the queue is empty,
// waits until an element is available, then dequeues it.
template <typename U>
void wait_dequeue(U& result) AE_NO_TSAN {
while (!sema->wait())
;
bool success = inner.try_dequeue(result);
AE_UNUSED(result);
assert(success);
AE_UNUSED(success);
}
// Attempts to dequeue an element; if the queue is empty,
// waits until an element is available up to the specified timeout,
// then dequeues it and returns true, or returns false if the timeout
// expires before an element can be dequeued.
// Using a negative timeout indicates an indefinite timeout,
// and is thus functionally equivalent to calling wait_dequeue.
template <typename U>
bool wait_dequeue_timed(U& result, std::int64_t timeout_usecs) AE_NO_TSAN {
if (!sema->wait(timeout_usecs)) {
return false;
}
bool success = inner.try_dequeue(result);
AE_UNUSED(result);
assert(success);
AE_UNUSED(success);
return true;
}
#if __cplusplus > 199711L || _MSC_VER >= 1700
// Attempts to dequeue an element; if the queue is empty,
// waits until an element is available up to the specified timeout,
// then dequeues it and returns true, or returns false if the timeout
// expires before an element can be dequeued.
// Using a negative timeout indicates an indefinite timeout,
// and is thus functionally equivalent to calling wait_dequeue.
template <typename U, typename Rep, typename Period>
inline bool wait_dequeue_timed(U& result,
std::chrono::duration<Rep, Period> const& timeout) AE_NO_TSAN {
return wait_dequeue_timed(
result, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
}
#endif
// Returns a pointer to the front element in the queue (the one that
// would be removed next by a call to `try_dequeue` or `pop`). If the
// queue appears empty at the time the method is called, nullptr is
// returned instead.
// Must be called only from the consumer thread.
AE_FORCEINLINE T* peek() const AE_NO_TSAN {
return inner.peek();
}
// Removes the front element from the queue, if any, without returning it.
// Returns true on success, or false if the queue appeared empty at the time
// `pop` was called.
AE_FORCEINLINE bool pop() AE_NO_TSAN {
if (sema->tryWait()) {
bool result = inner.pop();
assert(result);
AE_UNUSED(result);
return true;
}
return false;
}
// Returns the approximate number of items currently in the queue.
// Safe to call from both the producer and consumer threads.
AE_FORCEINLINE size_t size_approx() const AE_NO_TSAN {
return sema->availableApprox();
}
// Returns the total number of items that could be enqueued without incurring
// an allocation when this queue is empty.
// Safe to call from both the producer and consumer threads.
//
// NOTE: The actual capacity during usage may be different depending on the consumer.
// If the consumer is removing elements concurrently, the producer cannot add to
// the block the consumer is removing from until it's completely empty, except in
// the case where the producer was writing to the same block the consumer was
// reading from the whole time.
AE_FORCEINLINE size_t max_capacity() const {
return inner.max_capacity();
}
private:
// Disable copying & assignment
BlockingReaderWriterQueue(BlockingReaderWriterQueue const&) {}
BlockingReaderWriterQueue& operator=(BlockingReaderWriterQueue const&) {}
private:
ReaderWriterQueue inner;
std::unique_ptr<spsc_sema::LightweightSemaphore> sema;
};
} // namespace Common
#ifdef AE_VCPP
#pragma warning(pop)
#endif

View file

@ -336,6 +336,7 @@ struct Values {
"shader_backend", Category::Renderer, Specialization::RuntimeList}; "shader_backend", Category::Renderer, Specialization::RuntimeList};
SwitchableSetting<int> vulkan_device{linkage, 0, "vulkan_device", Category::Renderer, SwitchableSetting<int> vulkan_device{linkage, 0, "vulkan_device", Category::Renderer,
Specialization::RuntimeList}; Specialization::RuntimeList};
SwitchableSetting<bool> enable_raii{linkage, false, "enable_raii", Category::Renderer};
#ifdef __ANDROID__ #ifdef __ANDROID__
SwitchableSetting<bool> frame_interpolation{linkage, true, "frame_interpolation", Category::Renderer, SwitchableSetting<bool> frame_interpolation{linkage, true, "frame_interpolation", Category::Renderer,
Specialization::RuntimeList}; Specialization::RuntimeList};
@ -735,7 +736,6 @@ struct Values {
Setting<bool> enable_all_controllers{linkage, false, "enable_all_controllers", Setting<bool> enable_all_controllers{linkage, false, "enable_all_controllers",
Category::Debugging}; Category::Debugging};
Setting<bool> perform_vulkan_check{linkage, true, "perform_vulkan_check", Category::Debugging}; Setting<bool> perform_vulkan_check{linkage, true, "perform_vulkan_check", Category::Debugging};
Setting<bool> disable_web_applet{linkage, true, "disable_web_applet", Category::Debugging};
// Miscellaneous // Miscellaneous
Setting<std::string> log_filter{linkage, "*:Info", "log_filter", Category::Miscellaneous}; Setting<std::string> log_filter{linkage, "*:Info", "log_filter", Category::Miscellaneous};

View file

@ -143,7 +143,7 @@ ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never);
ENUM(FullscreenMode, Borderless, Exclusive); ENUM(FullscreenMode, Borderless, Exclusive);
ENUM(NvdecEmulation, Off, Cpu, Gpu); ENUM(NvdecEmulation, Off, Cpu, Gpu);
ENUM(ResolutionSetup, Res1_4X, Res1_2X, Res3_4X, Res1X, Res5_4X, Res3_2X, Res2X, Res3X, Res4X, Res5X, Res6X, Res7X, Res8X); ENUM(ResolutionSetup, Res1_4X, Res1_2X, Res3_4X, Res1X, Res5_4X, Res3_2X, Res2X, Res3X, Res4X, Res5X, Res6X, Res7X, Res8X);
ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, Lanczos, ScaleForce, Fsr, Area, ZeroTangent, BSpline, Mitchell, Spline1, Mmpx, MaxEnum); ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, ZeroTangent, BSpline, Mitchell, Spline1, Gaussian, Lanczos, ScaleForce, Fsr, Area, Mmpx, MaxEnum);
ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum); ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum);
ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch); ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
ENUM(ConsoleMode, Handheld, Docked); ENUM(ConsoleMode, Handheld, Docked);

View file

@ -1,11 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <array> #include <array>
#include <vector>
#include <mbedtls/cipher.h> #include <mbedtls/cipher.h>
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
@ -75,16 +71,14 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* des
mbedtls_cipher_reset(context); mbedtls_cipher_reset(context);
// Only ECB strictly requires block sized chunks.
std::size_t written = 0; std::size_t written = 0;
if (mbedtls_cipher_get_cipher_mode(context) != MBEDTLS_MODE_ECB) { if (mbedtls_cipher_get_cipher_mode(context) == MBEDTLS_MODE_XTS) {
mbedtls_cipher_update(context, src, size, dest, &written); mbedtls_cipher_update(context, src, size, dest, &written);
if (written != size) if (written != size) {
LOG_WARNING(Crypto, "Not all data was processed requested={:016X}, actual={:016X}.", size, written); LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
return; size, written);
} }
} else {
// ECB path: operate in block sized chunks and mirror previous behavior.
const auto block_size = mbedtls_cipher_get_block_size(context); const auto block_size = mbedtls_cipher_get_block_size(context);
if (size < block_size) { if (size < block_size) {
std::vector<u8> block(block_size); std::vector<u8> block(block_size);
@ -95,7 +89,7 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* des
} }
for (std::size_t offset = 0; offset < size; offset += block_size) { for (std::size_t offset = 0; offset < size; offset += block_size) {
const auto length = std::min<std::size_t>(block_size, size - offset); auto length = std::min<std::size_t>(block_size, size - offset);
mbedtls_cipher_update(context, src + offset, length, dest + offset, &written); mbedtls_cipher_update(context, src + offset, length, dest + offset, &written);
if (written != length) { if (written != length) {
if (length < block_size) { if (length < block_size) {
@ -105,7 +99,9 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* des
std::memcpy(dest + offset, block.data(), length); std::memcpy(dest + offset, block.data(), length);
return; return;
} }
LOG_WARNING(Crypto, "Not all data was processed requested={:016X}, actual={:016X}.", length, written); LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
length, written);
}
} }
} }
} }

View file

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -18,36 +15,26 @@ std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t o
if (length == 0) if (length == 0)
return 0; return 0;
std::size_t total_read = 0; const auto sector_offset = offset & 0xF;
// Handle an initial misaligned portion if needed. if (sector_offset == 0) {
if (auto const sector_offset = offset & 0xF; sector_offset != 0) {
const std::size_t aligned_off = offset - sector_offset;
std::array<u8, 0x10> block{};
if (auto const got = base->Read(block.data(), block.size(), aligned_off); got != 0) {
UpdateIV(base_offset + aligned_off);
cipher.Transcode(block.data(), got, block.data(), Op::Decrypt);
auto const to_copy = std::min<std::size_t>(length, got > sector_offset ? got - sector_offset : 0);
if (to_copy > 0) {
std::memcpy(data, block.data() + sector_offset, to_copy);
data += to_copy;
offset += to_copy;
length -= to_copy;
total_read += to_copy;
}
} else {
return 0;
}
}
if (length > 0) {
// Now aligned to 0x10
UpdateIV(base_offset + offset); UpdateIV(base_offset + offset);
const std::size_t got = base->Read(data, length, offset); std::vector<u8> raw = base->ReadBytes(length, offset);
if (got > 0) { cipher.Transcode(raw.data(), raw.size(), data, Op::Decrypt);
cipher.Transcode(data, got, data, Op::Decrypt); return length;
total_read += got;
} }
// offset does not fall on block boundary (0x10)
std::vector<u8> block = base->ReadBytes(0x10, offset - sector_offset);
UpdateIV(base_offset + offset - sector_offset);
cipher.Transcode(block.data(), block.size(), block.data(), Op::Decrypt);
std::size_t read = 0x10 - sector_offset;
if (length + sector_offset < 0x10) {
std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read));
return std::min<u64>(length, read);
} }
return total_read; std::memcpy(data, block.data() + sector_offset, read);
return read + Read(data + read, length - read, offset + read);
} }
void CTREncryptionLayer::SetIV(const IVData& iv_) { void CTREncryptionLayer::SetIV(const IVData& iv_) {

View file

@ -5,13 +5,12 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm> #include <algorithm>
#include <array>
#include <cstring> #include <cstring>
#include "core/crypto/xts_encryption_layer.h" #include "core/crypto/xts_encryption_layer.h"
namespace Core::Crypto { namespace Core::Crypto {
constexpr std::size_t XTS_SECTOR_SIZE = 0x4000; constexpr u64 XTS_SECTOR_SIZE = 0x4000;
XTSEncryptionLayer::XTSEncryptionLayer(FileSys::VirtualFile base_, Key256 key_) XTSEncryptionLayer::XTSEncryptionLayer(FileSys::VirtualFile base_, Key256 key_)
: EncryptionLayer(std::move(base_)), cipher(key_, Mode::XTS) {} : EncryptionLayer(std::move(base_)), cipher(key_, Mode::XTS) {}
@ -20,67 +19,41 @@ std::size_t XTSEncryptionLayer::Read(u8* data, std::size_t length, std::size_t o
if (length == 0) if (length == 0)
return 0; return 0;
std::size_t total_read = 0; const auto sector_offset = offset & 0x3FFF;
// Handle initial unaligned part within a sector. if (sector_offset == 0) {
if (auto const sector_offset = offset % XTS_SECTOR_SIZE; sector_offset != 0) { if (length % XTS_SECTOR_SIZE == 0) {
const std::size_t aligned_off = offset - sector_offset; std::vector<u8> raw = base->ReadBytes(length, offset);
std::array<u8, XTS_SECTOR_SIZE> block{}; cipher.XTSTranscode(raw.data(), raw.size(), data, offset / XTS_SECTOR_SIZE,
if (auto const got = base->Read(block.data(), XTS_SECTOR_SIZE, aligned_off); got > 0) {
if (got < XTS_SECTOR_SIZE)
std::memset(block.data() + got, 0, XTS_SECTOR_SIZE - got);
cipher.XTSTranscode(block.data(), XTS_SECTOR_SIZE, block.data(), aligned_off / XTS_SECTOR_SIZE,
XTS_SECTOR_SIZE, Op::Decrypt); XTS_SECTOR_SIZE, Op::Decrypt);
return raw.size();
auto const to_copy = std::min<std::size_t>(length, got > sector_offset ? got - sector_offset : 0);
if (to_copy > 0) {
std::memcpy(data, block.data() + sector_offset, to_copy);
data += to_copy;
offset += to_copy;
length -= to_copy;
total_read += to_copy;
} }
} else { if (length > XTS_SECTOR_SIZE) {
return 0; const auto rem = length % XTS_SECTOR_SIZE;
const auto read = length - rem;
return Read(data, read, offset) + Read(data + read, rem, offset + read);
} }
std::vector<u8> buffer = base->ReadBytes(XTS_SECTOR_SIZE, offset);
if (buffer.size() < XTS_SECTOR_SIZE)
buffer.resize(XTS_SECTOR_SIZE);
cipher.XTSTranscode(buffer.data(), buffer.size(), buffer.data(), offset / XTS_SECTOR_SIZE,
XTS_SECTOR_SIZE, Op::Decrypt);
std::memcpy(data, buffer.data(), (std::min)(buffer.size(), length));
return (std::min)(buffer.size(), length);
} }
if (length > 0) { // offset does not fall on block boundary (0x4000)
// Process aligned middle inplace, in sector sized multiples. std::vector<u8> block = base->ReadBytes(0x4000, offset - sector_offset);
while (length >= XTS_SECTOR_SIZE) { if (block.size() < XTS_SECTOR_SIZE)
const std::size_t req = (length / XTS_SECTOR_SIZE) * XTS_SECTOR_SIZE; block.resize(XTS_SECTOR_SIZE);
const std::size_t got = base->Read(data, req, offset); cipher.XTSTranscode(block.data(), block.size(), block.data(),
if (got == 0) { (offset - sector_offset) / XTS_SECTOR_SIZE, XTS_SECTOR_SIZE, Op::Decrypt);
return total_read; const std::size_t read = XTS_SECTOR_SIZE - sector_offset;
if (length + sector_offset < XTS_SECTOR_SIZE) {
std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read));
return std::min<u64>(length, read);
} }
const std::size_t got_rounded = got - (got % XTS_SECTOR_SIZE); std::memcpy(data, block.data() + sector_offset, read);
if (got_rounded > 0) { return read + Read(data + read, length - read, offset + read);
cipher.XTSTranscode(data, got_rounded, data, offset / XTS_SECTOR_SIZE, XTS_SECTOR_SIZE, Op::Decrypt);
data += got_rounded;
offset += got_rounded;
length -= got_rounded;
total_read += got_rounded;
}
// If we didn't get a full sector next, break to handle tail.
if (got_rounded != got) {
break;
}
}
// Handle tail within a sector, if any.
if (length > 0) {
std::array<u8, XTS_SECTOR_SIZE> block{};
const std::size_t got = base->Read(block.data(), XTS_SECTOR_SIZE, offset);
if (got > 0) {
if (got < XTS_SECTOR_SIZE) {
std::memset(block.data() + got, 0, XTS_SECTOR_SIZE - got);
}
cipher.XTSTranscode(block.data(), XTS_SECTOR_SIZE, block.data(),
offset / XTS_SECTOR_SIZE, XTS_SECTOR_SIZE, Op::Decrypt);
const std::size_t to_copy = std::min<std::size_t>(length, got);
std::memcpy(data, block.data(), to_copy);
total_read += to_copy;
}
}
}
return total_read;
} }
} // namespace Core::Crypto } // namespace Core::Crypto

View file

@ -4,7 +4,6 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <boost/container/static_vector.hpp>
#include "common/alignment.h" #include "common/alignment.h"
#include "common/swap.h" #include "common/swap.h"
#include "core/file_sys/fssystem/fssystem_aes_ctr_storage.h" #include "core/file_sys/fssystem/fssystem_aes_ctr_storage.h"
@ -84,24 +83,32 @@ size_t AesCtrStorage::Write(const u8* buffer, size_t size, size_t offset) {
std::memcpy(ctr.data(), m_iv.data(), IvSize); std::memcpy(ctr.data(), m_iv.data(), IvSize);
AddCounter(ctr.data(), IvSize, offset / BlockSize); AddCounter(ctr.data(), IvSize, offset / BlockSize);
// Loop until all data is written using a pooled buffer residing on the stack (blocksize = 0x10) // Loop until all data is written.
boost::container::static_vector<u8, BlockSize> pooled_buffer; size_t remaining = size;
for (size_t remaining = size; remaining > 0; ) { s64 cur_offset = 0;
// Determine data we're writing and where.
auto const write_size = (std::min)(pooled_buffer.size(), remaining);
u8* write_buf = pooled_buffer.data();
// Encrypt the data and then write it. // Get a pooled buffer.
std::vector<char> pooled_buffer(BlockSize);
while (remaining > 0) {
// Determine data we're writing and where.
const size_t write_size = std::min(pooled_buffer.size(), remaining);
u8* write_buf = reinterpret_cast<u8*>(pooled_buffer.data());
// Encrypt the data.
m_cipher->SetIV(ctr); m_cipher->SetIV(ctr);
m_cipher->Transcode(buffer, write_size, write_buf, Core::Crypto::Op::Encrypt); m_cipher->Transcode(buffer, write_size, write_buf, Core::Crypto::Op::Encrypt);
m_base_storage->Write(write_buf, write_size, offset);
// Advance next write chunk // Write the encrypted data.
offset += write_size; m_base_storage->Write(write_buf, write_size, offset + cur_offset);
// Advance.
cur_offset += write_size;
remaining -= write_size; remaining -= write_size;
if (remaining > 0) if (remaining > 0) {
AddCounter(ctr.data(), IvSize, write_size / BlockSize); AddCounter(ctr.data(), IvSize, write_size / BlockSize);
} }
}
return size; return size;
} }

View file

@ -4,13 +4,9 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
#include <boost/container/static_vector.hpp>
#include "common/alignment.h" #include "common/alignment.h"
#include "common/swap.h" #include "common/swap.h"
#include "core/file_sys/fssystem/fssystem_aes_xts_storage.h" #include "core/file_sys/fssystem/fssystem_aes_xts_storage.h"
#include "core/file_sys/fssystem/fssystem_nca_header.h"
#include "core/file_sys/fssystem/fssystem_utility.h" #include "core/file_sys/fssystem/fssystem_utility.h"
namespace FileSys { namespace FileSys {
@ -45,12 +41,18 @@ AesXtsStorage::AesXtsStorage(VirtualFile base, const void* key1, const void* key
size_t AesXtsStorage::Read(u8* buffer, size_t size, size_t offset) const { size_t AesXtsStorage::Read(u8* buffer, size_t size, size_t offset) const {
// Allow zero-size reads. // Allow zero-size reads.
if (size == 0) if (size == 0) {
return size; return size;
}
// Ensure buffer is valid and we can only read at block aligned offsets. // Ensure buffer is valid.
ASSERT(buffer != nullptr); ASSERT(buffer != nullptr);
ASSERT(Common::IsAligned(offset, AesBlockSize) && Common::IsAligned(size, AesBlockSize));
// We can only read at block aligned offsets.
ASSERT(Common::IsAligned(offset, AesBlockSize));
ASSERT(Common::IsAligned(size, AesBlockSize));
// Read the data.
m_base_storage->Read(buffer, size, offset); m_base_storage->Read(buffer, size, offset);
// Setup the counter. // Setup the counter.
@ -58,21 +60,25 @@ size_t AesXtsStorage::Read(u8* buffer, size_t size, size_t offset) const {
std::memcpy(ctr.data(), m_iv.data(), IvSize); std::memcpy(ctr.data(), m_iv.data(), IvSize);
AddCounter(ctr.data(), IvSize, offset / m_block_size); AddCounter(ctr.data(), IvSize, offset / m_block_size);
// Handle any unaligned data before the start; then read said data into a local pooled // Handle any unaligned data before the start.
// buffer that resides on the stack, do not use the global memory allocator this is a
// very tiny (512 bytes) buffer so should be fine to keep on the stack (Nca::XtsBlockSize wide buffer)
size_t processed_size = 0; size_t processed_size = 0;
if ((offset % m_block_size) != 0) { if ((offset % m_block_size) != 0) {
// Decrypt into our pooled stack buffer (max bound = NCA::XtsBlockSize)
boost::container::static_vector<u8, NcaHeader::XtsBlockSize> tmp_buf;
// Determine the size of the pre-data read. // Determine the size of the pre-data read.
auto const skip_size = size_t(offset - Common::AlignDown(offset, m_block_size)); const size_t skip_size =
auto const data_size = (std::min)(size, m_block_size - skip_size); static_cast<size_t>(offset - Common::AlignDown(offset, m_block_size));
std::fill_n(tmp_buf.begin(), skip_size, u8{0}); const size_t data_size = (std::min)(size, m_block_size - skip_size);
// Decrypt into a pooled buffer.
{
std::vector<char> tmp_buf(m_block_size, 0);
std::memcpy(tmp_buf.data() + skip_size, buffer, data_size); std::memcpy(tmp_buf.data() + skip_size, buffer, data_size);
m_cipher->SetIV(ctr); m_cipher->SetIV(ctr);
m_cipher->Transcode(tmp_buf.data(), m_block_size, tmp_buf.data(), Core::Crypto::Op::Decrypt); m_cipher->Transcode(tmp_buf.data(), m_block_size, tmp_buf.data(),
Core::Crypto::Op::Decrypt);
std::memcpy(buffer, tmp_buf.data() + skip_size, data_size); std::memcpy(buffer, tmp_buf.data() + skip_size, data_size);
}
AddCounter(ctr.data(), IvSize, 1); AddCounter(ctr.data(), IvSize, 1);
processed_size += data_size; processed_size += data_size;
@ -80,16 +86,20 @@ size_t AesXtsStorage::Read(u8* buffer, size_t size, size_t offset) const {
} }
// Decrypt aligned chunks. // Decrypt aligned chunks.
auto* cur = buffer + processed_size; char* cur = reinterpret_cast<char*>(buffer) + processed_size;
for (size_t remaining = size - processed_size; remaining > 0; ) { size_t remaining = size - processed_size;
auto const cur_size = (std::min)(m_block_size, remaining); while (remaining > 0) {
const size_t cur_size = (std::min)(m_block_size, remaining);
m_cipher->SetIV(ctr); m_cipher->SetIV(ctr);
auto* char_cur = reinterpret_cast<char*>(cur); //same repr cur - diff signedness m_cipher->Transcode(cur, cur_size, cur, Core::Crypto::Op::Decrypt);
m_cipher->Transcode(char_cur, cur_size, char_cur, Core::Crypto::Op::Decrypt);
remaining -= cur_size; remaining -= cur_size;
cur += cur_size; cur += cur_size;
AddCounter(ctr.data(), IvSize, 1); AddCounter(ctr.data(), IvSize, 1);
} }
return size; return size;
} }

View file

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -382,11 +379,6 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
if (romfs_dir != nullptr) if (romfs_dir != nullptr)
layers.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(romfs_dir))); layers.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(romfs_dir)));
// Support for romfslite introduced in Atmosphere 1.9.5
auto romfslite_dir = FindSubdirectoryCaseless(subdir, "romfslite");
if (romfslite_dir != nullptr)
layers.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(romfslite_dir)));
auto ext_dir = FindSubdirectoryCaseless(subdir, "romfs_ext"); auto ext_dir = FindSubdirectoryCaseless(subdir, "romfs_ext");
if (ext_dir != nullptr) if (ext_dir != nullptr)
layers_ext.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(ext_dir))); layers_ext.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(ext_dir)));
@ -545,8 +537,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
if (layeredfs) if (layeredfs)
AppendCommaIfNotEmpty(types, "LayeredExeFS"); AppendCommaIfNotEmpty(types, "LayeredExeFS");
} }
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(mod, "romfs")) || if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(mod, "romfs")))
IsDirValidAndNonEmpty(FindSubdirectoryCaseless(mod, "romfslite")))
AppendCommaIfNotEmpty(types, "LayeredFS"); AppendCommaIfNotEmpty(types, "LayeredFS");
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(mod, "cheats"))) if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(mod, "cheats")))
AppendCommaIfNotEmpty(types, "Cheats"); AppendCommaIfNotEmpty(types, "Cheats");
@ -572,8 +563,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs"))) { if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs"))) {
AppendCommaIfNotEmpty(types, "LayeredExeFS"); AppendCommaIfNotEmpty(types, "LayeredExeFS");
} }
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs")) || if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs"))) {
IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfslite"))) {
AppendCommaIfNotEmpty(types, "LayeredFS"); AppendCommaIfNotEmpty(types, "LayeredFS");
} }

View file

@ -509,9 +509,6 @@ std::vector<std::string> ProfileManager::FindOrphanedProfiles()
good_uuids.emplace_back(uuid_string); good_uuids.emplace_back(uuid_string);
} }
// used for acnh, etc
good_uuids.emplace_back("00000000000000000000000000000000");
// TODO: fetch save_id programmatically // TODO: fetch save_id programmatically
const auto path = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) const auto path = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir)
/ "user/save/0000000000000000"; / "user/save/0000000000000000";

View file

@ -134,7 +134,6 @@ void Controller::Initialize() {
break; break;
case ControllerAppletVersion::Version7: case ControllerAppletVersion::Version7:
case ControllerAppletVersion::Version8: case ControllerAppletVersion::Version8:
case ControllerAppletVersion::Version9:
ASSERT(user_arg.size() == sizeof(ControllerSupportArgNew)); ASSERT(user_arg.size() == sizeof(ControllerSupportArgNew));
std::memcpy(&controller_user_arg_new, user_arg.data(), user_arg.size()); std::memcpy(&controller_user_arg_new, user_arg.data(), user_arg.size());
break; break;

View file

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -33,7 +30,6 @@ enum class ControllerAppletVersion : u32_le {
Version5 = 0x5, // 6.0.0 - 7.0.1 Version5 = 0x5, // 6.0.0 - 7.0.1
Version7 = 0x7, // 8.0.0 - 10.2.0 Version7 = 0x7, // 8.0.0 - 10.2.0
Version8 = 0x8, // 11.0.0+ Version8 = 0x8, // 11.0.0+
Version9 = 0x9,
}; };
enum class ControllerSupportMode : u8 { enum class ControllerSupportMode : u8 {

View file

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -9,7 +6,6 @@
#include "common/fs/fs.h" #include "common/fs/fs.h"
#include "common/fs/path_util.h" #include "common/fs/path_util.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/settings.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "core/core.h" #include "core/core.h"
#include "core/file_sys/content_archive.h" #include "core/file_sys/content_archive.h"
@ -236,11 +232,6 @@ WebBrowser::WebBrowser(Core::System& system_, std::shared_ptr<Applet> applet_,
WebBrowser::~WebBrowser() = default; WebBrowser::~WebBrowser() = default;
void WebBrowser::Initialize() { void WebBrowser::Initialize() {
if (Settings::values.disable_web_applet) {
LOG_INFO(Service_AM, "Web Browser Applet disabled, skipping.");
return;
}
FrontendApplet::Initialize(); FrontendApplet::Initialize();
LOG_INFO(Service_AM, "Initializing Web Browser Applet."); LOG_INFO(Service_AM, "Initializing Web Browser Applet.");
@ -304,11 +295,6 @@ void WebBrowser::ExecuteInteractive() {
} }
void WebBrowser::Execute() { void WebBrowser::Execute() {
if (Settings::values.disable_web_applet) {
WebBrowserExit(WebExitReason::EndButtonPressed);
return;
}
switch (web_arg_header.shim_kind) { switch (web_arg_header.shim_kind) {
case ShimKind::Shop: case ShimKind::Shop:
ExecuteShop(); ExecuteShop();

View file

@ -28,7 +28,6 @@ ILibraryAppletAccessor::ILibraryAppletAccessor(Core::System& system_,
{30, D<&ILibraryAppletAccessor::GetResult>, "GetResult"}, {30, D<&ILibraryAppletAccessor::GetResult>, "GetResult"},
{50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"}, {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
{60, D<&ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero>, "PresetLibraryAppletGpuTimeSliceZero"}, {60, D<&ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero>, "PresetLibraryAppletGpuTimeSliceZero"},
{90, D<&ILibraryAppletAccessor::Unknown90>, "Unknown90"},
{100, D<&ILibraryAppletAccessor::PushInData>, "PushInData"}, {100, D<&ILibraryAppletAccessor::PushInData>, "PushInData"},
{101, D<&ILibraryAppletAccessor::PopOutData>, "PopOutData"}, {101, D<&ILibraryAppletAccessor::PopOutData>, "PopOutData"},
{102, nullptr, "PushExtraStorage"}, {102, nullptr, "PushExtraStorage"},
@ -97,11 +96,6 @@ Result ILibraryAppletAccessor::Terminate() {
R_SUCCEED(); R_SUCCEED();
} }
Result ILibraryAppletAccessor::Unknown90() {
LOG_WARNING(Service_AM, "(STUBBED) called");
R_SUCCEED();
}
Result ILibraryAppletAccessor::PushInData(SharedPointer<IStorage> storage) { Result ILibraryAppletAccessor::PushInData(SharedPointer<IStorage> storage) {
LOG_DEBUG(Service_AM, "called"); LOG_DEBUG(Service_AM, "called");
m_broker->GetInData().Push(storage); m_broker->GetInData().Push(storage);

View file

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -29,7 +26,6 @@ private:
Result Start(); Result Start();
Result RequestExit(); Result RequestExit();
Result Terminate(); Result Terminate();
Result Unknown90();
Result PushInData(SharedPointer<IStorage> storage); Result PushInData(SharedPointer<IStorage> storage);
Result PopOutData(Out<SharedPointer<IStorage>> out_storage); Result PopOutData(Out<SharedPointer<IStorage>> out_storage);
Result PushInteractiveInData(SharedPointer<IStorage> storage); Result PushInteractiveInData(SharedPointer<IStorage> storage);

View file

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -178,7 +175,6 @@ ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_, std::shared_
{0, D<&ILibraryAppletCreator::CreateLibraryApplet>, "CreateLibraryApplet"}, {0, D<&ILibraryAppletCreator::CreateLibraryApplet>, "CreateLibraryApplet"},
{1, nullptr, "TerminateAllLibraryApplets"}, {1, nullptr, "TerminateAllLibraryApplets"},
{2, nullptr, "AreAnyLibraryAppletsLeft"}, {2, nullptr, "AreAnyLibraryAppletsLeft"},
{3, D<&ILibraryAppletCreator::CreateLibraryAppletEx>, "CreateLibraryAppletEx"},
{10, D<&ILibraryAppletCreator::CreateStorage>, "CreateStorage"}, {10, D<&ILibraryAppletCreator::CreateStorage>, "CreateStorage"},
{11, D<&ILibraryAppletCreator::CreateTransferMemoryStorage>, "CreateTransferMemoryStorage"}, {11, D<&ILibraryAppletCreator::CreateTransferMemoryStorage>, "CreateTransferMemoryStorage"},
{12, D<&ILibraryAppletCreator::CreateHandleStorage>, "CreateHandleStorage"}, {12, D<&ILibraryAppletCreator::CreateHandleStorage>, "CreateHandleStorage"},
@ -214,32 +210,6 @@ Result ILibraryAppletCreator::CreateLibraryApplet(
R_SUCCEED(); R_SUCCEED();
} }
Result ILibraryAppletCreator::CreateLibraryAppletEx(
Out<SharedPointer<ILibraryAppletAccessor>> out_library_applet_accessor, AppletId applet_id,
LibraryAppletMode library_applet_mode, u64 thread_id) {
LOG_DEBUG(Service_AM, "called with applet_id={} applet_mode={} thread_id={}", applet_id,
library_applet_mode, thread_id);
std::shared_ptr<ILibraryAppletAccessor> library_applet;
if (ShouldCreateGuestApplet(applet_id)) {
library_applet =
CreateGuestApplet(system, m_window_system, m_applet, applet_id, library_applet_mode);
}
if (!library_applet) {
library_applet =
CreateFrontendApplet(system, m_window_system, m_applet, applet_id, library_applet_mode);
}
if (!library_applet) {
LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id);
R_THROW(ResultUnknown);
}
// Applet is created, can now be launched.
m_applet->library_applet_launchable_event.Signal();
*out_library_applet_accessor = library_applet;
R_SUCCEED();
}
Result ILibraryAppletCreator::CreateStorage(Out<SharedPointer<IStorage>> out_storage, s64 size) { Result ILibraryAppletCreator::CreateStorage(Out<SharedPointer<IStorage>> out_storage, s64 size) {
LOG_DEBUG(Service_AM, "called, size={}", size); LOG_DEBUG(Service_AM, "called, size={}", size);

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