Compare commits

..

27 Commits

Author SHA1 Message Date
crueter 58c1e20ee5 Remove bad translations
Signed-off-by: crueter <crueter@eden-emu.dev>
2026-06-01 13:57:41 -04:00
crueter 695f8fd197 [desktop] Increase rlimit on UNIX/Apple (#4030)
Sets max open fd limit to 8192 on non-windows platforms (or bounds it to
the system hard limit). Complement to the previous VFS PR.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4030
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-05-30 22:15:18 -04:00
lizzie 7efb4fc860 [audio_core] fix OOB copy when silencing channel on shutdown (#3969)
when shutting down the emulator will silence any remaining audio from the output buffer
however this is for some reason stored in an array instead of being a simple memset
additionally, said array can be small enough (`frame_size_bytes > silence.size()`) to cause a funky noise to play at the end

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3969
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-05-30 22:13:51 -04:00
Eden CI 7e26c7e5d2 [dist, android] Update translations from Transifex for May 30 (#4028)
Automatic translation update for May 30

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4028
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-05-30 22:13:23 -04:00
MaranBr 2a65389f61 [am] Fix application state notification (#4027)
This fixed issues in Mario Kart 8 multiplayer where the native controller applet would pop up, then once A was pressed and it exited, it would freeze the game but music would continue playing.

The issue was that UpdateRequestedFocusState() updates the focus state but never sets m_has_focus_state_changed for applications. Since ShouldSignalSystemEvent() checks that flag for applications, they never receive FocusStateChanged messages when LLE library applets exit. The game keeps running (hence the music) but is stuck waiting for a focus notification that never arrives. HLE applets aren't affected because their dummy process has is_process_running=false, so the game is never considered obscured in the first place.

Credits: [davidcollini](https://github.com/davidcollini)

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4027
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-05-30 22:13:23 -04:00
MaranBr 67563444d0 [buffer_cache] Fix buffer upload overwriting GPU-modified regions (#4000)
This fixes a bug in Super Mario Odyssey, in Bowser's Kingdom, where particles rapidly freeze and unfreeze in midair.

It also fixes vertex explosions in Super Mario Party Jamboree.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4000
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-05-30 22:05:26 -04:00
crueter c137bf2465 [desktop] Set max stdio limit to 2048 on MSVCRT environments (#4023)
Such as MINGW64.

I legitimately don't have an explanation for this.

Signed-off-by: crueter <crueter@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4023
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
2026-05-30 22:05:25 -04:00
crueter b7d0dcb8b6 [frontend] Fix auto updater flavors on Windows (#4019)
Matches the build ID and compiler now.

Note that this could still use some work on the Windows side of things.
Ideally, it would just replace the executables in place; however, I
think using the setup files will be better.

Most of my concerns w.r.t this issue is that users will want to install
multiple in the same place; however, I think it's fair to just not
support the older versions at all for now. If users really want to do
that, they can use the portable versions and cry about it.

Signed-off-by: crueter <crueter@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4019
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
2026-05-30 22:05:25 -04:00
lizzie a0ddf41425 [video_core] fix std::bitset<> dirty tracker OOB, fix slightly wrong index format estimate (#4006)
u8 may have been 0xff, (aka. 255), but bitset was only 255 elements, so doing bitset[255] is technically OOB

additionally the max size estimate for index formats was not correct, there can be up to 256 elements with a u8 format index, not just 255

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4006
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-05-30 22:05:25 -04:00
lizzie 9dd85234d0 [*] fix missing '#pragma once' on header files (#4014)
`find src -type f -name '*.h' -not -exec grep -q "#pragma once" {} \; -print`

- ignore qt with header guards already
- smaa too

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4014
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-05-30 22:05:25 -04:00
crueter 420db08588 [file_sys] Increase max file limit to 8192 (#4011)
Now hopefully the Redditors can get off my ass for five seconds

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4011
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-05-30 22:05:25 -04:00
Eden CI 967a81b5c4 [dist, android] Update translations from Transifex for May 23 (#4007)
Automatic translation update for May 23

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4007
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-05-30 22:05:25 -04:00
maufeat a83842816e [nvnflinger] Add case for DefaultDataSpace (#3956)
Amaterasu from Discord asked about why nvnWindowGetNumActiveTextures returns 0 - after researching the console output it was asking for `DefaultDataSpace` (enum val 12) after some tests with the mod and debugging the console, this is my one-liner solution.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3956
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-05-30 22:05:25 -04:00
lizzie d83a0d3bf8 [dist] Restore normal icon (#3990)
Anniversary is over!

May the next one be as silly :3

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3990
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-05-30 22:05:25 -04:00
lost-chaos 1489f7f28d [core/arm/nce] suppress unused variable warnings (#4002)
Add `[[maybe_unused]]` to unused constants in the ARM NCE backend to suppress compiler warnings.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4002
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-05-30 22:05:25 -04:00
xbzk 5976519344 [net] ban api.epicgames.dev domain (#3999)
Adding 'api.epicgames.dev' to the banlist, as instructed by lizzie.

MCI insists on connection and randomly crashes at Network::Poll(...).
Airplane mode would do, but then the DLC gets unavailable.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3999
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
2026-05-30 22:05:25 -04:00
lizzie dc09c8d98e [tools] refactor, use #!/bin/sh, update license files (#3998)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3998
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-05-30 22:05:25 -04:00
lizzie c61ae6a91f [net] support SCTP protocols (+all base FBSD protos), ban more NS domains (#3955)
bans more NS domains making hb dns check pass

also fixes bogus "SCTP is not implemented" assertion for translate protocol

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3955
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-05-30 22:05:25 -04:00
lizzie 8541f5a52b [common/virtual_buffer] fix technically wrong usage of std::exchange() (#3992)
simpleish fix https://en.cppreference.com/cpp/utility/exchange

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3992
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-05-30 22:05:25 -04:00
lizzie 001faff20c [core/hle/services/sockets] allow 'valid' range from [16,255] for IPv4 (#3491)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3491
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-05-30 22:05:12 -04:00
lizzie db1946a6fa [core/memory] remove duplicates from template when unsafe=T/F, remove increment, remove sad path for exception code on cache op (#3972)
Ideally this should increase perf when doing write/read operations
not by a lot, just enough for it not to suck so much as it does right now

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3972
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-05-30 22:05:12 -04:00
Marcin Serwin 0d0a29e1fc [dynarmic] drop Xeon Phi features from tests (#3978)
xbyak 7.37 dropped support for them, see
<https://github.com/herumi/xbyak/commit/a33007c908022b46b1a982afe649ded73a15219e>
and <https://github.com/herumi/xbyak/pull/244>.

Signed-off-by: Marcin Serwin <marcin@serwin.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3978
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
2026-05-30 22:05:12 -04:00
lizzie 5492647479 [dynarmic] fix NetBSD context.h compile error (#3966)
On NetBSD it stupidly aligns the stack acounting for the amd64 redzone.

This isn't... required, to put it lightly.

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3966
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-05-30 22:05:12 -04:00
crueter a2a569ed5f [externals] Remove httplib minimum version spec (#3967)
For some Godforsaken reason it uses SameMinorVersion.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3967
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-05-30 22:05:12 -04:00
Eden CI c9842ed42c [dist, android] Update translations from Transifex for May 16 (#3960)
Automatic translation update for May 16

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3960
2026-05-30 22:05:12 -04:00
lizzie b4e846f382 [hid_core] remove contentious mutex from EmulatedController and just rely on atomic semantics for fields (#3866)
inputs shouldnt be that critical to require a full mutex of them

this relies on CPU guaranteeing u32/u16/u8 atomic load/stores for EmulatedController fields, which works on x86_64 but may not have the same behaviour on other architectures - thats why i wrap them in `std::atomic<>`

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3866
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-05-30 22:02:45 -04:00
lizzie efdf57890f [dynarmic] fix 12th-gen Intel CPUs crashing due to UMONITOR (#3954)
see https://github.com/herumi/xbyak/issues/255

> Proof: https://godbolt.org/z/9vseq4Ynj
> Xbyak currently implements it as:
> ```c++
> void umonitor(const Reg& r) {
> int idx = r.getIdx();
> if (idx > 7) XBYAK_THROW(ERR_BAD_PARAMETER) //umonitor DOES accept r8,r9,r10,etc this is NOT correct
> int bit = r.getBit();
> if (BIT != bit) {
>   if ((BIT == 32 && bit == 16) || (BIT == 64 && bit == 32)) {
>     db(0x67);
>   } else {
>     XBYAK_THROW(ERR_BAD_SIZE_OF_REGISTER)
>   }
> }
> db(0xF3); db(0x0F); db(0xAE); setModRM(3, 6, idx);
> }
> ```
> My program was throwing Xbyak::Exception and I tracked it down to this particular umonitor

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3954
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-05-30 22:02:45 -04:00
277 changed files with 5547 additions and 11927 deletions
+1 -2
View File
@@ -37,7 +37,6 @@ jobs:
- name: Create PR
run: |
set -x
DATE=$(date +"%b %d")
TITLE="[dist, android] Update translations from Transifex for $DATE"
BODY="Automatic translation update for $DATE"
@@ -53,7 +52,7 @@ jobs:
}
EOF
curl -XPOST \
curl -X 'POST' \
'https://git.eden-emu.dev/api/v1/repos/eden-emu/eden/pulls' \
-H 'accept: application/json' \
-H 'Authorization: Bearer ${{ secrets.CI_FJ_TOKEN }}' \
+1 -3
View File
@@ -24,11 +24,9 @@ jobs:
git remote set-url origin ci:eden-emu/eden.git
DATE=$(date +"%b %d")
TIMESTAMP=$(date +"%s")
echo "DATE=$DATE" >> "$GITHUB_ENV"
echo "TIMESTAMP=$TIMESTAMP" >> "$GITHUB_ENV"
git switch -c update-deps-$TIMESTAMP
git switch -c update-deps-$DATE
tools/cpmutil.sh package update -ac
git push
@@ -1,214 +0,0 @@
From ec4c1fdf526cb9ad045abf59b29ee495bbf5023a Mon Sep 17 00:00:00 2001
From: crueter <crueter@eden-emu.dev>
Date: Sat, 30 May 2026 20:56:35 -0400
Subject: [PATCH] cpmutil compat
---
CMakeLists.txt | 31 ++++++++-----------
cmake/FetchOpenSSL.cmake | 64 ----------------------------------------
cmake/GetCPM.cmake | 5 ----
3 files changed, 13 insertions(+), 87 deletions(-)
delete mode 100644 cmake/FetchOpenSSL.cmake
delete mode 100644 cmake/GetCPM.cmake
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5420ecc..9ffd5a0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -19,9 +19,7 @@ include(FetchContent)
include(ProcessorCount)
include(cmake/ConfigureOpenSSL.cmake)
include(cmake/DetectTargetPlatform.cmake)
-include(cmake/FetchOpenSSL.cmake)
include(cmake/FindVcvarsall.cmake)
-include(cmake/GetCPM.cmake)
# Custom options
option(OPENSSL_BUILD_VERBOSE "Enable verbose output from build" OFF)
@@ -47,9 +45,6 @@ if("${OPENSSL_TARGET_PLATFORM}" STREQUAL "")
detect_target_platform(OPENSSL_TARGET_PLATFORM)
endif()
-# Fetch OpenSSL source
-fetch_openssl()
-
# Apply patches
foreach(patch IN LISTS OPENSSL_PATCH)
if(EXISTS "${patch}" AND NOT IS_DIRECTORY "${patch}")
@@ -59,13 +54,13 @@ foreach(patch IN LISTS OPENSSL_PATCH)
execute_process(
COMMAND git init
- WORKING_DIRECTORY ${openssl_SOURCE_DIR}
+ WORKING_DIRECTORY ${OpenSSL_SOURCE_DIR}
OUTPUT_QUIET
ERROR_QUIET
)
execute_process(
COMMAND git apply ${patch}
- WORKING_DIRECTORY ${openssl_SOURCE_DIR}
+ WORKING_DIRECTORY ${OpenSSL_SOURCE_DIR}
OUTPUT_QUIET
ERROR_QUIET
)
@@ -161,8 +156,8 @@ list(PREPEND OPENSSL_CONFIGURE_OPTIONS ${OPENSSL_TARGET_PLATFORM})
# Configure OpenSSL
configure_openssl(
COMMAND ${VCVARSALL_COMMAND}
- FILE ${openssl_SOURCE_DIR}/Configure
- BUILD_DIR ${openssl_BINARY_DIR}
+ FILE ${OpenSSL_SOURCE_DIR}/Configure
+ BUILD_DIR ${OpenSSL_BINARY_DIR}
OPTIONS ${OPENSSL_CONFIGURE_OPTIONS}
)
@@ -203,8 +198,8 @@ endif()
# Parse Makefile
parse_makefile(${OPENSSL_MAKEFILE} "INSTALL_LIBS" OPENSSL_STATIC_LIBS)
parse_makefile(${OPENSSL_MAKEFILE} "INSTALL_SHLIBS" OPENSSL_SHARED_LIBS)
-list(TRANSFORM OPENSSL_STATIC_LIBS PREPEND "${openssl_BINARY_DIR}/")
-list(TRANSFORM OPENSSL_SHARED_LIBS PREPEND "${openssl_BINARY_DIR}/")
+list(TRANSFORM OPENSSL_STATIC_LIBS PREPEND "${OpenSSL_BINARY_DIR}/")
+list(TRANSFORM OPENSSL_SHARED_LIBS PREPEND "${OpenSSL_BINARY_DIR}/")
foreach(LIBRARY IN LISTS OPENSSL_STATIC_LIBS)
if(LIBRARY MATCHES "crypto")
@@ -239,14 +234,14 @@ endif()
# Provide same targets and variables as FindOpenSSL module
set(OPENSSL_FOUND ON CACHE BOOL "Override FindOpenSSL variables" FORCE)
-set(OPENSSL_INCLUDE_DIR ${openssl_SOURCE_DIR}/include ${openssl_BINARY_DIR}/include CACHE STRING "Override FindOpenSSL variables" FORCE)
+set(OPENSSL_INCLUDE_DIR ${OpenSSL_SOURCE_DIR}/include ${OpenSSL_BINARY_DIR}/include CACHE STRING "Override FindOpenSSL variables" FORCE)
set(OPENSSL_CRYPTO_LIBRARY ${OPENSSL_${OPENSSL_LIBRARY_TYPE}_CRYPTO_LIBRARY} CACHE STRING "Override FindOpenSSL variables" FORCE)
set(OPENSSL_CRYPTO_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY} ${OPENSSL_DEPENDENCIES} CACHE STRING "Override FindOpenSSL variables" FORCE)
set(OPENSSL_SSL_LIBRARY ${OPENSSL_${OPENSSL_LIBRARY_TYPE}_SSL_LIBRARY} CACHE STRING "Override FindOpenSSL variables" FORCE)
set(OPENSSL_SSL_LIBRARIES ${OPENSSL_SSL_LIBRARY} ${OPENSSL_DEPENDENCIES} CACHE STRING "Override FindOpenSSL variables" FORCE)
set(OPENSSL_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY} ${OPENSSL_SSL_LIBRARY} ${OPENSSL_DEPENDENCIES} CACHE STRING "Override FindOpenSSL variables" FORCE)
set(OPENSSL_VERSION ${OPENSSL_CONFIGURED_VERSION} CACHE STRING "Override FindOpenSSL variables" FORCE)
-set(OPENSSL_APPLINK_SOURCE ${openssl_SOURCE_DIR}/ms/applink.c CACHE STRING "Override FindOpenSSL variables" FORCE)
+set(OPENSSL_APPLINK_SOURCE ${OpenSSL_SOURCE_DIR}/ms/applink.c CACHE STRING "Override FindOpenSSL variables" FORCE)
add_library(OpenSSL::Crypto ${OPENSSL_LIBRARY_TYPE} IMPORTED GLOBAL)
add_library(OpenSSL::SSL ${OPENSSL_LIBRARY_TYPE} IMPORTED GLOBAL)
@@ -308,8 +303,8 @@ if(ANDROID)
endif()
file(GLOB_RECURSE OPENSSL_SOURCES
- ${openssl_SOURCE_DIR}/*.[ch]
- ${openssl_SOURCE_DIR}/*.[ch].in
+ ${OpenSSL_SOURCE_DIR}/*.[ch]
+ ${OpenSSL_SOURCE_DIR}/*.[ch].in
)
set(OPENSSL_BUILD_OUTPUT
@@ -322,7 +317,7 @@ add_custom_command(
OUTPUT ${OPENSSL_BUILD_OUTPUT}
COMMAND ${OPENSSL_BUILD_COMMAND}
DEPENDS ${OPENSSL_SOURCES}
- WORKING_DIRECTORY ${openssl_BINARY_DIR}
+ WORKING_DIRECTORY ${OpenSSL_BINARY_DIR}
VERBATIM
)
@@ -341,7 +336,7 @@ if(OPENSSL_TEST AND NOT CMAKE_CROSSCOMPILING)
add_test(
NAME openssl-test
COMMAND ${OPENSSL_BUILD_TOOL} test VERBOSE_FAILURE=yes HARNESS_JOBS=${NUMBER_OF_THREADS}
- WORKING_DIRECTORY ${openssl_BINARY_DIR}
+ WORKING_DIRECTORY ${OpenSSL_BINARY_DIR}
)
endif()
@@ -356,7 +351,7 @@ if(OPENSSL_INSTALL)
install(CODE
"execute_process(
COMMAND ${OPENSSL_INSTALL_COMMAND}
- WORKING_DIRECTORY \"${openssl_BINARY_DIR}\"
+ WORKING_DIRECTORY \"${OpenSSL_BINARY_DIR}\"
)"
)
endif()
diff --git a/cmake/FetchOpenSSL.cmake b/cmake/FetchOpenSSL.cmake
deleted file mode 100644
index a43505d..0000000
--- a/cmake/FetchOpenSSL.cmake
+++ /dev/null
@@ -1,64 +0,0 @@
-function(fetch_openssl)
- if(EXISTS "${OPENSSL_SOURCE}" AND IS_DIRECTORY "${OPENSSL_SOURCE}")
- # Fetch the local OpenSSL source
- if(NOT IS_ABSOLUTE "${OPENSSL_SOURCE}")
- string(PREPEND OPENSSL_SOURCE ${CMAKE_SOURCE_DIR}/)
- endif()
-
- string(REPLACE "\\" "/" openssl-source_SOURCE_DIR "${OPENSSL_SOURCE}")
- set(openssl-source_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/openssl-source-build)
- else()
- set(CPM_OPTIONS
- NAME openssl-source
- DOWNLOAD_ONLY ON
- )
-
- if(NOT OPENSSL_CONFIGURE_VERBOSE)
- list(APPEND CPM_OPTIONS QUIET)
- endif()
-
- if("${OPENSSL_SOURCE}" MATCHES "^http")
- # Download OpenSSL source from the internet
- list(APPEND CPM_OPTIONS URL ${OPENSSL_SOURCE})
- else()
- # Download OpenSSL source from the official website
- if("${OPENSSL_TARGET_VERSION}" STREQUAL "")
- set(OPENSSL_TARGET_VERSION ${PROJECT_VERSION})
- endif()
-
- if(OPENSSL_TARGET_VERSION VERSION_EQUAL PROJECT_VERSION)
- list(APPEND CPM_OPTIONS URL_HASH SHA256=aaf51a1fe064384f811daeaeb4ec4dce7340ec8bd893027eee676af31e83a04f)
- endif()
-
- if(OPENSSL_TARGET_VERSION MATCHES "^1\.1\.1[a-w]$")
- string(REPLACE "." "_" OPENSSL_TAGGED_VERSION ${OPENSSL_TARGET_VERSION})
- list(APPEND CPM_OPTIONS URL https://github.com/openssl/openssl/releases/download/OpenSSL_${OPENSSL_TAGGED_VERSION}/openssl-${OPENSSL_TARGET_VERSION}.tar.gz)
- else()
- list(APPEND CPM_OPTIONS URL https://github.com/openssl/openssl/releases/download/openssl-${OPENSSL_TARGET_VERSION}/openssl-${OPENSSL_TARGET_VERSION}.tar.gz)
- endif()
- endif()
-
- CPMAddPackage(${CPM_OPTIONS})
- endif()
-
- # Clean build directory if source directory has changed
- if(DEFINED CACHE{openssl-source_SOURCE_DIR_OLD} AND NOT openssl-source_SOURCE_DIR STREQUAL openssl-source_SOURCE_DIR_OLD)
- set(openssl-source_SOURCE_DIR_OLD ${openssl-source_SOURCE_DIR} CACHE INTERNAL "Previously fetched OpenSSL source")
-
- if(IS_DIRECTORY ${openssl-source_BINARY_DIR})
- file(REMOVE_RECURSE ${openssl-source_BINARY_DIR})
- file(MAKE_DIRECTORY ${openssl-source_BINARY_DIR})
- endif()
- endif()
-
- # Override the FindOpenSSL module
- FetchContent_Declare(
- OpenSSL
- SOURCE_DIR ${openssl-source_SOURCE_DIR}
- BINARY_DIR ${openssl-source_BINARY_DIR}
- OVERRIDE_FIND_PACKAGE
- )
- FetchContent_MakeAvailable(OpenSSL)
-
- return(PROPAGATE openssl_SOURCE_DIR openssl_BINARY_DIR)
-endfunction()
diff --git a/cmake/GetCPM.cmake b/cmake/GetCPM.cmake
deleted file mode 100644
index bfc50f5..0000000
--- a/cmake/GetCPM.cmake
+++ /dev/null
@@ -1,5 +0,0 @@
-file(
- DOWNLOAD https://github.com/cpm-cmake/CPM.cmake/releases/latest/download/get_cpm.cmake
- ${CMAKE_CURRENT_BINARY_DIR}/get_cpm.cmake
-)
-include(${CMAKE_CURRENT_BINARY_DIR}/get_cpm.cmake)
--
2.54.0
@@ -1,54 +0,0 @@
From d46675fbb61eb6d51e478023ce4075e545ad4cfd Mon Sep 17 00:00:00 2001
From: crueter <crueter@eden-emu.dev>
Date: Sat, 30 May 2026 21:11:55 -0400
Subject: [PATCH] use ccache
---
CMakeLists.txt | 1 -
cmake/ConfigureOpenSSL.cmake | 12 +++---------
2 files changed, 3 insertions(+), 10 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9ffd5a0..9ff14c8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -28,7 +28,6 @@ option(OPENSSL_ENABLE_PARALLEL "Build and test in parallel if possible" ON)
option(OPENSSL_INSTALL "Install OpenSSL components to the <prefix> directory" OFF)
option(OPENSSL_INSTALL_CERT "Install cert.pem to the <openssldir> directory" OFF)
option(OPENSSL_TEST "Enable testing and build OpenSSL self tests" OFF)
-option(OPENSSL_USE_CCACHE "Use ccache if available" ON)
if("${OPENSSL_BUILD_TARGET}" STREQUAL "")
# Makefile target for build
diff --git a/cmake/ConfigureOpenSSL.cmake b/cmake/ConfigureOpenSSL.cmake
index 211c18b..3d8cbed 100644
--- a/cmake/ConfigureOpenSSL.cmake
+++ b/cmake/ConfigureOpenSSL.cmake
@@ -69,15 +69,9 @@ function(apply_ccache FILE)
message(FATAL_ERROR "Couldn't find Makefile")
endif()
- if(OPENSSL_USE_CCACHE)
- find_program(CCACHE ccache)
-
- if(NOT CCACHE)
- return()
- endif()
-
+ if(USE_CCACHE)
file(READ ${FILE} MAKEFILE)
- string(REPLACE "\nCC=" "\nCC=ccache " MAKEFILE "${MAKEFILE}")
+ string(REPLACE "\nCC=" "\nCC=${CCACHE_BINARY} " MAKEFILE "${MAKEFILE}")
if(MSVC)
string(REPLACE "/Zi /Fdossl_static.pdb " "" MAKEFILE "${MAKEFILE}")
@@ -171,4 +165,4 @@ function(configure_openssl)
string(REPLACE "/W3" "/W0" MAKEFILE "${MAKEFILE}")
file(WRITE ${OPENSSL_MAKEFILE} "${MAKEFILE}")
endif()
-endfunction()
\ No newline at end of file
+endfunction()
--
2.54.0
@@ -1,28 +0,0 @@
From 4a3cc92a7abad403529ed1cb4255ca63d9252de4 Mon Sep 17 00:00:00 2001
From: crueter <crueter@eden-emu.dev>
Date: Sat, 30 May 2026 21:48:42 -0400
Subject: [PATCH 2/2] use cmake compiler flags
---
cmake/ConfigureOpenSSL.cmake | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/cmake/ConfigureOpenSSL.cmake b/cmake/ConfigureOpenSSL.cmake
index 3d8cbed..3012e05 100644
--- a/cmake/ConfigureOpenSSL.cmake
+++ b/cmake/ConfigureOpenSSL.cmake
@@ -135,7 +135,10 @@ function(configure_openssl)
endif()
execute_process(
- COMMAND ${CONFIGURE_COMMAND}
+ COMMAND ${CMAKE_COMMAND} -E env
+ "CFLAGS=${CMAKE_C_FLAGS}"
+ "CXXFLAGS=${CMAKE_CXX_FLAGS}"
+ ${CONFIGURE_COMMAND}
WORKING_DIRECTORY ${CONFIGURE_BUILD_DIR}
${VERBOSE_OPTION}
COMMAND_ERROR_IS_FATAL ANY
--
2.54.0
File diff suppressed because it is too large Load Diff
+35 -45
View File
@@ -75,8 +75,6 @@ cmake_dependent_option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet im
set(YUZU_QT_MIRROR "" CACHE STRING "What mirror to use for downloading the bundled Qt libraries")
cmake_dependent_option(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSVC}" "ENABLE_QT" OFF)
option(ENABLE_DEBUG_TOOLS "Enable debugging tools (maxwell disassembler, SPIRV translator, etc)" OFF)
# non-linux bundled qt are static
if (YUZU_USE_BUNDLED_QT AND (APPLE OR NOT UNIX))
set(YUZU_STATIC_BUILD ON)
@@ -121,13 +119,13 @@ if (YUZU_STATIC_BUILD)
set(QuaZip-Qt6_FORCE_BUNDLED ON)
set(YUZU_USE_BUNDLED_FFMPEG ON)
set(YUZU_USE_BUNDLED_SDL3 ON)
set(YUZU_USE_BUNDLED_SDL2 ON)
set(YUZU_USE_BUNDLED_OPENSSL ON)
set(HTTPLIB_USE_BROTLI_IF_AVAILABLE OFF)
elseif(APPLE)
set(YUZU_USE_BUNDLED_FFMPEG ON)
set(YUZU_USE_BUNDLED_SDL3 ON)
set(YUZU_USE_BUNDLED_SDL2 ON)
set(YUZU_USE_BUNDLED_OPENSSL ON)
# these libs do not properly provide static libs/let you do it with cmake
@@ -192,11 +190,13 @@ if(MSVC)
$<$<COMPILE_LANGUAGE:C,CXX>:$<$<CONFIG:Debug>:/${libflag}d>>
$<$<COMPILE_LANGUAGE:C,CXX>:$<$<CONFIG:Release>:/${libflag}>>
$<$<COMPILE_LANGUAGE:C,CXX>:$<$<CONFIG:RelWithDebInfo>:/${libflag}>>
$<$<COMPILE_LANGUAGE:C,CXX>:$<$<CONFIG:MinSizeRel>:/${libflag}>>)
$<$<COMPILE_LANGUAGE:C,CXX>:$<$<CONFIG:MinSizeRel>:/${libflag}>>
)
endif()
# TODO(crueter): Cleanup, each dep that has a bundled option should allow to choose between bundled, external, system
cmake_dependent_option(YUZU_USE_BUNDLED_SDL3 "Download bundled SDL3 build" "${MSVC}" "NOT ANDROID" OFF)
cmake_dependent_option(YUZU_USE_EXTERNAL_SDL2 "Build SDL2 from external source" OFF "NOT MSVC;NOT ANDROID" OFF)
cmake_dependent_option(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 build" "${MSVC}" "NOT ANDROID" OFF)
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
@@ -230,10 +230,6 @@ cmake_dependent_option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF
option(YUZU_TESTS "Compile tests" "${BUILD_TESTING}")
# Install udev rules on Linux (mainly for gyros)
# Only acts on joysticks and nothing else.
cmake_dependent_option(YUZU_INSTALL_UDEV_RULES "Install udev rules for gyro access" OFF "PLATFORM_LINUX" OFF)
option(YUZU_DOWNLOAD_ANDROID_VVL "Download validation layer binary for android" ON)
option(YUZU_LEGACY "Apply patches that improve compatibility with older GPUs (e.g. Snapdragon 865) at the cost of performance" OFF)
@@ -362,6 +358,10 @@ if (CXX_GCC OR CXX_CLANG)
endif()
endif()
# Other presets, e.g. steamdeck
# TODO(crueter): Just have every Linux/Windows use old sdl2
set(YUZU_SYSTEM_PROFILE "generic" CACHE STRING "CMake and Externals profile to use. One of: generic, steamdeck")
# Configure C++ standard
# ===========================
@@ -383,20 +383,13 @@ find_package(RenderDoc MODULE)
# openssl funniness
if (YUZU_USE_BUNDLED_OPENSSL)
set(BUILD_SHARED_LIBS OFF)
AddJsonPackage(openssl-ci)
else()
AddJsonPackage(openssl)
set(OPENSSL_BUILD_VERBOSE ON)
set(OPENSSL_CONFIGURE_VERBOSE ON)
if (OpenSSL_ADDED)
AddJsonPackage(openssl-cmake)
add_compile_definitions(YUZU_BUNDLED_OPENSSL)
endif()
endif()
if (OpenSSL_ADDED)
add_compile_definitions(YUZU_BUNDLED_OPENSSL)
endif()
find_package(OpenSSL 3 REQUIRED)
message(STATUS "Fetching needed dependencies with CPM")
@@ -566,7 +559,7 @@ if (NOT YUZU_STATIC_ROOM)
endif()
if (NOT ANDROID)
find_package(SDL3)
find_package(SDL2)
endif()
if (USE_DISCORD_PRESENCE)
@@ -591,6 +584,8 @@ if (ENABLE_QT)
else()
AddQt(6.9.3)
endif()
set(YUZU_STATIC_BUILD ON)
else()
message(STATUS "Using system Qt")
if (NOT Qt6_DIR)
@@ -599,7 +594,23 @@ if (ENABLE_QT)
list(APPEND CMAKE_PREFIX_PATH "${Qt6_DIR}")
endif()
find_package(Qt6 REQUIRED COMPONENTS Core)
find_package(Qt6 CONFIG REQUIRED COMPONENTS Widgets Charts Concurrent)
if (YUZU_USE_QT_MULTIMEDIA)
find_package(Qt6 REQUIRED COMPONENTS Multimedia)
endif()
if (PLATFORM_LINUX OR PLATFORM_FREEBSD)
# yes Qt, we get it
set(QT_NO_PRIVATE_MODULE_WARNING ON)
find_package(Qt6 REQUIRED COMPONENTS DBus OPTIONAL_COMPONENTS GuiPrivate)
elseif (UNIX AND NOT APPLE)
find_package(Qt6 REQUIRED COMPONENTS DBus Gui)
endif()
if (ENABLE_QT_TRANSLATION)
find_package(Qt6 REQUIRED COMPONENTS LinguistTools)
endif()
if (NOT DEFINED QT_TARGET_PATH)
get_target_property(qtcore_path Qt6::Core LOCATION_Release)
@@ -622,27 +633,21 @@ if (ENABLE_QT)
## Components ##
# Best practice is to ask for all components at once, so they are from the same version
set(YUZU_QT_COMPONENTS Core Widgets Charts Concurrent Gui)
if (PLATFORM_LINUX OR PLATFORM_FREEBSD)
set(YUZU_QT_COMPONENTS Core Widgets Charts Concurrent)
if (PLATFORM_LINUX)
list(APPEND YUZU_QT_COMPONENTS DBus)
# yes Qt, we get it
set(QT_NO_PRIVATE_MODULE_WARNING ON)
list(APPEND YUZU_QT_OPTIONAL GuiPrivate)
endif()
if (YUZU_USE_QT_MULTIMEDIA)
list(APPEND YUZU_QT_COMPONENTS Multimedia)
endif()
if (YUZU_USE_QT_WEB_ENGINE)
list(APPEND YUZU_QT_COMPONENTS WebEngineCore WebEngineWidgets)
endif()
if (ENABLE_QT_TRANSLATION)
list(APPEND YUZU_QT_COMPONENTS LinguistTools)
endif()
find_package(Qt6 REQUIRED COMPONENTS ${YUZU_QT_COMPONENTS} OPTIONAL_COMPONENTS ${YUZU_QT_OPTIONAL})
find_package(Qt6 REQUIRED COMPONENTS ${YUZU_QT_COMPONENTS})
set(QT_MAJOR_VERSION 6)
# Qt6 sets cxx_std_17 and we need to undo that
set_target_properties(Qt6::Platform PROPERTIES INTERFACE_COMPILE_FEATURES "")
@@ -710,12 +715,6 @@ endif()
add_subdirectory(src)
if (ENABLE_DEBUG_TOOLS)
add_subdirectory(tools/maxwell-disas)
add_subdirectory(tools/maxwell-spirv)
add_subdirectory(tools/maxwell-ir)
endif()
# Set yuzu project or yuzu-cmd project as default StartUp Project in Visual Studio depending on whether QT is enabled or not
if(ENABLE_QT)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yuzu)
@@ -731,8 +730,6 @@ endif()
# https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
# https://specifications.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html
# https://www.freedesktop.org/software/appstream/docs/
# TODO: Icon/install handling n such should be put into dist/CMakeLists.txt
if(ENABLE_QT AND UNIX AND NOT APPLE)
install(FILES "dist/dev.eden_emu.eden.desktop"
DESTINATION "share/applications")
@@ -745,10 +742,3 @@ if(ENABLE_QT AND UNIX AND NOT APPLE)
install(FILES "dist/dev.eden_emu.eden.metainfo.xml"
DESTINATION "share/metainfo")
endif()
if (YUZU_INSTALL_UDEV_RULES)
include(GNUInstallDirs)
install(FILES "dist/72-eden-input.rules"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/udev/rules.d")
endif()
+31 -56
View File
@@ -3,7 +3,7 @@
set(CPM_SOURCE_CACHE "${PROJECT_SOURCE_DIR}/.cache/cpm" CACHE STRING "" FORCE)
if(MSVC OR ANDROID OR IOS)
if(MSVC OR ANDROID)
set(BUNDLED_DEFAULT ON)
else()
set(BUNDLED_DEFAULT OFF)
@@ -128,8 +128,7 @@ function(AddDependentPackages)
message(FATAL_ERROR "Partial dependency installation detected "
"for the following packages:\n${package_names}\n"
"You can solve this in one of two ways:\n"
"1. Install or upgrade the following packages "
"to your system if available:"
"1. Install the following packages to your system if available:"
"\n\t${bundled_names}\n"
"2. Set the following variables to ON:"
"\n\t${system_names}\n"
@@ -248,9 +247,7 @@ function(AddJsonPackage)
set(multiValueArgs OPTIONS)
set(optionArgs MODULE)
cmake_parse_arguments(JSON "${optionArgs}" "${oneValueArgs}" "${multiValueArgs}"
cmake_parse_arguments(JSON "" "${oneValueArgs}" "${multiValueArgs}"
"${ARGN}")
list(LENGTH ARGN argnLength)
@@ -279,10 +276,6 @@ function(AddJsonPackage)
parse_object(${object})
if (JSON_MODULE)
set(EXTRA_ARGS MODULE)
endif()
if(ci)
AddCIPackage(
VERSION ${version}
@@ -291,8 +284,8 @@ function(AddJsonPackage)
PACKAGE ${package}
EXTENSION ${extension}
MIN_VERSION ${min_version}
DISABLED_PLATFORMS ${disabled_platforms}
${EXTRA_ARGS})
DISABLED_PLATFORMS ${disabled_platforms})
else()
if (NOT DEFINED JSON_FORCE_BUNDLED_PACKAGE)
set(JSON_FORCE_BUNDLED_PACKAGE OFF)
@@ -314,24 +307,23 @@ function(AddJsonPackage)
FORCE_BUNDLED_PACKAGE "${JSON_FORCE_BUNDLED_PACKAGE}"
SOURCE_SUBDIR "${source_subdir}"
GIT_VERSION "${git_version}"
GIT_HOST "${git_host}"
GIT_VERSION ${git_version}
GIT_HOST ${git_host}
ARTIFACT "${artifact}"
TAG "${tag}"
${EXTRA_ARGS})
ARTIFACT ${artifact}
TAG ${tag})
endif()
# pass stuff to parent scope
Propagate(${package}_ADDED)
Propagate(${package}_SOURCE_DIR)
Propagate(${package}_BINARY_DIR)
Propagate(CMAKE_PREFIX_PATH)
endfunction()
function(AddPackage)
cpm_set_policies()
set(EXTRA_ARGS "")
# TODO(crueter): git clone?
#[[
URL configurations, descending order of precedence:
@@ -376,9 +368,7 @@ function(AddPackage)
set(multiValueArgs OPTIONS PATCHES)
set(optionArgs MODULE)
cmake_parse_arguments(PKG_ARGS "${optionArgs}" "${oneValueArgs}" "${multiValueArgs}"
cmake_parse_arguments(PKG_ARGS "" "${oneValueArgs}" "${multiValueArgs}"
"${ARGN}")
if(NOT DEFINED PKG_ARGS_NAME)
@@ -562,12 +552,6 @@ function(AddPackage)
VERSION ${PKG_ARGS_VERSION})
endif()
if (PKG_ARGS_MODULE)
set(PKG_ARGS_DOWNLOAD_ONLY ON)
elseif (NOT DEFINED PKG_ARGS_DOWNLOAD_ONLY)
set(PKG_ARGS_DOWNLOAD_ONLY OFF)
endif()
CPMAddPackage(
NAME ${PKG_ARGS_NAME}
URL ${pkg_url}
@@ -617,14 +601,13 @@ function(AddPackage)
endif()
# pass stuff to parent scope
Propagate(${PKG_ARGS_NAME}_ADDED)
Propagate(${PKG_ARGS_NAME}_SOURCE_DIR)
Propagate(${PKG_ARGS_NAME}_BINARY_DIR)
set(${PKG_ARGS_NAME}_ADDED "${${PKG_ARGS_NAME}_ADDED}"
PARENT_SCOPE)
set(${PKG_ARGS_NAME}_SOURCE_DIR "${${PKG_ARGS_NAME}_SOURCE_DIR}"
PARENT_SCOPE)
set(${PKG_ARGS_NAME}_BINARY_DIR "${${PKG_ARGS_NAME}_BINARY_DIR}"
PARENT_SCOPE)
if (PKG_ARGS_MODULE)
list(PREPEND CMAKE_PREFIX_PATH "${${ARTIFACT_PACKAGE}_SOURCE_DIR}")
Propagate(CMAKE_PREFIX_PATH)
endif()
endfunction()
# TODO(crueter): we could do an AddMultiArchPackage, multiplatformpackage?
@@ -707,20 +690,14 @@ function(AddCIPackage)
set(pkgname linux-amd64)
elseif(PLATFORM_LINUX AND ARCHITECTURE_arm64)
set(pkgname linux-aarch64)
elseif(APPLE AND NOT IOS)
elseif(APPLE)
set(pkgname macos-universal)
elseif(IOS AND ARCHITECTURE_arm64)
set(pkgname ios-aarch64)
endif()
if (DEFINED pkgname AND NOT "${pkgname}" IN_LIST DISABLED_PLATFORMS)
set(ARTIFACT
"${ARTIFACT_NAME}-${pkgname}-${ARTIFACT_VERSION}.${ARTIFACT_EXT}")
if (PKG_ARGS_MODULE)
set(EXTRA_ARGS MODULE)
endif()
AddPackage(
NAME ${ARTIFACT_PACKAGE}
REPO ${ARTIFACT_REPO}
@@ -731,39 +708,37 @@ function(AddCIPackage)
KEY "${pkgname}-${ARTIFACT_VERSION}"
HASH_SUFFIX sha512sum
FORCE_BUNDLED_PACKAGE ON
${EXTRA_ARGS})
DOWNLOAD_ONLY ${PKG_ARGS_MODULE})
set(${ARTIFACT_PACKAGE}_ADDED TRUE PARENT_SCOPE)
Propagate(${ARTIFACT_PACKAGE}_SOURCE_DIR)
Propagate(CMAKE_PREFIX_PATH)
set(${ARTIFACT_PACKAGE}_SOURCE_DIR
"${${ARTIFACT_PACKAGE}_SOURCE_DIR}" PARENT_SCOPE)
if (PKG_ARGS_MODULE)
list(APPEND CMAKE_PREFIX_PATH "${${ARTIFACT_PACKAGE}_SOURCE_DIR}")
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE)
endif()
else()
find_package(${ARTIFACT_PACKAGE} ${ARTIFACT_MIN_VERSION} REQUIRED)
endif()
endfunction()
# Utility function for Qt
function(AddQt repo version)
if (NOT DEFINED repo)
message(FATAL_ERROR "[CPMUtil] AddQt: repo is required")
endif()
function(AddQt version)
if (NOT DEFINED version)
message(FATAL_ERROR "[CPMUtil] AddQt: version is required")
endif()
AddCIPackage(
NAME qt
NAME Qt
PACKAGE Qt6
VERSION ${version}
MIN_VERSION 6
REPO ${repo}
REPO crueter-ci/Qt
DISABLED_PLATFORMS
android-x86_64 android-aarch64
freebsd-amd64 solaris-amd64 openbsd-amd64
MODULE)
find_package(Qt6 REQUIRED PATHS ${Qt6_SOURCE_DIR} NO_DEFAULT_PATH)
Propagate(CMAKE_PREFIX_PATH)
Propagate(Qt6_SOURCE_DIR)
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE)
endfunction()
+6 -34
View File
@@ -1,5 +1,5 @@
{
"openssl-ci": {
"openssl": {
"ci": true,
"package": "OpenSSL",
"name": "openssl",
@@ -7,40 +7,13 @@
"version": "3.6.0-1cb0d36b39",
"min_version": "3"
},
"openssl-cmake": {
"repo": "jimmy-park/openssl-cmake",
"hash": "c8bc3001497ada490065a18bca4c2ed7326701da3372c1d2c167e9853d43ad107341f97939b67eaa9cb4f2d9637cacbfb9f4ac5a52d9402a1605a6de05756345",
"git_version": "4.0.0",
"tag": "%VERSION%",
"bundled": true,
"options": [
"OPENSSL_CONFIGURE_OPTIONS threads"
],
"patches": [
"0001-cpmutil-compat.patch",
"0002-use-ccache.patch",
"0003-use-cmake-compiler-flags.patch"
]
},
"openssl": {
"repo": "openssl/openssl",
"package": "OpenSSL",
"min_version": "3",
"version": "3",
"hash": "e50bcb288436dc9fe35feff7fe3ce293a67cd168aa5c3a66cb24acd94f55c3dc3bc0be5be402432daf58605d2fb42805105d0e601519ca6e696f2b74b9f06911",
"git_version": "4.0.0",
"tag": "openssl-%VERSION%",
"patches": [
"0001-add-bundled-cert.patch"
]
},
"boost": {
"package": "Boost",
"repo": "boostorg/boost",
"tag": "boost-%VERSION%",
"artifact": "%TAG%-cmake.tar.xz",
"hash": "935b3d516e996f6d25948ba8a54c1b7f70f7f0e3f517e36481fdf0196c2c5cfc2841f86e891f3df9517746b7fb605db47cdded1b8ff78d9482ddaa621db43a34",
"git_version": "1.91.0",
"hash": "6ae6e94664fe7f2fb01976b59b276ac5df8085c7503fa829d810fbfe495960cfec44fa2c36e2cb23480bc19c956ed199d4952b02639a00a6c07625d4e7130c2d",
"git_version": "1.90.0",
"version": "1.57",
"find_args": "CONFIG OPTIONAL_COMPONENTS headers context system fiber filesystem",
"patches": [
@@ -125,9 +98,9 @@
"package": "VVL",
"repo": "KhronosGroup/Vulkan-ValidationLayers",
"tag": "vulkan-sdk-%VERSION%",
"git_version": "1.4.350.0",
"git_version": "1.4.341.0",
"artifact": "android-binaries-%VERSION%.zip",
"hash": "5cbed0369d4c325c8ea9ab5a1d78d98ec721bdfae4d772dac23803f31aa909be6e132fca3b64d507e6b9835f9be9d0848407adbf74365fd3f37ae99add502d21"
"hash": "8812ae84cbe49e6a3418ade9c458d3be6d74a3dffd319d4502007b564d580998056e8190414368ec11b27bc83993c7a0dad713c31bcc3d9553b51243efee3753"
},
"quazip": {
"package": "QuaZip-Qt6",
@@ -139,8 +112,7 @@
"options": [
"QUAZIP_QT_MAJOR_VERSION 6",
"QUAZIP_INSTALL OFF",
"QUAZIP_ENABLE_QTEXTCODEC OFF",
"QUAZIP_BZIP2 OFF"
"QUAZIP_ENABLE_QTEXTCODEC OFF"
]
}
}
-8
View File
@@ -1,8 +0,0 @@
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
# Allow systemd-logind to manage user access to hidraw with this file
# On most systems, this file should be installed to /etc/udev/rules.d/72-eden-input.rules
# Consult your distro if this is not the case
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ENV{ID_INPUT_JOYSTICK}=="1", MODE="0660", TAG+="uaccess"
+19
View File
@@ -0,0 +1,19 @@
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
# Allow systemd-logind to manage user access to hidraw with this file
# On most systems, this file should be installed to /etc/udev/rules.d/72-yuzu-input.rules
# Consult your distro if this is not the case
# Switch Pro Controller (USB/Bluetooth)
KERNEL=="hidraw*", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="2009", MODE="0660", TAG+="uaccess"
KERNEL=="hidraw*", KERNELS=="*057e:2009*", MODE="0660", TAG+="uaccess"
# Joy-Con L (Bluetooth)
KERNEL=="hidraw*", KERNELS=="*057e:2006*", MODE="0660", TAG+="uaccess"
# Joy-Con R (Bluetooth)
KERNEL=="hidraw*", KERNELS=="*057e:2007*", MODE="0660", TAG+="uaccess"
# Joy-Con Charging Grip (USB)
KERNEL=="hidraw*", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="200e", MODE="0660", TAG+="uaccess"
-1
View File
@@ -18,4 +18,3 @@
- `linux-amd64`
- `linux-aarch64`
- `macos-universal`
- `ios-aarch64`
+1 -2
View File
@@ -61,8 +61,7 @@ In order: OpenSSL CI, Boost (tag + artifact), Opus (options + find_args), discor
"version": "3.6.0",
"min_version": "1.1.1",
"disabled_platforms": [
"macos-universal",
"ios-aarch64"
"macos-universal"
]
},
"boost": {
+2 -1
View File
@@ -65,13 +65,14 @@ export LIBGL_ALWAYS_SOFTWARE=1
```
- Modify the generated ffmpeg.make (in build dir) if using multiple threads (base system `make` doesn't use `-j4`, so change for `gmake`).
- If using OpenIndiana, due to a bug in SDL2's CMake configuration, audio driver defaults to SunOS `<sys/audioio.h>`, which does not exist on OpenIndiana. Using external or bundled SDL2 may solve this.
- System OpenSSL generally does not work. Instead, use `-DYUZU_USE_BUNDLED_OPENSSL=ON` to use a bundled static OpenSSL, or build a system dependency from source.
## OmniOS
Install `developer/gcc14` on OmniOS using pkgsrc.
Since so many dependencies are missing on `OmniOS`, you may wish to use `-DCPMUTIL_FORCE_BUNDLED=ON`
Since so many dependencies are missing on `OmniOS`, you may wish to use `-DCPMUTIL_FORCE_BUNDLED=ON -DYUZU_USE_EXTERNAL_SDL2=ON`
For OmniOS you are required to build glslang yourself:
```sh
+19 -31
View File
@@ -35,11 +35,6 @@ If you are on desktop and plan to use the Qt frontend, you *must* install Qt 6,
* For help setting up Qt Creator, run `./install.sh -h qtcreator`
* If you're using clang-cl and want to still use MSVC
* Check the option to add "C++ clang compiler for Windows" on Visual Studio installer and uncheck "x64/x86 build tool for MSVC" while selecting "C++ desktop developement tools" and change Visual Studio to 2026, from 2022.
* At qt creator section generator tab change Visual Studio 17 2022 to 2026.
* Finally, to use clang-cl: `cmake -S . -B build -G "Visual Studio 17 2026" -T ClangCL`
If you are on **Windows** and building with **MSVC** or **clang-cl**, you may go [back home](Build.md) and continue.
## Externals
@@ -47,7 +42,7 @@ If you are on **Windows** and building with **MSVC** or **clang-cl**, you may go
The following are handled by Eden's externals:
* [FFmpeg](https://ffmpeg.org/) (should use `-DYUZU_USE_EXTERNAL_FFMPEG=ON`)
* [SDL3](https://www.libsdl.org/download-2.0.php) 3.2.10+ (Use `-DYUZU_USE_BUNDLED_SDL2=ON` to reduce compile time)
* [SDL2](https://www.libsdl.org/download-2.0.php) 2.0.18+ (should use `-DYUZU_USE_EXTERNAL_SDL2=ON` OR `-DYUZU_USE_BUNDLED_SDL2=ON` to reduce compile time)
All other dependencies will be downloaded and built by [CPM](https://github.com/cpm-cmake/CPM.cmake/) if `YUZU_USE_CPM` is on, but will always use system dependencies if available (UNIX-like only):
@@ -123,7 +118,7 @@ sudo emerge -a \
dev-util/spirv-tools dev-util/spirv-headers dev-util/vulkan-headers \
dev-util/vulkan-utility-libraries dev-util/glslang \
media-gfx/renderdoc media-libs/libva media-libs/opus media-video/ffmpeg \
media-libs/VulkanMemoryAllocator media-libs/libsdl3 media-libs/cubeb \
media-libs/VulkanMemoryAllocator media-libs/libsdl2 media-libs/cubeb \
net-libs/enet \
sys-libs/zlib \
dev-cpp/nlohmann_json dev-cpp/simpleini dev-cpp/cpp-httplib dev-cpp/cpp-jwt \
@@ -142,8 +137,7 @@ Required USE flags:
* `dev-qt/qtbase network concurrent dbus gui widgets`
* `dev-libs/quazip qt6`
* `media-libs/libsdl3 haptic joystick sound video`
* Adding `X vulkan udev opengl` is recommended but not required
* `media-libs/libsdl2 haptic joystick sound video`
* `dev-cpp/cpp-httplib ssl`
[Caveats](./Caveats.md#gentoo-linux)
@@ -154,7 +148,7 @@ Required USE flags:
<summary>Arch Linux</summary>
```sh
sudo pacman -Syu --needed base-devel boost catch2 cmake enet ffmpeg fmt git glslang libzip lz4 ninja nlohmann-json openssl opus qt6-base qt6-multimedia qt6-charts sdl3 zlib zstd zip unzip vulkan-headers vulkan-utility-libraries libusb spirv-tools spirv-headers
sudo pacman -Syu --needed base-devel boost catch2 cmake enet ffmpeg fmt git glslang libzip lz4 ninja nlohmann-json openssl opus qt6-base qt6-multimedia qt6-charts sdl2 zlib zstd zip unzip vulkan-headers vulkan-utility-libraries libusb spirv-tools spirv-headers
```
* Building with QT Web Engine requires `qt6-webengine` as well.
@@ -167,10 +161,10 @@ sudo pacman -Syu --needed base-devel boost catch2 cmake enet ffmpeg fmt git glsl
<summary>Ubuntu, Debian, Mint Linux</summary>
```sh
sudo apt-get install autoconf cmake g++ gcc git glslang-tools libglu1-mesa-dev libhidapi-dev libpulse-dev libtool libudev-dev libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-xinerama0 libxcb-xkb1 libxext-dev libxkbcommon-x11-0 mesa-common-dev nasm ninja-build qt6-base-private-dev catch2 libfmt-dev liblz4-dev nlohmann-json3-dev libzstd-dev libssl-dev libavfilter-dev libavcodec-dev libswscale-dev pkg-config zlib1g-dev libva-dev libvdpau-dev qt6-tools-dev qt6-charts-dev libvulkan-dev spirv-tools spirv-headers libusb-1.0-0-dev libxbyak-dev libboost-dev libboost-fiber-dev libboost-context-dev libsdl3-dev libopus-dev libasound2t64 vulkan-utility-libraries-dev
sudo apt-get install autoconf cmake g++ gcc git glslang-tools libglu1-mesa-dev libhidapi-dev libpulse-dev libtool libudev-dev libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-xinerama0 libxcb-xkb1 libxext-dev libxkbcommon-x11-0 mesa-common-dev nasm ninja-build qt6-base-private-dev catch2 libfmt-dev liblz4-dev nlohmann-json3-dev libzstd-dev libssl-dev libavfilter-dev libavcodec-dev libswscale-dev pkg-config zlib1g-dev libva-dev libvdpau-dev qt6-tools-dev qt6-charts-dev libvulkan-dev spirv-tools spirv-headers libusb-1.0-0-dev libxbyak-dev libboost-dev libboost-fiber-dev libboost-context-dev libsdl2-dev libopus-dev libasound2t64 vulkan-utility-libraries-dev
```
* Ubuntu 26.04, Linux Mint 22.3, or Debian 13 or later is required.
* Ubuntu 22.04, Linux Mint 20, or Debian 12 or later is required.
* To enable QT Web Engine, add `-DYUZU_USE_QT_WEB_ENGINE=ON` when running CMake.
</details>
@@ -191,13 +185,13 @@ AlmaLinux (use `YUZU_USE_CPM=ON`):
sudo dnf install epel-release dnf-utils
# (run rpmfusion installation afterwards)
# vvv - This will work for most systems
sudo dnf install autoconf cmake libtool libudev cmake gcc gcc-c++ qt6-qtbase-devel zlib-devel openssl-devel boost SDL3 ffmpeg-devel libdrm glslang jq patch
sudo dnf install autoconf cmake libtool libudev cmake gcc gcc-c++ qt6-qtbase-devel zlib-devel openssl-devel boost SDL2 ffmpeg-devel libdrm glslang jq patch
# Qt6 private GUI must be taken from CRB repos
sudo dnf config-manager --enable crb
sudo dnf install qt6-qtbase-private-devel
```
For systems like OpenEuler or derivates, don't forget to also install: `SDL3-devel pkg-config fmt-dev nlohmann-json-dev`.
For systems like OpenEuler or derivates, don't forget to also install: `SDL2-devel pkg-config fmt-dev nlohmann-json-dev`.
* [RPM Fusion](https://rpmfusion.org/Configuration) is required for `ffmpeg-devel`
* Fedora 32 or later is required.
@@ -214,7 +208,7 @@ First, enable the community repository; [see here](https://wiki.alpinelinux.org/
# Enable the community repository
setup-apkrepos -c
# Install
apk add g++ git cmake make mesa-dev qt6-qtbase-dev qt6-qtbase-private-dev libquazip1-qt6 ffmpeg-dev qt6-charts-dev libusb-dev libtool boost-dev sdl3-dev zstd-dev vulkan-utility-libraries spirv-tools-dev openssl-dev nlohmann-json lz4-dev opus-dev jq patch
apk add g++ git cmake make mesa-dev qt6-qtbase-dev qt6-qtbase-private-dev libquazip1-qt6 ffmpeg-dev qt6-charts-dev libusb-dev libtool boost-dev sdl2-dev zstd-dev vulkan-utility-libraries spirv-tools-dev openssl-dev nlohmann-json lz4-dev opus-dev jq patch
```
</details>
@@ -222,7 +216,7 @@ apk add g++ git cmake make mesa-dev qt6-qtbase-dev qt6-qtbase-private-dev libqua
<summary>Void Linux</summary>
```sh
xbps-install -Su git make cmake clang pkg-config patch SPIRV-Tools-devel SPIRV-Headers lz4 liblz4-devel boost-devel ffmpeg6-devel catch2 Vulkan-Utility-Libraries Vulkan-Headers glslang openssl-devel SDL3-devel quazip-qt6-devel qt6-base-devel qt6-qt5compat-devel qt6-charts-devel fmt-devel json-c++ libenet-devel libusb-devel
xbps-install -Su git make cmake clang pkg-config patch SPIRV-Tools-devel SPIRV-Headers lz4 liblz4-devel boost-devel ffmpeg6-devel catch2 Vulkan-Utility-Libraries Vulkan-Headers glslang openssl-devel SDL2-devel quazip-qt6-devel qt6-base-devel qt6-qt5compat-devel qt6-charts-devel fmt-devel json-c++ libenet-devel libusb-devel
```
Yes, `nlohmann-json` is just named `json-c++`. Why?
@@ -243,7 +237,7 @@ If you're going for a pure build (i.e no downloaded deps), use `-DYUZU_USE_CPM=O
Install dependencies from **[Homebrew](https://brew.sh/)**
```sh
brew install autoconf automake boost ffmpeg fmt glslang hidapi libtool libusb lz4 ninja nlohmann-json openssl pkg-config qt@6 sdl3 speexdsp zlib zstd cmake Catch2 molten-vk vulkan-loader spirv-tools
brew install autoconf automake boost ffmpeg fmt glslang hidapi libtool libusb lz4 ninja nlohmann-json openssl pkg-config qt@6 sdl2 speexdsp zlib zstd cmake Catch2 molten-vk vulkan-loader spirv-tools
```
If you are compiling on Intel Mac, or are using a Rosetta Homebrew installation, you must replace all references of `/opt/homebrew` with `/usr/local`.
@@ -260,7 +254,7 @@ brew install molten-vk
<details>
<summary>FreeBSD</summary>
As root run: `pkg install devel/cmake sdl3 devel/boost-libs devel/catch2 devel/libfmt devel/nlohmann-json devel/ninja devel/nasm devel/autoconf devel/pkgconf devel/qt6-base devel/qt6-charts devel/simpleini net/enet multimedia/ffnvcodec-headers multimedia/ffmpeg audio/opus archivers/liblz4 lang/gcc12 graphics/glslang graphics/vulkan-utility-libraries graphics/spirv-tools www/cpp-httplib devel/unordered-dense vulkan-headers quazip-qt6`
As root run: `pkg install devel/cmake devel/sdl20 devel/boost-libs devel/catch2 devel/libfmt devel/nlohmann-json devel/ninja devel/nasm devel/autoconf devel/pkgconf devel/qt6-base devel/qt6-charts devel/simpleini net/enet multimedia/ffnvcodec-headers multimedia/ffmpeg audio/opus archivers/liblz4 lang/gcc12 graphics/glslang graphics/vulkan-utility-libraries graphics/spirv-tools www/cpp-httplib devel/unordered-dense vulkan-headers quazip-qt6`
If using FreeBSD 12 or prior, use `devel/pkg-config` instead.
@@ -270,11 +264,7 @@ If using FreeBSD 12 or prior, use `devel/pkg-config` instead.
<details>
<summary>NetBSD</summary>
For NetBSD +10.1:
```sh
pkgin install git cmake boost fmtlib SDL3 catch2 libjwt spirv-headers spirv-tools ffmpeg7 libva nlohmann-json jq libopus qt6 cpp-httplib lz4 vulkan-headers nasm autoconf enet pkg-config libusb1 libcxx frozen
```
For NetBSD +10.1: `pkgin install git cmake boost fmtlib SDL2 catch2 libjwt spirv-headers spirv-tools ffmpeg7 libva nlohmann-json jq libopus qt6 cpp-httplib lz4 vulkan-headers nasm autoconf enet pkg-config libusb1 libcxx`.
[Caveats](./Caveats.md#netbsd).
@@ -284,7 +274,7 @@ pkgin install git cmake boost fmtlib SDL3 catch2 libjwt spirv-headers spirv-tool
```sh
pkg_add -u
pkg_add cmake nasm git boost unzip--iconv autoconf-2.72p0 bash ffmpeg glslang gmake qt6 jq fmt nlohmann-json enet boost vulkan-utility-libraries vulkan-headers spirv-headers spirv-tools catch2 sdl3 libusb1-1.0.29 quazip-qt6
pkg_add cmake nasm git boost unzip--iconv autoconf-2.72p0 bash ffmpeg glslang gmake qt6 jq fmt nlohmann-json enet boost vulkan-utility-libraries vulkan-headers spirv-headers spirv-tools catch2 sdl2 libusb1-1.0.29
```
[Caveats](./Caveats.md#openbsd).
@@ -294,7 +284,7 @@ pkg_add cmake nasm git boost unzip--iconv autoconf-2.72p0 bash ffmpeg glslang gm
<summary>DragonFlyBSD</summary>
```sh
pkg install gcc14 git cmake unzip nasm autoconf bash pkgconf ffmpeg glslang gmake jq nlohmann-json enet spirv-tools sdl3 vulkan-utility-libraries vulkan-headers catch2 libfmt openssl liblz4 boost-libs cpp-httplib qt6-base qt6-charts quazip-qt6 unordered-dense libva-vdpau-driver libva-utils libva-intel-driver
pkg install gcc14 git cmake unzip nasm autoconf bash pkgconf ffmpeg glslang gmake jq nlohmann-json enet spirv-tools sdl2 vulkan-utility-libraries vulkan-headers catch2 libfmt openssl liblz4 boost-libs cpp-httplib qt6-base qt6-charts quazip-qt6 unordered-dense libva-vdpau-driver libva-utils libva-intel-driver
```
[Caveats](./Caveats.md#dragonflybsd).
@@ -304,7 +294,7 @@ pkg install gcc14 git cmake unzip nasm autoconf bash pkgconf ffmpeg glslang gmak
<summary>OpenIndiana</summary>
```sh
sudo pkg install git cmake qt6 boost glslang libzip library/lz4 libusb-1 nlohmann-json openssl opus sdl3 zlib compress/zstd unzip pkg-config nasm autoconf mesa library/libdrm header-drm developer/fmt
sudo pkg install git cmake qt6 boost glslang libzip library/lz4 libusb-1 nlohmann-json openssl opus sdl2 zlib compress/zstd unzip pkg-config nasm autoconf mesa library/libdrm header-drm developer/fmt
```
[Caveats](./Caveats.md#openindiana).
@@ -328,7 +318,7 @@ sudo pkgin install git cmake autoconf build-essential libusb-1 nasm gcc13
```sh
BASE="git make autoconf libtool automake-wrapper jq patch"
MINGW="qt6-base qt6-charts qt6-tools qt6-translations qt6-svg cmake toolchain clang python-pip openssl vulkan-memory-allocator vulkan-devel glslang boost fmt lz4 nlohmann-json zlib zstd enet opus libusb unordered_dense openssl SDL3"
MINGW="qt6-base qt6-charts qt6-tools qt6-translations qt6-svg cmake toolchain clang python-pip openssl vulkan-memory-allocator vulkan-devel glslang boost fmt lz4 nlohmann-json zlib zstd enet opus libusb unordered_dense openssl SDL2"
# Either x86_64 or clang-aarch64 (Windows on ARM)
packages="$BASE"
for pkg in $MINGW; do
@@ -354,7 +344,7 @@ pacman -Syuu --needed --noconfirm $packages
<summary>HaikuOS</summary>
```sh
pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel boost1.90_devel vulkan_devel qt6_base_devel qt6_declarative_devel libsdl3_devel ffmpeg7_devel libx11_devel enet_devel catch2_devel quazip1_qt5_devel qt6_5compat_devel glslang qt6_devel qt6_charts_devel cubeb_devel simpleini quazip_qt6_devel
pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel boost1.90_devel vulkan_devel qt6_base_devel qt6_declarative_devel libsdl2_devel ffmpeg7_devel libx11_devel enet_devel catch2_devel quazip1_qt5_devel qt6_5compat_devel glslang qt6_devel qt6_charts_devel
```
[Caveats](./Caveats.md#haikuos).
@@ -365,11 +355,9 @@ pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel b
```sh
sudo pkg update
sudo pkg install git cmake ffmpeg6 zlib llvm18
sudo pkg install git cmake ffmpeg6 sdl2 zlib llvm18
```
RedoxOS currently does not support SDL3. You will have to compile it yourself and pray.
[Caveats](./Caveats.md#redoxos).
</details>
-31
View File
@@ -1,31 +0,0 @@
# HOS Kernel
In brief, the HOS kernel is a microkernel, some services and programs run in userspace, the primary way to do communication between these is via `HIPC` (not covered here); otherwise most of the primitives reside in the forms of syscalls invoked via `svc #imm`. The kernel supports both 32-bit and 64-bit programs, and has the capacity to use 32, 36 and 39 bits of address space for spawned processes. Most of the networking stack is based off FreeBSD's network stack.
The emulator implements the majority of the syscalls pertaining to the HOS kernel itself. When we talk about the HOS Kernel (in the context of the emulator) we are strictly speaking about the mechanisms from which syscalls are handled (and it's subsequent side effects, such as the page table book-keeping). The emulator at it's current state is unable to load a custom low-level kernel and do supervisor-level emulation.
Most programs in NX eventually invoke an `svc`, which, depending on it's immediate value, will go on to be dispatched into one of the specific syscall handlers.
These can be seen in [svc.cpp](/src/core/hle/kernel/svc.cpp). All of these correspond to syscalls which userspace programs may perform.
In turn, these syscalls create the mechanisms that allows programs to use CMIF/TIPC as their primary IPC form to contact other services/processes running on the system, the details of which will not be covered here, but you can consult the relevant [SwitchBrew article: 'HIPC'](https://switchbrew.org/wiki/HIPC).
From the point of view of the programs, no special devices (such as PCIE, Realtek drivers, Bluetooth or USB) has to be handled by the emulator; this is because most of the fun occurs in specialized services such as `usb:u` or `pcie` services. Which aren't emulated (yet).
Due to the nature of syscalls, many of them interact with memory. The emulated kernel has an internal tree-like structure, borrowed from FreeBSD's intrusive red-black tree; this is used to track and find mappings added or removed. Thus most of the process space is emulated in this way.
The kernel keeps it's own separate pagetable, in a traditional sense, each process has it's own pagetable, this is true for HOS as well.
Every process keeps it's own tracking of the following structures:
- Name (13 characters)
- 64-bit ID
- A handle table
- Exclusive monitor
- Threads
- Held locks
- Thread local pages
- A page table for each process
The emulator willingly restricts itself to only use 4 threads (to emulate 4 cores), this is because most existing applications do not benefit greatly from the added core count, and in fact can be detrimental due to extra contention. This translates equitatively to about 4 `ArmInterface` slots for each process, these are then redirected to whatever is the last `pc` of the last thread running on the core is meant to be; proceed to run it, then when returning (due to halt or interruption), proceed to reschedule the thread.
The scheduler as-is isn't 100% faithful to the original, and has great timing variance (especially due to the fact the emulator can run in systems with wildly different timings).
-2
View File
@@ -853,8 +853,6 @@ Texture Query.
Vote Across SIMD Thread Group
`VOTE_vtg` is a kepler leftover.
# VSET
`0100 000- ---- ----`
+3 -6
View File
@@ -29,7 +29,8 @@ These options control dependencies.
- `YUZU_TZDB_PATH` (string) Path to a pre-downloaded timezone database (useful for nixOS and Gentoo)
- `YUZU_USE_BUNDLED_MOLTENVK` (ON, macOS only) Download bundled MoltenVK lib
- `YUZU_USE_BUNDLED_OPENSSL` (ON for MSVC, Android, Solaris, and OpenBSD) Download bundled OpenSSL build
- `YUZU_USE_BUNDLED_SDL3` (ON for MSVC) Download a prebuilt SDL3
- `YUZU_USE_EXTERNAL_SDL2` (OFF) Compiles SDL2 from source
- `YUZU_USE_BUNDLED_SDL2` (ON for MSVC) Download a prebuilt SDL2
### Miscellaneous
@@ -43,9 +44,6 @@ These options control dependencies.
- UNIX may be better off appending `-flto=thin` to compiler args
- `USE_FASTER_LINKER` (OFF) Check if a faster linker is available
- Not recommended outside of Linux
- `YUZU_INSTALL_UDEV_RULES` (OFF) Install udev rules to enable hidraw access
- Needed for gyroscopes
- Only available on Linux
### Flavors
@@ -62,7 +60,7 @@ These options control executables and build flavors.
**Desktop only**:
- `YUZU_CMD` (ON) Compile the SDL-based frontend (eden-cli)
- `YUZU_CMD` (ON) Compile the SDL2 frontend (eden-cli)
- `YUZU_ROOM` (OFF) Compile dedicated room functionality into the main executable
- `YUZU_ROOM_STANDALONE` (OFF) Compile a separate executable for room functionality
- `YUZU_STATIC_ROOM` (OFF) Compile the room executable *only* as a static, portable executable
@@ -98,6 +96,5 @@ The following options were a part of Eden at one point, but have since been reti
- `ENABLE_SDL2` - While technically possible to *not* use SDL2 on desktop, this is **NOT** a supported configuration under any means, and adding this matrix to our build system was not worth the effort.
- `YUZU_USE_CPM` - This option once had a purpose, but that purpose has long since passed us by. *All* builds use CPMUtil to manage dependencies now.
- If you want to *force* the usage of system dependencies, use `-DCPMUTIL_FORCE_SYSTEM=ON`.
- `YUZU_USE_EXTERNAL_SDL` - This is now handled automatically. It was included even after CPM for purposes that have not applied for a very long time.
See `src/dynarmic/CMakeLists.txt` for additional options--usually, these don't need changed
+7 -21
View File
@@ -2,26 +2,21 @@
Are you just a casual user? Take a look at our [User Handbook](./user) then!
If you want to register/signup as a contributor, take a gander at the [signup guide](./SIGNUP.md).
This contains documentation created by developers. This contains build instructions, guidelines, instructions/layouts for [cool stuff we made](./CPMUtil), and more.
- **[General Build Instructions](./Build.md)**
- **[CMake Options](./Options.md)**
- **[Cross Compiling](./CrossCompile.md)**
- **[Development Guidelines](./Development.md)**
- **[Dependencies](./Deps.md)**
- **[General Build Instructions](Build.md)**
- **[CMake Options](Options.md)**
- **[Cross Compiling](CrossCompile.md)**
- **[Development Guidelines](Development.md)**
- **[Dependencies](Deps.md)**
- **[Debug Guidelines](./Debug.md)**
- **[CPM - CMake Package Manager](./CPMUtil)**
- **[Platform-Specific Caveats](./Caveats.md)**
- **[Platform-Specific Caveats](Caveats.md)**
- **[The NVIDIA SM86 (Maxwell) GPU](./NvidiaGpu.md)**
- **[Dynarmic](./dynarmic)**
- **[Cross compilation](./CrossCompile.md)**
- **[Driver Bugs](./DriverBugs.md)**
- **[Building Older Commits](./build/OlderCommits.md)**
- Subsystems:
- **[Dynarmic](./dynarmic/README.md)**
- **[HOS Kernel](./HosKernel.md)**
- **[Settings](./Settings.md)**
## Policies
@@ -30,12 +25,3 @@ Policies and information on development.
- **[AI and LLM Usage](./policies/AI.md)**
- **[Release Policy](./policies/Release.md)**
- **[Coding guidelines](./policies/Coding.md)**
- **[Coding Style guidelines](./policies/CodingStyle.md)**
## Externals
Other useful resources in general, take a quick read if you need.
- **[SwitchBrew](https://switchbrew.org/wiki/Main_Page)**
- **[IPS file format](https://zerosoft.zophar.net/ips.php)**
- **[IPSwitch file format](https://github.com/3096/ipswitch)**
-327
View File
@@ -1,327 +0,0 @@
# Settings
> [!WARNING]
> This guide is intended for developers ONLY. If you're looking for configuring the emulator itself, please read **[the user handbook](./user/README.md)**.
Settings on the emulator are very important, toggles and such can be used to guard and/or add branches to paths where some games may crash while others won't, and viceversa.
However, this process can be tedious for those unfamiliar; this document serves as a outline/documentation for the settings subsystem.
## Index
* [Adding Debug Knobs](#adding-debug-knobs)
* [Advantages](#advantages)
* [Usage](#usage)
* [Accessing Debug Knobs (dev side)](#accessing-debug-knobs-dev-side)
* [Setting Debug Knobs (user side)](#setting-debug-knobs-user-side)
* [Bit Manipulation Examples](#bit-manipulation-examples)
* [Terminology and user communication](#terminology-and-user-communication)
* [Examples](#examples)
* [Example 1: Conditional Debug Logging](#example-1-conditional-debug-logging)
* [Example 2: Performance Tuning](#example-2-performance-tuning)
* [Example 3: Feature Gating](#example-3-feature-gating)
* [Best Practices](#best-practices)
* [Adding Boolean Settings Toggles](#adding-boolean-settings-toggles)
* [Step 1 - Common Setting](#step-1-common-setting)
* [Step 2 - Qt Toggle](#step-2-qt-toggle)
* [Step 3 - Kotlin (Android)](#step-3-kotlin-android)
* [Step 3.1 - BooleanSetting.kt](#step-3-1-booleansetting-kt)
* [Step 3.2 - SettingsItem.kt](#step-3-2-settingsitem-kt)
* [Step 3.3 - SettingsFragmentPresenter.kt](#step-3-3-settingsfragmentpresenter-kt)
* [Step 3.4 - Localization](#step-3-4-localization)
* [Step 4 - Use Your Toggle](#step-4-use-your-toggle)
* [Best Practices](#best-practices)
## Adding Boolean Settings Toggles
This guide will walk you through adding a new boolean toggle setting to Eden's configuration across both Qt's (PC) and Kotlin's (Android) UIs.
---
### Step 1 - Common Setting
Firstly add your desired toggle:
Example: `src/common/setting.h`
```cpp
SwitchableSetting<bool> your_setting_name{linkage, false, "your_setting_name", Category::RendererExtensions};
```
Remember to add your toggle to the appropriate category, for example:
Common Categories:
* Category::Renderer
* Category::RendererAdvanced
* Category::RendererExtensions
* Category::System
* Category::Core
> [!WARNING]
> If you wish for your toggle to be `on by default` then change `false` to `true` after `linkage,`.
---
### Step 2 - Qt Toggle
Add the toggle to the Qt UI, where you wish for it to appear and place it there.
Example: `src/qt_common/config/shared_translation.cpp`
```cpp
INSERT(Settings,
your_setting_name,
tr("Your Setting Display Name"),
tr("Detailed description of what this setting does.\n"
"You can use multiple lines.\n"
"Explain any caveats or requirements."));
```
#### Make sure to:
* Keep display naming consistant
* Put detailed info in the description
* Use `\n` for line breaks in descriptions
---
### Step 3 - Kotlin (Android)
#### Step 3.1 - BooleanSetting.kt
Add where it should be in the settings.
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt`
```kts
RENDERER_YOUR_SETTING_NAME("your_setting_name"),
```
#### Make sure to:
* Ensure the prefix naming matches the intended category.
---
#### Step 3.2 - SettingsItem.kt
Add the toggle to the Kotlin (Android) UI
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt`
```kts
put(
SwitchSetting(
BooleanSetting.RENDERER_YOUR_SETTING_NAME,
titleId = R.string.your_setting_name,
descriptionId = R.string.your_setting_name_description
)
)
```
---
#### Step 3.3 - SettingsFragmentPresenter.kt
Add your setting within the right category.
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt`
```kts
add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key)
```
> [!WARNING]
> Remember, placing matters! Settings appear in the order of where you add them.
---
#### Step 3.4 - Localization
Add your setting and description in the appropriate place.
Example: `src/android/app/src/main/res/values/strings.xml`
```xml
<string name="your_setting_name">Your Setting Display Name</string>
<string name="your_setting_name_description">Detailed description of what this setting does. Explain any caveats, requirements, or warnings here.</string>
```
---
### Step 4 - Use Your Toggle!
Now the UI part is done find a place in the code for the toggle,
And use it to your heart's desire!
Example:
```cpp
const bool your_value = Settings::values.your_setting_name.GetValue();
if (your_value) {
// Do something when enabled
}
```
If you wish to do something only when the toggle is disabled,
Use `if (!your_value) {` instead of `if (your_value) {`.
---
### Best Practices
* Naming - Use clear, descriptive names. Something for both the devs and the users.
* Defaults - Choose safe default values (usually false for new features).
* Documentation - Write clear descriptions explaining when and why to use the setting.
* Categories - Put settings in the appropriate category.
* Order - Place related settings near each other.
* Testing - Always test on both PC and Android before committing when possible.
Thank you for reading, I hope this guide helped you making your toggle!
## Adding Debug Knobs
Debug Knobs is a 16-bit integer setting (`debug_knobs`) in the Eden Emulator that serves as a bitmask for gating various testing and debugging features. This allows developers and advanced users to enable or disable specific debug behaviors without requiring deploying of complete but temporary toggles.
The setting ranges from 0 to 65535 (0x0000 to 0xFFFF), where each bit represents a different debug feature flag.
---
### Advantages
The main advantage is to avoid deploying new disposable toggles (those made only for testing stage, and are disposed once new feature gets good to merge). This empowers devs to be free of all frontend burocracy and hassle of new toggles.
Common advantages recap:
* **Fine-Grained Control**: Enable or disable up to 16 individual debug features independently using bit manipulation on a single build
* **Runtime Configuration**: Change debug behavior at runtime the same way as new toggles would do
* **Safe incremental development**: New debug features can be added while impact can be isolated from previous deployments
### Usage
#### Accessing Debug Knobs (dev side)
Use the `Settings::getDebugKnobAt(u8 i)` function to check if a specific bit is set:
```cpp
//cpp side
#include "common/settings.h"
// Check if bit 0 is set
bool feature_enabled = Settings::getDebugKnobAt(0);
// Check if bit 15 is set
bool another_feature = Settings::getDebugKnobAt(15);
```
```kts
//kotlin side
import org.yuzu.yuzu_emu.features.settings.model.Settings
// Check if bit x is set
bool feature_enabled = Settings.getDebugKnobAt(x); //x as integer from 0 to 15
```
The function returns `true` if the specified bit (0-15) is set in the `debug_knobs` value, `false` otherwise.
#### Setting Debug Knobs (user side)
Developers must inform which knobs are tied to each functionality to be tested.
The debug knobs value can be set through:
1. **Desktop UI**: In the Debug configuration tab, there's a spinbox for "Debug knobs" (0-65535)
2. **Android UI**: Available as an integer setting in the Debug section
3. **Configuration Files**: Set the `debug_knobs` value in the emulator's configuration
#### Bit Manipulation Examples
To enable specific features, calculate the decimal value by setting the appropriate bits:
* **Enable only bit 0**: Value = 1 (2^0)
* **Enable only bit 1**: Value = 2 (2^1)
* **Enable bits 0 and 1**: Value = 3 (2^0 + 2^1)
* **Enable bit 15**: Value = 32768 (2^15)
### Terminology and user communication
There are two main confusions when talking about knobs:
#### Whether it's zero-based or one-based
Sometimes when an user reports: knobs 1 and 2 gets better performance, dev may get confuse whether he means the knobs 1 and 2 literally, or the 1st and 2nd knobs (knobs 0 and 1).
Debug knobs are **zero-based**, which means:
* The first knob is the knob(0) (or knob0 henceforth), and the last one is the 15 (knob15, likewise)
* You can talk: "knob0 is enabled/disabled", "In this video i was using only knobs 0 and 2", etc.
#### Whether one is talking about the knob itself or about the entire parameter value (which represents all knobs)
Sometimes when an user reports: knob 3 results, it's unclear whether he's referring to knob setting with value 3 (which means both knob 0 and 1 are enabled), or to knob(3) specifically.
Whenever you're instructing tests or reporting results, be precise about whether one you're talking to avoid confusion:
#### Setting based terminology
ALWAYS use the word in PLURAL (knobs), without mentioning which one, to refer to the setting, aka multiple knobs at once:
Examples:
- **knobs=0**: no knobs enabled
- **knobs=1**: knob0 enabled, others disabled
- **knobs=2**: knob1 enabled, others disabled
- **knobs=3**: knobs 0 and 1 enabled, others disabled
...
#### Knob based terminology
Use the word in SINGULAR (knob), or in plural but referring which ones, when meaning multiple knobs at once:
Examples:
- **knob0**: knob 0 enabled, others disabled
- **knob1**: knob 1 enabled, others disabled
- **knobs 0 and 1**: knobs 0 and 1 enabled, others disabled
...
### Examples
#### Example 1: Conditional Debug Logging
```cpp
void SomeFunction() {
if (Settings::getDebugKnobAt(0)) {
LOG_DEBUG(Common, "Debug feature 0 is enabled");
// Additional debug code here
}
if (Settings::getDebugKnobAt(1)) {
LOG_DEBUG(Common, "Debug feature 1 is enabled");
// Different debug behavior
}
}
```
#### Example 2: Performance Tuning
```cpp
bool UseOptimizedPath() {
// Skip optimization if debug bit 2 is set for testing
return !Settings::getDebugKnobAt(2);
}
```
#### Example 3: Feature Gating
```cpp
void ExperimentalFeature() {
static constexpr u8 EXPERIMENTAL_FEATURE_BIT = 3;
if (!Settings::getDebugKnobAt(EXPERIMENTAL_FEATURE_BIT)) {
// Fallback to stable implementation
StableImplementation();
return;
}
// Experimental implementation
ExperimentalImplementation();
}
```
### Best Practices
* This setting is intended for development and testing purposes only
* Knobs must be unwired before PR creation
* The setting is per-game configurable, allowing different debug setups for different titles
+114 -58
View File
@@ -1,70 +1,126 @@
# Coding guidelines
These are **not** stylistic guidelines, they're, for the most part, suggestions on how to architecture new systems or improve upon the existing codebase.
These are mostly "suggestions", if you feel like your code is readable, comprehensible to others; and most importantly doesn't result in unreadable spaghetti you're fine to go.
# Foreword
But for new developers you may find that following these guidelines will make everything x10 easier.
Don't try to micro-optimize out of the get go, while yes, most of the code is pretty, subpar, most of these are aftertoughts and details that can be glossed over **generally**.
## Naming conventions
Architectural issues are more important, for example an API returning a `std::string` is not as efficient as one that operates on `std::string_view` directly (cost of constructing an `std::string` w/o small-string optimization and all of that).
Simply put, types/classes are named as `PascalCase`, same for methods and functions like `AddElement`. Variables are named `like_this_snake_case` and constants are `IN_SCREAMING_CASE`.
Regardless of the details, try to keep things simple. As a general rule of thumb.
Except for Qt MOC where `functionName` is preferred.
# C++ guidelines
Template typenames prefer short names like `T`, `I`, `U`, if a longer name is required either `Iterator` or `perform_action` are fine as well. Do not use names like `SS` as systems like solaris define it for registers, in general do not use any of the following for short names:
Everyone has their own way of viewing good/bad C++ practices, my general outline:
- `SS`, `DS`, `GS`, `FS`: Segment registers, defined by Solaris `<ucontext.h>`
- `EAX`, `EBX`, `ECX`, `EDX`, `ESI`, `EDI`, `ESP`, `EBP`, `EIP`: Registers, defined by Solaris.
- `X`: Defined by some utility headers, avoid.
- `_`: Defined by gettext, avoid.
- `N`, `M`, `S`: Preferably don't use this for types, use it for numeric constants.
- `TR`: Used by some weird `<ucontext.h>` whom define the Task Register as a logical register to provide to the user... (Need to remember which OS in specific).
- At your disposal you may use `boost::container::static_vector<>` (beware it has a ctor/initialization cost which goes up the more elements you add).
- Or you may use `boost::container::small_vector<>` (which has an initialization cost as well, and will use extra book-keeping for heap, try to keep a balance).
- Don't use `[[likely]]` or `[[unlikely]]`; PGO builds exist for that.
- Don't use inline assembly to try to outsmart the compiler unless you're 100% sure the assembly you're writing is actually good.
- And if so, try to restructure your C++ code so the compiler vectorizes it/makes it better, right?
- Or if that fails, use intrinsics instead of raw `asm volatile`.
- Use `std::optional<>` instead of `std::unique_ptr<>` if possible.
- `std::unique_ptr<>` carries indirection cost due to it being memory allocated on the heap.
- It isn't often that objects that contain `std::unique_ptr<>`, are allocated on the heap themselves, allocating even more things on the heap seems redundant.
- Avoid `std::recursive_mutex` at all costs.
- It's basically implemented as a linked list most of the time and has HEAVY performance penalties.
- Exploit the fact `std::atomic<uint32_t>/std::atomic<int32_t>` is basically free on most arches that matter.
- In x86_64, an atomic `uint32_t` is basically `mov [m32], r32`, which is essentially free/cheap.
- Avoid template parameters unless you really need them.
- For small inlineable functions this is fine, for more complex ones, please consider the generated assembly.
- Dont make your own memcpy/memset/strcpy/strncpy/etc.
- Seriously DON'T DO THIS. You will NOT beat the compiler.
- Nor 30 years of writing optimized `mem*`.
- If your code is slow, don't blame `mem*`, blame your code.
- Try to avoid using `virtual` since vtable indirection has a cost
- Avoid `dynamic_cast` and `typeid` at all costs.
- The reason is because the project has `-fno-rtti` disabled by default, due to the costs of dynamic polymorphism.
- Always copy-on-value for objects with `sizeof(void *) >= sizeof(T) * 2`, i.e objects sized as 2 pointers or less, for bigger objects you can use ref/pointer as usual.
- Try using move semantics instead of references, whenever possible.
- Remember function parameters are extremelly cheap as fuck, don't be afraid to place upto 8 parameters on a given function.
- Don't save a reference in structures of a parent object, i.e:
```c++
struct Child {
Parent& parent;
void Mehod() {
parent.Something();
}
};
```
- Instead you can do the following:
```c++
struct Child {
void Mehod(Parent& parent) {
parent.Something();
}
};
```
- This reduces the amount of pointers you have lying around, and also works better because of the aforementioned cheapness of parameter functions.
Macros must always be in `SCREAMING_CASE`. Do not use short letter macros as systems like Solaris will conflict with them; a good rule of thumb is >5 characters per macro - i.e `THIS_MACRO_IS_GOOD`, `AND_ALSO_THIS_ONE`.
# Engineering guidelines
Try not using hungarian notation, if you're able.
Coding isn't also writing stuff but architecturing stuff, consider the following:
## Formatting
- Try to reduce dependency on... dependencies
- While some dependencies are useful `boost::container` and `fmt` to name a few, remember each dependency added incurs a cost.
- It may also be subpar with a hand rolled implementation, biggest exemplar of this is `spirv-tools` providing subpar SPIRV optimizations in comparison to the in-house optimizer.
- Try to rely less on indirection for architecturing systems
- If the underlying HLE kernel emulation requires it, try making a solution that keeps things local
- For example, there isn't a need for file descriptors to each be a pointer, when they could be a fixed table size with elements that may be emplaced at will.
Formatting is extremelly lax, the general rule of thumb is: Don't add new lines just to increase line count. The less lines we have to look at, the better. This means also packing densely your code while not making it a clusterfuck. Strike a balance of "this is a short and comprehensible piece of code" and "my eyes are actually happy to see this!". Don't just drop the entire thing in a single line and call it "dense code", that's just spaghetti posing as code. In general, be mindful of what other devs need to look at.
Do not put if/while/etc braces after lines:
```c++
// no dont do this
// this is more lines of code for no good reason (why braces need their separate lines?)
// and those take space in someone's screen, cumulatively
if (thing)
{ //<--
some(); // ...
} //<-- 2 lines of code for basically "opening" and "closing" an statment
// do this
if (thing) { //<-- [...] and with your brain you can deduce it's this piece of code
// that's being closed
some(); // ...
} //<-- only one line, and it's clearer since you know its closing something [...]
// or this, albeit the extra line isn't needed (at your discretion of course)
if (thing)
some(); // ...
// this is also ok, keeps things in one line and makes it extremely clear
if (thing) some();
// NOT ok, don't be "clever" and use the comma operator to stash a bunch of statments
// in a single line, doing this will definitely ruin someone's day - just do the thing below
// vvv
if (thing) some(), thing(), a2(a1(), y1(), j1()), do_complex_shit(wa(), wo(), ploo());
// ... and in general don't use the comma operator for "multiple statments", EXCEPT if you think
// that it makes the code more readable (the situation may be rare however)
// Wow so much clearer! Now I can actually see what each statment is meant to do!
if (thing) {
some();
thing();
a2(a1(), y1(), j1());
do_complex_shit(wa(), wo(), ploo());
}
```
Brace rules are lax, if you can get the point across, do it:
```c++
// this is fine
do {
if (thing) {
return 0;
}
} while (other);
// this is also ok --- albeit a bit more dense
do if (thing) return 0; while (other);
// ok as well
do {
if (thing) return 0;
} while (other);
```
There is no 80-column limit but preferably be mindful of other developer's readability (like don't just put everything onto one line).
```c++
// someone is going to be mad due to this
SDL_AudioSpec obtained;
device_name.empty() ? device = SDL_OpenAudioDevice(nullptr, capture, &spec, &obtained, false) : device = SDL_OpenAudioDevice(device_name.c_str(), capture, &spec, &obtained, false);
// maybe consider this
SDL_AudioSpec obtained;
if (device_name.empty()) {
device = SDL_OpenAudioDevice(nullptr, capture, &spec, &obtained, false);
} else {
device = SDL_OpenAudioDevice(device_name.c_str(), capture, &spec, &obtained, false);
}
// or this is fine as well
SDL_AudioSpec obtained;
device = SDL_OpenAudioDevice(device_name.empty() ? nullptr : device_name.c_str(), capture, &spec, &obtained, false);
```
A note about operators: Use them sparingly, yes, the language is lax on them, but some usages can be... tripping to say the least.
```c++
a, b, c; //<-- NOT OK multiple statments with comma operator is definitely a recipe for disaster
return c ? a : b; //<-- OK ternaries at end of return statments are clear and fine
return a, b; //<-- NOT OK return will take value of `b` but also evaluate `a`, just use a separate statment
void f(int a[]) //<-- OK? if you intend to use the pointer as an array, otherwise just mark it as *
```
And about templates, use them sparingly, don't just do meta-templating for the sake of it, do it when you actually need it. This isn't a competition to see who can make the most complicated and robust meta-templating system. Just use what works, and preferably stick to the standard libary instead of reinventing the wheel. Additionally:
```c++
// NOT OK This will create (T * N * C * P) versions of the same function. DO. NOT. DO. THIS.
template<typename T, size_t N, size_t C, size_t P> inline void what() const noexcept;
// OK use parameters like a normal person, don't be afraid to use them :)
template<typename T> inline void what(size_t n, size_t c, size_t p) const noexcept;
```
-126
View File
@@ -1,126 +0,0 @@
# Coding Style guidelines
These are mostly "suggestions", if you feel like your code is readable, comprehensible to others; and most importantly doesn't result in unreadable spaghetti you're fine to go.
But for new developers you may find that following these guidelines will make everything x10 easier.
## Naming conventions
Simply put, types/classes are named as `PascalCase`, same for methods and functions like `AddElement`. Variables are named `like_this_snake_case` and constants are `IN_SCREAMING_CASE`.
Except for Qt MOC where `functionName` is preferred.
Template typenames prefer short names like `T`, `I`, `U`, if a longer name is required either `Iterator` or `perform_action` are fine as well. Do not use names like `SS` as systems like solaris define it for registers, in general do not use any of the following for short names:
- `SS`, `DS`, `GS`, `FS`: Segment registers, defined by Solaris `<ucontext.h>`
- `EAX`, `EBX`, `ECX`, `EDX`, `ESI`, `EDI`, `ESP`, `EBP`, `EIP`: Registers, defined by Solaris.
- `X`: Defined by some utility headers, avoid.
- `_`: Defined by gettext, avoid.
- `N`, `M`, `S`: Preferably don't use this for types, use it for numeric constants.
- `TR`: Used by some weird `<ucontext.h>` whom define the Task Register as a logical register to provide to the user... (Need to remember which OS in specific).
Macros must always be in `SCREAMING_CASE`. Do not use short letter macros as systems like Solaris will conflict with them; a good rule of thumb is >5 characters per macro - i.e `THIS_MACRO_IS_GOOD`, `AND_ALSO_THIS_ONE`.
Try not using hungarian notation, if you're able.
## Formatting
Formatting is extremelly lax, the general rule of thumb is: Don't add new lines just to increase line count. The less lines we have to look at, the better. This means also packing densely your code while not making it a clusterfuck. Strike a balance of "this is a short and comprehensible piece of code" and "my eyes are actually happy to see this!". Don't just drop the entire thing in a single line and call it "dense code", that's just spaghetti posing as code. In general, be mindful of what other devs need to look at.
Do not put if/while/etc braces after lines:
```c++
// no dont do this
// this is more lines of code for no good reason (why braces need their separate lines?)
// and those take space in someone's screen, cumulatively
if (thing)
{ //<--
some(); // ...
} //<-- 2 lines of code for basically "opening" and "closing" an statment
// do this
if (thing) { //<-- [...] and with your brain you can deduce it's this piece of code
// that's being closed
some(); // ...
} //<-- only one line, and it's clearer since you know its closing something [...]
// or this, albeit the extra line isn't needed (at your discretion of course)
if (thing)
some(); // ...
// this is also ok, keeps things in one line and makes it extremely clear
if (thing) some();
// NOT ok, don't be "clever" and use the comma operator to stash a bunch of statments
// in a single line, doing this will definitely ruin someone's day - just do the thing below
// vvv
if (thing) some(), thing(), a2(a1(), y1(), j1()), do_complex_shit(wa(), wo(), ploo());
// ... and in general don't use the comma operator for "multiple statments", EXCEPT if you think
// that it makes the code more readable (the situation may be rare however)
// Wow so much clearer! Now I can actually see what each statment is meant to do!
if (thing) {
some();
thing();
a2(a1(), y1(), j1());
do_complex_shit(wa(), wo(), ploo());
}
```
Brace rules are lax, if you can get the point across, do it:
```c++
// this is fine
do {
if (thing) {
return 0;
}
} while (other);
// this is also ok --- albeit a bit more dense
do if (thing) return 0; while (other);
// ok as well
do {
if (thing) return 0;
} while (other);
```
There is no 80-column limit but preferably be mindful of other developer's readability (like don't just put everything onto one line).
```c++
// someone is going to be mad due to this
SDL_AudioSpec obtained;
device_name.empty() ? device = SDL_OpenAudioDevice(nullptr, capture, &spec, &obtained, false) : device = SDL_OpenAudioDevice(device_name.c_str(), capture, &spec, &obtained, false);
// maybe consider this
SDL_AudioSpec obtained;
if (device_name.empty()) {
device = SDL_OpenAudioDevice(nullptr, capture, &spec, &obtained, false);
} else {
device = SDL_OpenAudioDevice(device_name.c_str(), capture, &spec, &obtained, false);
}
// or this is fine as well
SDL_AudioSpec obtained;
device = SDL_OpenAudioDevice(device_name.empty() ? nullptr : device_name.c_str(), capture, &spec, &obtained, false);
```
A note about operators: Use them sparingly, yes, the language is lax on them, but some usages can be... tripping to say the least.
```c++
a, b, c; //<-- NOT OK multiple statments with comma operator is definitely a recipe for disaster
return c ? a : b; //<-- OK ternaries at end of return statments are clear and fine
return a, b; //<-- NOT OK return will take value of `b` but also evaluate `a`, just use a separate statment
void f(int a[]) //<-- OK? if you intend to use the pointer as an array, otherwise just mark it as *
```
And about templates, use them sparingly, don't just do meta-templating for the sake of it, do it when you actually need it. This isn't a competition to see who can make the most complicated and robust meta-templating system. Just use what works, and preferably stick to the standard libary instead of reinventing the wheel. Additionally:
```c++
// NOT OK This will create (T * N * C * P) versions of the same function. DO. NOT. DO. THIS.
template<typename T, size_t N, size_t C, size_t P> inline void what() const noexcept;
// OK use parameters like a normal person, don't be afraid to use them :)
template<typename T> inline void what(size_t n, size_t c, size_t p) const noexcept;
```
+159
View File
@@ -0,0 +1,159 @@
# User Handbook - Adding Boolean Settings Toggles
> [!WARNING]
> This guide is intended for developers ONLY. If you are not a developer, this likely irrelevant to yourself.
>
> If you want to add temporary toggles, please refer to **[Adding Debug Knobs](AddingDebugKnobs.md)**
This guide will walk you through adding a new boolean toggle setting to Eden's configuration across both Qt's (PC) and Kotlin's (Android) UIs.
## Index
1. [Step 1 - Common Setting](#step-1-common-setting)
2. [Step 2 - Qt Toggle](#step-2-qt-toggle)
3. [Step 3 - Kotlin (Android)](#step-3-kotlin-android)
* [Step 3.1 - BooleanSetting.kt](#step-3-1-booleansetting-kt)
* [Step 3.2 - SettingsItem.kt](#step-3-2-settingsitem-kt)
* [Step 3.3 - SettingsFragmentPresenter.kt](#step-3-3-settingsfragmentpresenter-kt)
* [Step 3.4 - Localization](#step-3-4-localization)
4. [Step 4 - Use Your Toggle](#step-4-use-your-toggle)
5. [Best Practices](#best-practices)
---
## Step 1 - Common Setting
Firstly add your desired toggle:
Example: `src/common/setting.h`
```cpp
SwitchableSetting<bool> your_setting_name{linkage, false, "your_setting_name", Category::RendererExtensions};
```
### Remember to add your toggle to the appropriate category, for example:
Common Categories:
* Category::Renderer
* Category::RendererAdvanced
* Category::RendererExtensions
* Category::System
* Category::Core
> [!WARNING]
> If you wish for your toggle to be `on by default` then change `false` to `true` after `linkage,`.
---
## Step 2 - Qt Toggle
Add the toggle to the Qt UI, where you wish for it to appear and place it there.
Example: `src/qt_common/config/shared_translation.cpp`
```cpp
INSERT(Settings,
your_setting_name,
tr("Your Setting Display Name"),
tr("Detailed description of what this setting does.\n"
"You can use multiple lines.\n"
"Explain any caveats or requirements."));
```
### Make sure to:
* Keep display naming consistant
* Put detailed info in the description
* Use `\n` for line breaks in descriptions
---
## Step 3 - Kotlin (Android)
### Step 3.1 - BooleanSetting.kt
Add where it should be in the settings.
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt`
```kts
RENDERER_YOUR_SETTING_NAME("your_setting_name"),
```
### Make sure to:
* Ensure the prefix naming matches the intended category.
---
### Step 3.2 - SettingsItem.kt
Add the toggle to the Kotlin (Android) UI
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt`
```kts
put(
SwitchSetting(
BooleanSetting.RENDERER_YOUR_SETTING_NAME,
titleId = R.string.your_setting_name,
descriptionId = R.string.your_setting_name_description
)
)
```
---
### Step 3.3 - SettingsFragmentPresenter.kt
Add your setting within the right category.
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt`
```kts
add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key)
```
> [!WARNING]
> Remember, placing matters! Settings appear in the order of where you add them.
---
### Step 3.4 - Localization
Add your setting and description in the appropriate place.
Example: `src/android/app/src/main/res/values/strings.xml`
```xml
<string name="your_setting_name">Your Setting Display Name</string>
<string name="your_setting_name_description">Detailed description of what this setting does. Explain any caveats, requirements, or warnings here.</string>
```
---
## Step 4 - Use Your Toggle!
Now the UI part is done find a place in the code for the toggle,
And use it to your heart's desire!
Example:
```cpp
const bool your_value = Settings::values.your_setting_name.GetValue();
if (your_value) {
// Do something when enabled
}
```
If you wish to do something only when the toggle is disabled,
Use `if (!your_value) {` instead of `if (your_value) {`.
---
## Best Practices
* Naming - Use clear, descriptive names. Something for both the devs and the users.
* Defaults - Choose safe default values (usually false for new features).
* Documentation - Write clear descriptions explaining when and why to use the setting.
* Categories - Put settings in the appropriate category.
* Order - Place related settings near each other.
* Testing - Always test on both PC and Android before committing when possible.
### Thank you for reading, I hope this guide helped you making your toggle!
+167
View File
@@ -0,0 +1,167 @@
# User Handbook - Adding Debug Knobs
Debug Knobs is a 16-bit integer setting (`debug_knobs`) in the Eden Emulator that serves as a bitmask for gating various testing and debugging features. This allows developers and advanced users to enable or disable specific debug behaviors without requiring deploying of complete but temporary toggles.
The setting ranges from 0 to 65535 (0x0000 to 0xFFFF), where each bit represents a different debug feature flag.
## Index
1. [Advantages](#advantages)
2. [Usage](#usage)
* [Accessing Debug Knobs (dev side)](#accessing-debug-knobs-dev-side)
* [Setting Debug Knobs (user side)](#setting-debug-knobs-user-side)
* [Bit Manipulation Examples](#bit-manipulation-examples)
3. [Terminology and user communication](#terminology-and-user-communication)
4. [Examples](#examples)
* [Example 1: Conditional Debug Logging](#example-1-conditional-debug-logging)
* [Example 2: Performance Tuning](#example-2-performance-tuning)
* [Example 3: Feature Gating](#example-3-feature-gating)
5. [Best Practices](#best-practices)
---
## Advantages
The main advantage is to avoid deploying new disposable toggles (those made only for testing stage, and are disposed once new feature gets good to merge). This empowers devs to be free of all frontend burocracy and hassle of new toggles.
Common advantages recap:
* **Fine-Grained Control**: Enable or disable up to 16 individual debug features independently using bit manipulation on a single build
* **Runtime Configuration**: Change debug behavior at runtime the same way as new toggles would do
* **Safe incremental development**: New debug features can be added while impact can be isolated from previous deployments
## Usage
### Accessing Debug Knobs (dev side)
Use the `Settings::getDebugKnobAt(u8 i)` function to check if a specific bit is set:
```cpp
//cpp side
#include "common/settings.h"
// Check if bit 0 is set
bool feature_enabled = Settings::getDebugKnobAt(0);
// Check if bit 15 is set
bool another_feature = Settings::getDebugKnobAt(15);
```
```kts
//kotlin side
import org.yuzu.yuzu_emu.features.settings.model.Settings
// Check if bit x is set
bool feature_enabled = Settings.getDebugKnobAt(x); //x as integer from 0 to 15
```
The function returns `true` if the specified bit (0-15) is set in the `debug_knobs` value, `false` otherwise.
### Setting Debug Knobs (user side)
Developers must inform which knobs are tied to each functionality to be tested.
The debug knobs value can be set through:
1. **Desktop UI**: In the Debug configuration tab, there's a spinbox for "Debug knobs" (0-65535)
2. **Android UI**: Available as an integer setting in the Debug section
3. **Configuration Files**: Set the `debug_knobs` value in the emulator's configuration
### Bit Manipulation Examples
To enable specific features, calculate the decimal value by setting the appropriate bits:
* **Enable only bit 0**: Value = 1 (2^0)
* **Enable only bit 1**: Value = 2 (2^1)
* **Enable bits 0 and 1**: Value = 3 (2^0 + 2^1)
* **Enable bit 15**: Value = 32768 (2^15)
## Terminology and user communication
There are two main confusions when talking about knobs:
### Whether it's zero-based or one-based
Sometimes when an user reports: knobs 1 and 2 gets better performance, dev may get confuse whether he means the knobs 1 and 2 literally, or the 1st and 2nd knobs (knobs 0 and 1).
Debug knobs are **zero-based**, which means:
* The first knob is the knob(0) (or knob0 henceforth), and the last one is the 15 (knob15, likewise)
* You can talk: "knob0 is enabled/disabled", "In this video i was using only knobs 0 and 2", etc.
### Whether one is talking about the knob itself or about the entire parameter value (which represents all knobs)
Sometimes when an user reports: knob 3 results, it's unclear whether he's referring to knob setting with value 3 (which means both knob 0 and 1 are enabled), or to knob(3) specifically.
Whenever you're instructing tests or reporting results, be precise about whether one you're talking to avoid confusion:
### Setting based terminology
ALWAYS use the word in PLURAL (knobs), without mentioning which one, to refer to the setting, aka multiple knobs at once:
Examples:
- **knobs=0**: no knobs enabled
- **knobs=1**: knob0 enabled, others disabled
- **knobs=2**: knob1 enabled, others disabled
- **knobs=3**: knobs 0 and 1 enabled, others disabled
...
### Knob based terminology
Use the word in SINGULAR (knob), or in plural but referring which ones, when meaning multiple knobs at once:
Examples:
- **knob0**: knob 0 enabled, others disabled
- **knob1**: knob 1 enabled, others disabled
- **knobs 0 and 1**: knobs 0 and 1 enabled, others disabled
...
## Examples
### Example 1: Conditional Debug Logging
```cpp
void SomeFunction() {
if (Settings::getDebugKnobAt(0)) {
LOG_DEBUG(Common, "Debug feature 0 is enabled");
// Additional debug code here
}
if (Settings::getDebugKnobAt(1)) {
LOG_DEBUG(Common, "Debug feature 1 is enabled");
// Different debug behavior
}
}
```
### Example 2: Performance Tuning
```cpp
bool UseOptimizedPath() {
// Skip optimization if debug bit 2 is set for testing
return !Settings::getDebugKnobAt(2);
}
```
### Example 3: Feature Gating
```cpp
void ExperimentalFeature() {
static constexpr u8 EXPERIMENTAL_FEATURE_BIT = 3;
if (!Settings::getDebugKnobAt(EXPERIMENTAL_FEATURE_BIT)) {
// Fallback to stable implementation
StableImplementation();
return;
}
// Experimental implementation
ExperimentalImplementation();
}
```
## Best Practices
* This setting is intended for development and testing purposes only
* Knobs must be unwired before PR creation
* The setting is per-game configurable, allowing different debug setups for different titles
+1 -1
View File
@@ -40,7 +40,7 @@ Windows/riscv64 doesn't exist, and may never (until corporate greed no longer co
Android/riscv64 is interesting. While support for it may be added if and when RISC-V phones/handhelds ever go mainstream, arm64 devices will always be preferred due to NCE.
Only Fedora/riscv64 has been tested, but in theory, every riscv64 distribution that has *at least* the standard build tools, Qt, FFmpeg, and SDL3 should work.
Only Fedora/riscv64 has been tested, but in theory, every riscv64 distribution that has *at least* the standard build tools, Qt, FFmpeg, and SDL2 should work.
## Other
-8
View File
@@ -9,11 +9,3 @@ At the moment of writing, we do not support CFW such as Atmosphere, due to:
We do however, maintain HLE compatibility with the former mentioned CFW, applications that require Atmosphere to run will run fine in the emulator without any adjustments.
If they don't run - then that's a bug!
## Atmosphere
Fusee Galee, the bootloader and other low-level mechanisms are not emulated at the moment.
Having OFW is recommended, but may not be required (untested).
Extract the contents of Atmosphere into `sdmc`. Then to launch simply use `-hlaunch` instead (orthogonal to `-qlaunch`).
+1 -6
View File
@@ -1,22 +1,17 @@
# User Handbook - Command Line
There are two main applications, an SDL-based app (`eden-cli`) and a Qt based app (`eden`); both accept command line arguments.
There are two main applications, an SDL2 based app (`eden-cli`) and a Qt based app (`eden`); both accept command line arguments.
## eden
- `./eden <path>`: Running with a single argument and nothing else, will make the emulator look for the given file and load it, this behaviour is similar to `eden-cli`; allows dragging and dropping games into the application.
- `-g <path>`: Alternate way to specify what to load, overrides. However let it be noted that arguments that use `-` will be treated as options/ignored, if your game, for some reason, starts with `-`, in order to safely handle it you may need to specify it as an argument.
- `-f`: Use fullscreen.
- `-u <number>`: Select the index of the user to load as.
- `-input-profile <name>`: Specifies input profile name to use (for player #0 only).
- `-qlaunch`: Launch QLaunch.
- `-hlaunch`: Launch homebrew launcher `nx-hbloader`.
- Requires a copy of Atmosphere to be extracted onto `sdmc`.
- This is a shorthand for `<eden folder>/sdmc/atmosphere/hbl.nsp`.
- `-setup`: Launch setup applet.
## eden-cli
- `--debug/-d`: Enter debug mode, allow gdb stub at port `1234`
- `--config/-c`: Specify alternate configuration file.
- `--fullscreen/-f`: Set fullscreen.
-4
View File
@@ -44,10 +44,6 @@ Various graphical filters exist - each of them aimed at a specific target/image
- **MMPX**: Nearest-neighbour filter aimed at providing higher pixel-art quality.
- **Pros**: Offers decent pixel-art upscaling.
- **Cons**: Only works for pixel-art.
- **SGSR**: Uses Snapdragon Studios Game Super Resolution to enhance image quality (similar to FSR, but for Adreno devices).
- **Pros**: Optimized for Adreno devices.
- **Cons**: Doesn't play nicely with non-Adreno devices.
- **SGSR Edge**: Almost the same pipeline as SGSR, but with improved edge detection.
### Anisotropy values
+1 -1
View File
@@ -1,4 +1,4 @@
# User Handbook - Multiplayer
# Multiplayer
Use this guide to answer questions regarding and to start using the multiplayer functionality of Eden.
## Multiplayer FAQ
+7 -8
View File
@@ -25,12 +25,12 @@ A copy of this handbook is [available online](https://git.eden-emu.dev/eden-emu/
- **[Importing Saves](./ImportingSaves.md)**
- **[Installing Atmosphere Mods](./InstallingAtmosphereMods.md)**
- **[Installing Updates & DLCs](./InstallingUpdatesDLC.md)**
- **[Multiplayer](./Multiplayer.md)**
- **[Alter Date & Time](./AlterDateTime.md)**
## 3rd-party Integration
- **[Configuring Steam ROM Manager](./SteamROM.md)**
- **[Server hosting](./ServerHosting.md)**
- **[Server hosting](ServerHosting.md)**
- **[Syncthing Guide](./SyncthingGuide.md)**
- **[Third Party](./ThirdParty.md)**
- **[Obtainium](./ThirdParty.md#configuring-obtainium)**
@@ -40,13 +40,12 @@ A copy of this handbook is [available online](https://git.eden-emu.dev/eden-emu/
## Advanced
- **[Command Line](./CommandLine.md)**
- **[Custom Firmware](./CFW.md)**
- **[Alter Date & Time](./AlterDateTime.md)**
- **[How To Access Logs](./HowToAccessLogs.md)**
- **[Gyro Controls](./GyroControls.md)**
- **[Platforms and Architectures](./Architectures.md)**
- **[Native Application Development](./Native.md)**
- **[Adding Boolean Settings Toggles](./AddingBooleanToggles.md)**
- **[Platforms and Architectures](Architectures.md)**
- **[Command Line](CommandLine.md)**
- **[Native Application Development](Native.md)**
- **[Adding Boolean Settings Toggles](AddingBooleanToggles.md)**
- **[Adding Debug Knobs](./AddingDebugKnobs.md)**
- **[Testing](./Testing.md)**
- **[Testing](Testing.md)**
+12 -21
View File
@@ -82,11 +82,6 @@ if (ARCHITECTURE_riscv64)
AddJsonPackage(biscuit)
endif()
# Lagoon
if (ARCHITECTURE_loongarch64)
AddJsonPackage(lagoon)
endif()
# Vulkan stuff
AddDependentPackages(vulkan-headers vulkan-utility-libraries)
@@ -141,7 +136,8 @@ if(ENABLE_CUBEB)
endif()
if (NOT ANDROID)
if (NOT YUZU_USE_BUNDLED_SDL3)
if (YUZU_USE_EXTERNAL_SDL2)
message(STATUS "Using SDL2 from externals.")
if (NOT WIN32)
# Yuzu itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers
# Since 2.0.18 Atomic+Threads required for HIDAPI/libusb (see https://github.com/libsdl-org/SDL/issues/5095)
@@ -162,26 +158,21 @@ if (NOT ANDROID)
set(SDL_FILE ON)
endif()
AddJsonPackage(sdl3)
else()
message(STATUS "Using bundled SDL3")
if ("${YUZU_SYSTEM_PROFILE}" STREQUAL "steamdeck")
set(SDL_PIPEWIRE OFF) # build errors out with this on
AddJsonPackage("sdl2_steamdeck")
else()
AddJsonPackage("sdl2_generic")
endif()
elseif (YUZU_USE_BUNDLED_SDL2)
message(STATUS "Using bundled SDL2")
if (PLATFORM_FREEBSD)
set(BUILD_SHARED_LIBS ON)
endif()
AddJsonPackage(sdl3-ci)
AddJsonPackage(sdl2)
endif()
# Normalize SDL3 link target across package variants.
# Some SDL3 packages export only SDL3::SDL3-shared or SDL3::SDL3-static.
if (NOT TARGET SDL3::SDL3)
if (TARGET SDL3::SDL3-shared)
add_library(SDL3::SDL3 ALIAS SDL3::SDL3-shared)
elseif (TARGET SDL3::SDL3-static)
add_library(SDL3::SDL3 ALIAS SDL3::SDL3-static)
else()
message(FATAL_ERROR "SDL3 package found, but no usable SDL3 target was exported")
endif()
endif()
find_package(SDL2 2.26.4 REQUIRED)
endif()
set(BUILD_SHARED_LIBS OFF)
+33 -30
View File
@@ -9,7 +9,7 @@
},
"sirit": {
"repo": "eden-emulator/sirit",
"git_version": "1.0.5",
"git_version": "1.0.4",
"tag": "v%VERSION%",
"artifact": "sirit-source-%VERSION%.tar.zst",
"hash_suffix": "sha512sum",
@@ -28,8 +28,8 @@
"httplib": {
"repo": "yhirose/cpp-httplib",
"tag": "v%VERSION%",
"hash": "5133e3378b8b26c12e0634bb4782d3a5f6e98530149a3b2e54e18955ffd17e3997fd6196e6a4351d7ea1d4fa1826760418a92f9849b0b4a2d52308f6e0670b5d",
"git_version": "0.46.1",
"hash": "5efa8140aadffe105dcf39935b732476e95755f6c7473ada3d0b64df2bc02c557633ae3948a25b45e1cf67e89a3ff6329fb30362e4ac033b9a1d1e453aa2eded",
"git_version": "0.37.0",
"find_args": "MODULE GLOBAL",
"patches": [
"0001-mingw.patch",
@@ -57,8 +57,8 @@
"package": "xbyak",
"repo": "herumi/xbyak",
"tag": "v%VERSION%",
"hash": "03d870f63741ff8c00501329bf9947bdf58e7cc4c3884e3de9b827f31942438887555f601a5f80d405946854eb54ab907b4e2a3ad955ca4ad890a243917dd4ae",
"git_version": "7.37.3"
"hash": "b6475276b2faaeb315734ea8f4f8bd87ededcee768961b39679bee547e7f3e98884d8b7851e176d861dab30a80a76e6ea302f8c111483607dde969b4797ea95a",
"git_version": "7.35.2"
},
"oaknut": {
"repo": "eden-emulator/oaknut",
@@ -67,12 +67,6 @@
"tag": "v%VERSION%",
"hash": "9697e80a7d5d9bcb3ce51051a9a24962fb90ca79d215f1f03ae6b58da8ba13a63b5dda1b4dde3d26ac6445029696b8ef2883f4e5a777b342bba01283ed293856"
},
"lagoon": {
"repo": "loongson-community/lagoon",
"tag": "%VERSION%",
"version": "1.0.0",
"hash": "b9380f99c6effaeccc6d8f81d4942e852c11ad28613df637e155451556ae5826f93765bee57a5c87a9740d2bd1db463ad0f55a947772fe9d57eeabae3efa373e"
},
"libadrenotools": {
"repo": "eden-emulator/libadrenotools",
"sha": "8ba23b42d7",
@@ -128,21 +122,21 @@
"BUNDLE_SPEEX ON"
]
},
"sdl3-ci": {
"sdl2": {
"ci": true,
"package": "SDL3",
"name": "SDL3",
"repo": "crueter-ci/SDL3",
"version": "3.4.8-d57c3b685c",
"min_version": "3.2.10"
"package": "SDL2",
"name": "SDL2",
"repo": "crueter-ci/SDL2",
"version": "2.32.10-3c28e8ecc0",
"min_version": "2.26.4"
},
"catch2": {
"package": "Catch2",
"repo": "catchorg/Catch2",
"tag": "v%VERSION%",
"hash": "09df8b291d0c756c492871ce0a4fb5c703162d1364a9864470b227d4d19d9865a0ba5293a5f38d0117cbc7c230f42be35db9f6e0ca5d889dff02f1745807f380",
"hash": "7eea385d79d88a5690cde131fe7ccda97d5c54ea09d6f515000d7bf07c828809d61c1ac99912c1ee507cf933f61c1c47ecdcc45df7850ffa82714034b0fccf35",
"version": "3.0.1",
"git_version": "3.15.0",
"git_version": "3.13.0",
"patches": [
"0001-solaris-isnan-fix.patch"
]
@@ -162,13 +156,22 @@
"find_args": "MODULE",
"git_version": "4.25"
},
"sdl3": {
"package": "SDL3",
"sdl2_generic": {
"package": "SDL2",
"repo": "libsdl-org/SDL",
"tag": "release-%VERSION%",
"hash": "10a9c1fa4d6891c7387fa694b786a168f86b6e5447c6c522f85b739e33720e106ee879dfa9e6e537d5521b983cfbd4908927c555c4d3412735129a4231e16fe6",
"git_version": "3.4.10",
"version": "3.2.10"
"hash": "d5622d6bb7266f7942a7b8ad43e8a22524893bf0c2ea1af91204838d9b78d32768843f6faa248757427b8404b8c6443776d4afa6b672cd8571a4e0c03a829383",
"bundled": true,
"git_version": "2.32.10",
"skip_updates": true
},
"sdl2_steamdeck": {
"package": "SDL2",
"repo": "libsdl-org/SDL",
"sha": "cc016b0046",
"hash": "b8d9873446cdb922387471df9968e078714683046674ef0d0edddf8e25da65a539a3bae83d635496b970237f90b07b36a69f8d7855d450de59311d6d6e8c3dbc",
"bundled": true,
"skip_updates": "true"
},
"moltenvk": {
"repo": "V380-Ori/Ryujinx.MoltenVK",
@@ -195,9 +198,9 @@
"libusb": {
"repo": "libusb/libusb",
"tag": "v%VERSION%",
"hash": "3251c9f41e900efa13caf981483f46886c8434bf4c30f3fd3073921b06be9977cebcf4a18f7bc46db86e33d7f19752296377be9050abfb8d887ffeb377864647",
"hash": "98c5f7940ff06b25c9aa65aa98e23de4c79a4c1067595f4c73cc145af23a1c286639e1ba11185cd91bab702081f307b973f08a4c9746576dc8d01b3620a3aeb5",
"find_args": "MODULE",
"git_version": "1.0.30",
"git_version": "1.0.29",
"patches": [
"0001-netbsd-gettime.patch"
]
@@ -230,15 +233,15 @@
"repo": "KhronosGroup/Vulkan-Headers",
"package": "VulkanHeaders",
"version": "1.4.317",
"hash": "dbb7a6def360aebc2a14df5cdeab0fe6455f37d7885d451a0aa307e82ea46193c262f77a79bea6a45530286d225b03687880a11e857279f339437069b866fa82",
"git_version": "1.4.352",
"hash": "d2846ea228415772645eea4b52a9efd33e6a563043dd3de059e798be6391a8f0ca089f455ae420ff22574939ed0f48ed7c6ff3d5a9987d5231dbf3b3f89b484b",
"git_version": "1.4.345",
"tag": "v%VERSION%"
},
"vulkan-utility-libraries": {
"repo": "KhronosGroup/Vulkan-Utility-Libraries",
"package": "VulkanUtilityLibraries",
"hash": "4aaaba1e6ffd235bdc2d7ae5dfdebb3024ff011c47157d838d839ea8c6824af5b5a26f4771c7fdc83a883b69ce766f993bba3ecad7dde2dec102c238e68f6210",
"git_version": "1.4.352",
"hash": "114f6b237a6dcba923ccc576befb5dea3f1c9b3a30de7dc741f234a831d1c2d52d8a224afb37dd57dffca67ac0df461eaaab6a5ab5e503b393f91c166680c3e1",
"git_version": "1.4.345",
"tag": "v%VERSION%"
},
"frozen": {
+6 -21
View File
@@ -1,6 +1,3 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
@@ -19,24 +16,15 @@ if (NOT FILE_LIST)
endif()
set(DIRECTORY_NAME ${HEADER_NAME})
set(FILE_DATA "")
string(APPEND FILE_DATA "[[nodiscard]] static inline std::vector<FileSys::VirtualFile> CollectFiles_${DIRECTORY_NAME}() {\n")
string(APPEND FILE_DATA [[
std::vector<FileSys::VirtualFile> vfs_files;
auto const fn = [&](std::string_view name, std::span<const u8> data) {
vfs_files.push_back(std::make_shared<FileSys::VectorVfsFile>(
std::vector<u8>(data.begin(), data.end()),
std::string{name}
));
};
]])
set(FILE_DATA "")
foreach(ZONE_FILE ${FILE_LIST})
if (ZONE_FILE STREQUAL "\n")
continue()
endif()
string(APPEND FILE_DATA " {\n")
string(APPEND FILE_DATA " constexpr uint8_t tzdb_data[] = {\n")
string(APPEND FILE_DATA "{\"${ZONE_FILE}\",\n{")
file(READ ${ZONE_PATH}/${ZONE_FILE} ZONE_DATA HEX)
string(LENGTH "${ZONE_DATA}" ZONE_DATA_LEN)
foreach(I RANGE 0 ${ZONE_DATA_LEN} 2)
@@ -54,12 +42,9 @@ foreach(ZONE_FILE ${FILE_LIST})
string(APPEND FILE_DATA " ")
endif()
endforeach()
string(APPEND FILE_DATA " };\n")
string(APPEND FILE_DATA " fn(\"${ZONE_FILE}\", tzdb_data);\n")
string(APPEND FILE_DATA " }\n")
string(APPEND FILE_DATA "}},\n")
endforeach()
string(APPEND FILE_DATA " return vfs_files;\n")
string(APPEND FILE_DATA "}\n")
file(READ ${NX_TZDB_SOURCE_DIR}/tzdb_template.h.in NX_TZDB_TEMPLATE_H_IN)
file(CONFIGURE OUTPUT ${NX_TZDB_INCLUDE_DIR}/nx_tzdb/${HEADER_NAME}.h CONTENT "${NX_TZDB_TEMPLATE_H_IN}")
+3 -3
View File
@@ -12,10 +12,10 @@
namespace NxTzdb {
// @DIRECTORY_NAME@
// clang-format off
@FILE_DATA@
const static std::map<const char*, const std::vector<uint8_t>> @DIRECTORY_NAME@ =
{
@FILE_DATA@};
// clang-format on
} // namespace NxTzdb
+1 -1
View File
@@ -20,7 +20,7 @@ pkgs.mkShellNoCC {
qt6.qtbase qt6.qtmultimedia qt6.qtwayland qt6.qttools
qt6.qtwebengine qt6.qt5compat
# eden-cli
SDL3
SDL2
# optional components
discord-rpc gamemode
];
+2 -5
View File
@@ -8,7 +8,7 @@
include_directories(.)
# Dynarmic
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_loongarch64) AND NOT YUZU_STATIC_ROOM)
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64) AND NOT YUZU_STATIC_ROOM)
add_subdirectory(dynarmic)
add_library(dynarmic::dynarmic ALIAS dynarmic)
endif()
@@ -180,10 +180,7 @@ else()
endif()
if (ARCHITECTURE_x86_64)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-mcx16>)
if (PLATFORM_LINUX OR PLATFORM_FREEBSD)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-mtls-dialect=gnu2>)
endif()
add_compile_options(-mcx16)
endif()
if (APPLE AND CXX_CLANG)
@@ -15,6 +15,7 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.Rect
import android.graphics.drawable.Icon
@@ -100,6 +101,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
private var romSwapGeneration = 0
private var hasEmulationSession = processHasEmulationSession
private val romSwapStopTimeoutRunnable = Runnable { onRomSwapStopTimeout() }
private val pictureInPictureFailureActions: MutableSet<String> = mutableSetOf()
private fun onRomSwapStopTimeout() {
if (!isWaitingForRomSwapStop) {
@@ -125,8 +127,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
super.onCreate(savedInstanceState)
NativeConfig.reloadGlobalConfig()
InputHandler.updateControllerData()
val players = NativeConfig.getInputSettings(true)
var hasConfiguredControllers = false
@@ -268,12 +268,18 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
}
override fun onUserLeaveHint() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) {
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
enterPictureInPictureMode(pictureInPictureParamsBuilder.build())
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ||
!isPictureInPictureSupported() ||
!BooleanSetting.PICTURE_IN_PICTURE.getBoolean() ||
isInPictureInPictureMode
) {
return
}
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
runPictureInPictureAction("enter picture-in-picture mode") {
enterPictureInPictureMode(pictureInPictureParamsBuilder.build())
}
}
@@ -653,7 +659,29 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
return this.apply { setActions(pictureInPictureActions) }
}
private fun isPictureInPictureSupported() =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
private fun runPictureInPictureAction(actionName: String, action: () -> Unit) {
try {
action()
} catch (e: IllegalStateException) {
if (pictureInPictureFailureActions.add(actionName)) {
Log.warning("[PiP] Failed to $actionName: ${e.message}")
}
} catch (e: UnsupportedOperationException) {
if (pictureInPictureFailureActions.add(actionName)) {
Log.warning("[PiP] Failed to $actionName: ${e.message}")
}
}
}
fun buildPictureInPictureParams() {
if (!isPictureInPictureSupported()) {
return
}
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
@@ -663,7 +691,9 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && isEmulationActive
)
}
setPictureInPictureParams(pictureInPictureParamsBuilder.build())
runPictureInPictureAction("set picture-in-picture params") {
setPictureInPictureParams(pictureInPictureParamsBuilder.build())
}
}
fun displayMultiplayerDialog() {
@@ -16,7 +16,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_USE_SPEED_LIMIT("use_speed_limit"),
USE_CUSTOM_CPU_TICKS("use_custom_cpu_ticks"),
SKIP_CPU_INNER_INVALIDATION("skip_cpu_inner_invalidation"),
ANTIFLICKER("antiflicker"),
FIX_BLOOM_EFFECTS("fix_bloom_effects"),
EMULATE_BGR565("emulate_bgr565"),
RESCALE_HACK("rescale_hack"),
@@ -750,13 +750,6 @@ abstract class SettingsItem(
descriptionId = R.string.skip_cpu_inner_invalidation_description
)
)
put(
SwitchSetting(
BooleanSetting.ANTIFLICKER,
titleId = R.string.antiflicker,
descriptionId = R.string.antiflicker_description
)
)
put(
SwitchSetting(
BooleanSetting.FIX_BLOOM_EFFECTS,
@@ -76,25 +76,18 @@ class SettingsFragmentPresenter(
}
}
private fun isSharpnessScalingFilterSelected(): Boolean {
private fun isFsrScalingFilterSelected(): Boolean {
val fsrFilterValue = resolveFsrScalingFilterValue() ?: return false
val needsGlobal = getNeedsGlobalForKey(IntSetting.RENDERER_SCALING_FILTER.key)
val selectedFilter = IntSetting.RENDERER_SCALING_FILTER.getInt(needsGlobal)
return selectedFilter in resolveSharpnessScalingFilterValues()
return selectedFilter == fsrFilterValue
}
private fun resolveSharpnessScalingFilterValues(): Set<Int> {
private fun resolveFsrScalingFilterValue(): Int? {
val names = context.resources.getStringArray(R.array.rendererScalingFilterNames)
val values = context.resources.getIntArray(R.array.rendererScalingFilterValues)
val sharpnessFilterNames = setOf(
context.getString(R.string.scaling_filter_fsr),
context.getString(R.string.scaling_filter_sgsr),
context.getString(R.string.scaling_filter_sgsr_edge),
)
return names.asSequence()
.mapIndexedNotNull { index, name ->
if (name in sharpnessFilterNames && index in values.indices) values[index] else null
}
.toSet()
val fsrIndex = names.indexOf(context.getString(R.string.scaling_filter_fsr))
return if (fsrIndex in values.indices) values[fsrIndex] else null
}
// Allows you to show/hide abstract settings based on the paired setting key
@@ -274,7 +267,7 @@ class SettingsFragmentPresenter(
add(IntSetting.RENDERER_RESOLUTION.key)
add(IntSetting.RENDERER_VSYNC.key)
add(IntSetting.RENDERER_SCALING_FILTER.key)
if (isSharpnessScalingFilterSelected()) {
if (isFsrScalingFilterSelected()) {
add(IntSetting.FSR_SHARPENING_SLIDER.key)
}
add(IntSetting.RENDERER_ANTI_ALIASING.key)
@@ -298,7 +291,6 @@ class SettingsFragmentPresenter(
add(IntSetting.FAST_GPU_TIME.key)
add(BooleanSetting.SKIP_CPU_INNER_INVALIDATION.key)
add(BooleanSetting.ANTIFLICKER.key)
add(BooleanSetting.FIX_BLOOM_EFFECTS.key)
add(BooleanSetting.EMULATE_BGR565.key)
add(BooleanSetting.RESCALE_HACK.key)
@@ -1090,7 +1090,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private fun addQuickSettings() {
binding.quickSettingsSheet.apply {
val container = binding.quickSettingsSheet.findViewById<ViewGroup>(R.id.quick_settings_container)
val isSharpnessFilterSelected = isSharpnessScalingFilterSelected()
val isFsrSelected = isFsrScalingFilterSelected()
container.removeAllViews()
@@ -1176,7 +1176,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
addQuickSettings()
}
if (isSharpnessFilterSelected) {
if (isFsrSelected) {
quickSettings.addSliderSetting(
R.string.fsr_sharpness,
container,
@@ -1197,24 +1197,17 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
private fun isSharpnessScalingFilterSelected(): Boolean {
private fun isFsrScalingFilterSelected(): Boolean {
val fsrFilterValue = resolveFsrScalingFilterValue() ?: return false
val selectedFilter = IntSetting.RENDERER_SCALING_FILTER.getInt(needsGlobal = false)
return selectedFilter in resolveSharpnessScalingFilterValues()
return selectedFilter == fsrFilterValue
}
private fun resolveSharpnessScalingFilterValues(): Set<Int> {
private fun resolveFsrScalingFilterValue(): Int? {
val names = resources.getStringArray(R.array.rendererScalingFilterNames)
val values = resources.getIntArray(R.array.rendererScalingFilterValues)
val sharpnessFilterNames = setOf(
getString(R.string.scaling_filter_fsr),
getString(R.string.scaling_filter_sgsr),
getString(R.string.scaling_filter_sgsr_edge),
)
return names.asSequence()
.mapIndexedNotNull { index, name ->
if (name in sharpnessFilterNames && index in values.indices) values[index] else null
}
.toSet()
val fsrIndex = names.indexOf(getString(R.string.scaling_filter_fsr))
return if (fsrIndex in values.indices) values[fsrIndex] else null
}
private fun openQuickSettingsMenu() {
@@ -19,9 +19,6 @@ AndroidConfig::AndroidConfig(const std::string& config_name, ConfigType config_t
}
void AndroidConfig::ReloadAllValues() {
// Ensure the INI file is current before reloading values.
SetUpIni();
Reload();
ReadAndroidValues();
SaveAndroidValues();
@@ -503,8 +503,6 @@
<string name="fast_gpu_time_description">يُجبر هذا الخيار معظم الألعاب على العمل بأعلى دقة عرض أصلية. استخدم 256 للحصول على أقصى أداء و512 للحصول على أعلى جودة رسومات.</string>
<string name="skip_cpu_inner_invalidation">تخطي إبطال صلاحية وحدة المعالجة المركزية الداخلية</string>
<string name="skip_cpu_inner_invalidation_description">يتخطى بعض عمليات إبطال ذاكرة التخزين المؤقتة من جانب وحدة المعالجة المركزية أثناء تحديثات الذاكرة، مما يقلل من استخدام وحدة المعالجة المركزية ويحسن أداءها. قد يتسبب ذلك في حدوث أعطال أو تعطل في بعض الألعاب.</string>
<string name="antiflicker">مضاد الوميض</string>
<string name="antiflicker_description">يُجبر هذا الوضع وظائف وحدة معالجة الرسومات على الانتظار حتى يتم إرسال العمل إليها. استخدمه مع وضع وحدة معالجة الرسومات السريع لتجنب الوميض مع تأثير أقل على الأداء.</string>
<string name="fix_bloom_effects">إصلاح تأثيرات التوهج</string>
<string name="fix_bloom_effects_description">يقلل من ضبابية التوهج في LA/EOW (Adreno A6XX - A7XX/ Turnip)، ويزيل التوهج في Burnout. تحذير: قد يسبب تشوهات رسومية في ألعاب أخرى.</string>
<string name="emulate_bgr565">محاكاة BGR565</string>
@@ -497,8 +497,6 @@
<string name="fast_gpu_time_description">Fuerza a la mayoría de los juegos a ejecutarse a su resolución nativa más alta. Usa 256 para un máximo rendimiento y 512 para una fidelidad gráfica óptima.</string>
<string name="skip_cpu_inner_invalidation">Omitir invalidación interna de la CPU</string>
<string name="skip_cpu_inner_invalidation_description">Omite ciertas invalidaciones de caché de la CPU durante las actualizaciones de memoria, lo que reduce el uso de la CPU y mejora su rendimiento. Esto puede causar fallos o bloqueos en algunos juegos.</string>
<string name="antiflicker">Antiparpadeo</string>
<string name="antiflicker_description">Fuerza a las funciones de devolución de llamada de la GPU a esperar a que se envíen las tareas a la GPU.\nÚsalo con el modo de GPU rápida para evitar el parpadeo con un menor impacto en el rendimiento.</string>
<string name="fix_bloom_effects">Arreglar los efectos de resplandor</string>
<string name="fix_bloom_effects_description">Reduce el efecto de resplandor en LA/EOW (Adreno A6XX - A7XX/ Turnip), elimina el resplandor en Burnout. Advertencia: puede causar artefactos gráficos en otros juegos.</string>
<string name="emulate_bgr565">Emular BGR565</string>
@@ -499,8 +499,6 @@
<string name="fast_gpu_time_description">Принудительно запускает большинство игр в их максимальном нативном разрешении. Используйте значение 256 для максимальной производительности и 512 для максимального качества графики.</string>
<string name="skip_cpu_inner_invalidation">Пропустить внутреннюю инвалидацию ЦП</string>
<string name="skip_cpu_inner_invalidation_description">Пропускает некоторые инвалидации кэша на стороне ЦП при обновлениях памяти, уменьшая нагрузку на процессор и повышая производительность. Может вызывать сбои в некоторых играх.</string>
<string name="antiflicker">Анти-мерцание</string>
<string name="antiflicker_description">Принудительно заставляет обратные вызовы ГПУ-фильтра ожидать выполнения отправленных задач на ГПУ. Используйте с Быстрым режимом ГПУ, что бы избежать мерцаний с меньшим влиянием на производительность.</string>
<string name="fix_bloom_effects">Исправить эффекты размытия</string>
<string name="fix_bloom_effects_description">Частично убирает размытие в LA/EOW (Adreno A6XX - A7XX/ Turnip), полностью отключает его в Burnout. Внимание: может вызывать графические артефакты в других играх.</string>
<string name="emulate_bgr565">Эмулировать BGR565</string>
@@ -499,8 +499,6 @@
<string name="fast_gpu_time_description">Примушує більшість ігор працювати на їхній максимальній нативній роздільності. Використовуйте 256 для максимальної продуктивності та 512 для найкращої якості.</string>
<string name="skip_cpu_inner_invalidation">Пропустити внутрішнє інвалідування CPU</string>
<string name="skip_cpu_inner_invalidation_description">Пропускає деякі інвалідації кешу на стороні CPU під час оновлення пам\'яті, зменшуючи навантаження на процесор і покращуючи продуктивність. Може спричинити збої в деяких іграх.</string>
<string name="antiflicker">Антимерехтіння</string>
<string name="antiflicker_description">Змушує механізм синхронізації чекати, доки ГП завершить подані завдання. Використовуйте з режимом ГП «Швидко», щоб уникнути мерехтіння з меншими втратами продуктивності.</string>
<string name="fix_bloom_effects">Виправити ефекти світіння</string>
<string name="fix_bloom_effects_description">Зменшує розмиття світіння в LA/EOW (Adreno A6XXA7XX / Turnip), прибирає світіння в Burnout. Увага: може спричинити графічні артефакти в інших іграх.</string>
<string name="emulate_bgr565">Емулювати BGR565</string>
@@ -493,8 +493,6 @@
<string name="fast_gpu_time_description">强制大多数游戏以其最高原生分辨率运行。设置为 256 可获得最佳性能,设置为 512 可获得最佳画面保真度。</string>
<string name="skip_cpu_inner_invalidation">跳过CPU内部无效化</string>
<string name="skip_cpu_inner_invalidation_description">在内存更新期间跳过某些CPU端缓存无效化,减少CPU使用率并提高其性能。可能会导致某些游戏出现故障或崩溃。</string>
<string name="antiflicker">防闪烁</string>
<string name="antiflicker_description">强制 GPU 围栏回调等待已提交的 GPU 任务。配合“快速 GPU 模式”一起使用,以避免画面闪烁现象,仅会牺牲少量性能。</string>
<string name="fix_bloom_effects">修复 Bloom 效果</string>
<string name="fix_bloom_effects_description">减少《塞尔达传说:智慧的再现》(Adreno A6XX - A7XX/ Turnip)中的 bloom 模糊,并移除《Burnout》中的 bloom 效果。警告:可能会导致在其他游戏中出现图形异常。</string>
<string name="emulate_bgr565">模拟 BGR565</string>
@@ -240,8 +240,6 @@
<item>@string/scaling_filter_bspline</item>
<item>@string/scaling_filter_mitchell</item>
<item>@string/scaling_filter_spline1</item>
<item>@string/scaling_filter_sgsr</item>
<item>@string/scaling_filter_sgsr_edge</item>
</string-array>
<integer-array name="rendererScalingFilterValues">
@@ -258,8 +256,6 @@
<item>10</item>
<item>11</item>
<item>12</item>
<item>13</item>
<item>14</item>
</integer-array>
<string-array name="rendererAntiAliasingNames">
@@ -468,8 +468,8 @@
<string name="renderer_resolution">Resolution (Handheld/Docked)</string>
<string name="renderer_vsync">VSync mode</string>
<string name="renderer_scaling_filter">Window adapting filter</string>
<string name="fsr_sharpness">FSR/SGSR sharpness</string>
<string name="fsr_sharpness_description">Determines how sharpened the image will look while using FSR or SGSR filters</string>
<string name="fsr_sharpness">FSR sharpness</string>
<string name="fsr_sharpness_description">Determines how sharpened the image will look while using FSR\'s dynamic contrast</string>
<string name="renderer_anti_aliasing">Anti-aliasing method</string>
@@ -509,8 +509,6 @@
<string name="fast_gpu_time_description">Forces most games to run at their highest native resolution. Use 256 for maximal performance and 512 for maximal graphics fidelity.</string>
<string name="skip_cpu_inner_invalidation">Skip CPU Inner Invalidation</string>
<string name="skip_cpu_inner_invalidation_description">Skips certain CPU-side cache invalidations during memory updates, reducing CPU usage and improving it\'s performance. This may cause glitches or crashes on some games.</string>
<string name="antiflicker">Anti-Flicker</string>
<string name="antiflicker_description">Forces GPU fence callbacks to wait for submitted GPU work. Use with Fast GPU mode, to avoid flicker with lower performance impact.</string>
<string name="fix_bloom_effects">Fix Bloom Effects</string>
<string name="fix_bloom_effects_description">Reduces bloom blur in LA/EOW (Adreno A6XX - A7XX/ Turnip), removes bloom in Burnout. Warning: may cause graphical artifacts in other games.</string>
<string name="emulate_bgr565">Emulate BGR565</string>
@@ -1080,8 +1078,6 @@
<string name="scaling_filter_bspline" translatable="false">B-Spline</string>
<string name="scaling_filter_mitchell" translatable="false">Mitchell</string>
<string name="scaling_filter_mmpx" translatable="false">MMPX</string>
<string name="scaling_filter_sgsr" translatable="false">Snapdragon GSR</string>
<string name="scaling_filter_sgsr_edge" translatable="false">Snapdragon GSR EdgeDir</string>
<!-- Anti-Aliasing -->
<string name="anti_aliasing_none">None</string>
+4 -4
View File
@@ -247,11 +247,11 @@ if(ANDROID)
target_compile_definitions(audio_core PUBLIC HAVE_OBOE)
else()
target_sources(audio_core PRIVATE
sink/sdl3_sink.cpp
sink/sdl3_sink.h)
sink/sdl2_sink.cpp
sink/sdl2_sink.h)
target_link_libraries(audio_core PRIVATE SDL3::SDL3)
target_compile_definitions(audio_core PRIVATE HAVE_SDL3)
target_link_libraries(audio_core PRIVATE SDL2::SDL2)
target_compile_definitions(audio_core PRIVATE HAVE_SDL2)
endif()
create_target_directory_groups(audio_core)
@@ -29,96 +29,8 @@ void AudioRenderer::Start() {
CreateSinkStreams();
mailbox.Initialize(AppMailboxId::AudioRenderer);
// Main AudioRenderer thread, responsible for processing the command lists.
main_thread = std::jthread([this](std::stop_token stop_token) {
Common::SetCurrentThreadName("DSP_AudioRenderer_Main");
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
// TODO: Create buffer map/unmap thread + mailbox
// TODO: Create gMix devices, initialize them here
if (mailbox.Receive(Direction::DSP) != Message::InitializeOK) {
LOG_ERROR(Service_Audio, "ADSP Audio Renderer -- Failed to receive initialize message from host!");
return;
}
mailbox.Send(Direction::Host, Message::InitializeOK);
// 0.12 seconds (2,304,000 / 19,200,000)
constexpr u64 max_process_time{2'304'000ULL};
while (!stop_token.stop_requested()) {
auto msg{mailbox.Receive(Direction::DSP)};
switch (msg) {
case Message::Shutdown:
mailbox.Send(Direction::Host, Message::Shutdown);
return;
case Message::Render: {
if (system.IsShuttingDown()) {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
mailbox.Send(Direction::Host, Message::RenderResponse);
continue;
}
std::array<bool, MaxRendererSessions> buffers_reset{};
std::array<u64, MaxRendererSessions> render_times_taken{};
const auto start_time{system.CoreTiming().GetGlobalTimeUs().count()};
for (u32 index = 0; index < MaxRendererSessions; index++) {
auto& command_buffer{command_buffers[index]};
auto& command_list_processor{command_list_processors[index]};
// Check this buffer is valid, as it may not be used.
if (command_buffer.buffer != 0) {
// If there are no remaining commands (from the previous list),
// this is a new command list, initialize it.
if (command_buffer.remaining_command_count == 0) {
command_list_processor.Initialize(system, *command_buffer.process,
command_buffer.buffer,
command_buffer.size, streams[index]);
}
if (command_buffer.reset_buffer && !buffers_reset[index]) {
streams[index]->ClearQueue();
buffers_reset[index] = true;
}
u64 max_time{max_process_time};
if (index == 1 && command_buffer.applet_resource_user_id ==
command_buffers[0].applet_resource_user_id) {
max_time = max_process_time - render_times_taken[0];
if (render_times_taken[0] > max_process_time) {
max_time = 0;
}
}
max_time = (std::min)(command_buffer.time_limit, max_time);
command_list_processor.SetProcessTimeMax(max_time);
if (index == 0) {
streams[index]->WaitFreeSpace(stop_token);
}
// Process the command list
{
render_times_taken[index] =
command_list_processor.Process(index) - start_time;
}
const auto end_time{system.CoreTiming().GetGlobalTimeUs().count()};
command_buffer.remaining_command_count =
command_list_processor.GetRemainingCommandCount();
command_buffer.render_time_taken_us = end_time - start_time;
}
}
mailbox.Send(Direction::Host, Message::RenderResponse);
} break;
default:
LOG_WARNING(Service_Audio, "ADSP AudioRenderer received an invalid message, msg={:02X}!", msg);
break;
}
}
});
main_thread = std::jthread([this](std::stop_token stop_token) { Main(stop_token); });
mailbox.Send(Direction::DSP, Message::InitializeOK);
if (mailbox.Receive(Direction::Host) != Message::InitializeOK) {
@@ -217,4 +129,95 @@ void AudioRenderer::CreateSinkStreams() {
}
}
void AudioRenderer::Main(std::stop_token stop_token) {
Common::SetCurrentThreadName("DSP_AudioRenderer_Main");
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
// TODO: Create buffer map/unmap thread + mailbox
// TODO: Create gMix devices, initialize them here
if (mailbox.Receive(Direction::DSP) != Message::InitializeOK) {
LOG_ERROR(Service_Audio, "ADSP Audio Renderer -- Failed to receive initialize message from host!");
return;
}
mailbox.Send(Direction::Host, Message::InitializeOK);
// 0.12 seconds (2,304,000 / 19,200,000)
constexpr u64 max_process_time{2'304'000ULL};
while (!stop_token.stop_requested()) {
auto msg{mailbox.Receive(Direction::DSP)};
switch (msg) {
case Message::Shutdown:
mailbox.Send(Direction::Host, Message::Shutdown);
return;
case Message::Render: {
if (system.IsShuttingDown()) {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
mailbox.Send(Direction::Host, Message::RenderResponse);
continue;
}
std::array<bool, MaxRendererSessions> buffers_reset{};
std::array<u64, MaxRendererSessions> render_times_taken{};
const auto start_time{system.CoreTiming().GetGlobalTimeUs().count()};
for (u32 index = 0; index < MaxRendererSessions; index++) {
auto& command_buffer{command_buffers[index]};
auto& command_list_processor{command_list_processors[index]};
// Check this buffer is valid, as it may not be used.
if (command_buffer.buffer != 0) {
// If there are no remaining commands (from the previous list),
// this is a new command list, initialize it.
if (command_buffer.remaining_command_count == 0) {
command_list_processor.Initialize(system, *command_buffer.process,
command_buffer.buffer,
command_buffer.size, streams[index]);
}
if (command_buffer.reset_buffer && !buffers_reset[index]) {
streams[index]->ClearQueue();
buffers_reset[index] = true;
}
u64 max_time{max_process_time};
if (index == 1 && command_buffer.applet_resource_user_id ==
command_buffers[0].applet_resource_user_id) {
max_time = max_process_time - render_times_taken[0];
if (render_times_taken[0] > max_process_time) {
max_time = 0;
}
}
max_time = (std::min)(command_buffer.time_limit, max_time);
command_list_processor.SetProcessTimeMax(max_time);
if (index == 0) {
streams[index]->WaitFreeSpace(stop_token);
}
// Process the command list
{
render_times_taken[index] =
command_list_processor.Process(index) - start_time;
}
const auto end_time{system.CoreTiming().GetGlobalTimeUs().count()};
command_buffer.remaining_command_count =
command_list_processor.GetRemainingCommandCount();
command_buffer.render_time_taken_us = end_time - start_time;
}
}
mailbox.Send(Direction::Host, Message::RenderResponse);
} break;
default:
LOG_WARNING(Service_Audio, "ADSP AudioRenderer received an invalid message, msg={:02X}!", msg);
break;
}
}
}
} // namespace AudioCore::ADSP::AudioRenderer
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -82,6 +82,11 @@ public:
u64 GetRenderingStartTick(s32 session_id) const noexcept;
private:
/**
* Main AudioRenderer thread, responsible for processing the command lists.
*/
void Main(std::stop_token stop_token);
/**
* Creates the streams which will receive the processed samples.
*/
+195 -194
View File
@@ -37,9 +37,7 @@ bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 stereo_stream_co
} // namespace
OpusDecoder::OpusDecoder(Core::System& system_) : system{system_} {
init_thread = std::jthread([this](std::stop_token stop_token) {
Init(stop_token);
});
init_thread = std::jthread([this](std::stop_token stop_token) { Init(stop_token); });
}
OpusDecoder::~OpusDecoder() {
@@ -66,203 +64,206 @@ u32 OpusDecoder::Receive(Direction dir, std::stop_token stop_token) {
return mailbox.Receive(dir, stop_token);
}
void OpusDecoder::Init(std::stop_token rc_stop_token) {
void OpusDecoder::Init(std::stop_token stop_token) {
Common::SetCurrentThreadName("DSP_OpusDecoder_Init");
if (Receive(Direction::DSP, rc_stop_token) != Message::Start) {
LOG_ERROR(Service_Audio, "DSP OpusDecoder failed to receive Start message. Opus initialization failed.");
if (Receive(Direction::DSP, stop_token) != Message::Start) {
LOG_ERROR(Service_Audio,
"DSP OpusDecoder failed to receive Start message. Opus initialization failed.");
return;
}
// Main OpusDecoder thread, responsible for processing the incoming Opus packets.
main_thread = std::jthread([this](std::stop_token stop_token) {
Common::SetCurrentThreadName("DSP_OpusDecoder_Main");
while (!stop_token.stop_requested()) {
auto msg = Receive(Direction::DSP, stop_token);
switch (msg) {
case Shutdown:
Send(Direction::Host, Message::ShutdownOK);
return;
case GetWorkBufferSize: {
auto channel_count = static_cast<s32>(shared_memory->host_send_data[0]);
ASSERT(IsValidChannelCount(channel_count));
shared_memory->dsp_return_data[0] = OpusDecodeObject::GetWorkBufferSize(channel_count);
Send(Direction::Host, Message::GetWorkBufferSizeOK);
} break;
case InitializeDecodeObject: {
auto buffer = shared_memory->host_send_data[0];
auto buffer_size = shared_memory->host_send_data[1];
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
auto channel_count = static_cast<s32>(shared_memory->host_send_data[3]);
ASSERT(sample_rate >= 0);
ASSERT(IsValidChannelCount(channel_count));
ASSERT(buffer_size >= OpusDecodeObject::GetWorkBufferSize(channel_count));
auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
shared_memory->dsp_return_data[0] =
decoder_object.InitializeDecoder(sample_rate, channel_count);
Send(Direction::Host, Message::InitializeDecodeObjectOK);
} break;
case ShutdownDecodeObject: {
auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
shared_memory->dsp_return_data[0] = decoder_object.Shutdown();
Send(Direction::Host, Message::ShutdownDecodeObjectOK);
} break;
case DecodeInterleaved: {
auto start_time = system.CoreTiming().GetGlobalTimeUs();
auto buffer = shared_memory->host_send_data[0];
auto input_data = shared_memory->host_send_data[1];
auto input_data_size = shared_memory->host_send_data[2];
auto output_data = shared_memory->host_send_data[3];
auto output_data_size = shared_memory->host_send_data[4];
auto final_range = static_cast<u32>(shared_memory->host_send_data[5]);
auto reset_requested = shared_memory->host_send_data[6];
u32 decoded_samples{0};
auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
s32 error_code{OPUS_OK};
if (reset_requested) {
error_code = decoder_object.ResetDecoder();
}
if (error_code == OPUS_OK) {
error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size,
input_data, input_data_size);
}
if (error_code == OPUS_OK) {
if (final_range && decoder_object.GetFinalRange() != final_range) {
error_code = OPUS_INVALID_PACKET;
}
}
auto end_time = system.CoreTiming().GetGlobalTimeUs();
shared_memory->dsp_return_data[0] = error_code;
shared_memory->dsp_return_data[1] = decoded_samples;
shared_memory->dsp_return_data[2] = (end_time - start_time).count();
Send(Direction::Host, Message::DecodeInterleavedOK);
} break;
case MapMemory: {
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
Send(Direction::Host, Message::MapMemoryOK);
} break;
case UnmapMemory: {
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
Send(Direction::Host, Message::UnmapMemoryOK);
} break;
case GetWorkBufferSizeForMultiStream: {
auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[0]);
auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[1]);
ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count));
shared_memory->dsp_return_data[0] = OpusMultiStreamDecodeObject::GetWorkBufferSize(
total_stream_count, stereo_stream_count);
Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK);
} break;
case InitializeMultiStreamDecodeObject: {
auto buffer = shared_memory->host_send_data[0];
auto buffer_size = shared_memory->host_send_data[1];
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
auto channel_count = static_cast<s32>(shared_memory->host_send_data[3]);
auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[4]);
auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[5]);
// Nintendo seem to have a bug here, they try to use &host_send_data[6] for the channel
// mappings, but [6] is never set, and there is not enough room in the argument data for
// more than 40 channels, when 255 are possible.
// It also means the mapping values are undefined, though likely always 0,
// and the mappings given by the game are ignored. The mappings are copied to this
// dedicated buffer host side, so let's do as intended.
auto mappings = shared_memory->channel_mapping.data();
ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count));
ASSERT(sample_rate >= 0);
ASSERT(buffer_size >= OpusMultiStreamDecodeObject::GetWorkBufferSize(
total_stream_count, stereo_stream_count));
auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
shared_memory->dsp_return_data[0] = decoder_object.InitializeDecoder(
sample_rate, total_stream_count, channel_count, stereo_stream_count, mappings);
Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK);
} break;
case ShutdownMultiStreamDecodeObject: {
auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
shared_memory->dsp_return_data[0] = decoder_object.Shutdown();
Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK);
} break;
case DecodeInterleavedForMultiStream: {
auto start_time = system.CoreTiming().GetGlobalTimeUs();
auto buffer = shared_memory->host_send_data[0];
auto input_data = shared_memory->host_send_data[1];
auto input_data_size = shared_memory->host_send_data[2];
auto output_data = shared_memory->host_send_data[3];
auto output_data_size = shared_memory->host_send_data[4];
auto final_range = static_cast<u32>(shared_memory->host_send_data[5]);
auto reset_requested = shared_memory->host_send_data[6];
u32 decoded_samples{0};
auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
s32 error_code{OPUS_OK};
if (reset_requested) {
error_code = decoder_object.ResetDecoder();
}
if (error_code == OPUS_OK) {
error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size,
input_data, input_data_size);
}
if (error_code == OPUS_OK) {
if (final_range && decoder_object.GetFinalRange() != final_range) {
error_code = OPUS_INVALID_PACKET;
}
}
auto end_time = system.CoreTiming().GetGlobalTimeUs();
shared_memory->dsp_return_data[0] = error_code;
shared_memory->dsp_return_data[1] = decoded_samples;
shared_memory->dsp_return_data[2] = (end_time - start_time).count();
Send(Direction::Host, Message::DecodeInterleavedForMultiStreamOK);
} break;
default:
LOG_ERROR(Service_Audio, "Invalid OpusDecoder command {}", msg);
continue;
}
}
});
main_thread = std::jthread([this](std::stop_token st) { Main(st); });
running = true;
Send(Direction::Host, Message::StartOK);
}
void OpusDecoder::Main(std::stop_token stop_token) {
Common::SetCurrentThreadName("DSP_OpusDecoder_Main");
while (!stop_token.stop_requested()) {
auto msg = Receive(Direction::DSP, stop_token);
switch (msg) {
case Shutdown:
Send(Direction::Host, Message::ShutdownOK);
return;
case GetWorkBufferSize: {
auto channel_count = static_cast<s32>(shared_memory->host_send_data[0]);
ASSERT(IsValidChannelCount(channel_count));
shared_memory->dsp_return_data[0] = OpusDecodeObject::GetWorkBufferSize(channel_count);
Send(Direction::Host, Message::GetWorkBufferSizeOK);
} break;
case InitializeDecodeObject: {
auto buffer = shared_memory->host_send_data[0];
auto buffer_size = shared_memory->host_send_data[1];
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
auto channel_count = static_cast<s32>(shared_memory->host_send_data[3]);
ASSERT(sample_rate >= 0);
ASSERT(IsValidChannelCount(channel_count));
ASSERT(buffer_size >= OpusDecodeObject::GetWorkBufferSize(channel_count));
auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
shared_memory->dsp_return_data[0] =
decoder_object.InitializeDecoder(sample_rate, channel_count);
Send(Direction::Host, Message::InitializeDecodeObjectOK);
} break;
case ShutdownDecodeObject: {
auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
shared_memory->dsp_return_data[0] = decoder_object.Shutdown();
Send(Direction::Host, Message::ShutdownDecodeObjectOK);
} break;
case DecodeInterleaved: {
auto start_time = system.CoreTiming().GetGlobalTimeUs();
auto buffer = shared_memory->host_send_data[0];
auto input_data = shared_memory->host_send_data[1];
auto input_data_size = shared_memory->host_send_data[2];
auto output_data = shared_memory->host_send_data[3];
auto output_data_size = shared_memory->host_send_data[4];
auto final_range = static_cast<u32>(shared_memory->host_send_data[5]);
auto reset_requested = shared_memory->host_send_data[6];
u32 decoded_samples{0};
auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
s32 error_code{OPUS_OK};
if (reset_requested) {
error_code = decoder_object.ResetDecoder();
}
if (error_code == OPUS_OK) {
error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size,
input_data, input_data_size);
}
if (error_code == OPUS_OK) {
if (final_range && decoder_object.GetFinalRange() != final_range) {
error_code = OPUS_INVALID_PACKET;
}
}
auto end_time = system.CoreTiming().GetGlobalTimeUs();
shared_memory->dsp_return_data[0] = error_code;
shared_memory->dsp_return_data[1] = decoded_samples;
shared_memory->dsp_return_data[2] = (end_time - start_time).count();
Send(Direction::Host, Message::DecodeInterleavedOK);
} break;
case MapMemory: {
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
Send(Direction::Host, Message::MapMemoryOK);
} break;
case UnmapMemory: {
[[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
Send(Direction::Host, Message::UnmapMemoryOK);
} break;
case GetWorkBufferSizeForMultiStream: {
auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[0]);
auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[1]);
ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count));
shared_memory->dsp_return_data[0] = OpusMultiStreamDecodeObject::GetWorkBufferSize(
total_stream_count, stereo_stream_count);
Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK);
} break;
case InitializeMultiStreamDecodeObject: {
auto buffer = shared_memory->host_send_data[0];
auto buffer_size = shared_memory->host_send_data[1];
auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
auto channel_count = static_cast<s32>(shared_memory->host_send_data[3]);
auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[4]);
auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[5]);
// Nintendo seem to have a bug here, they try to use &host_send_data[6] for the channel
// mappings, but [6] is never set, and there is not enough room in the argument data for
// more than 40 channels, when 255 are possible.
// It also means the mapping values are undefined, though likely always 0,
// and the mappings given by the game are ignored. The mappings are copied to this
// dedicated buffer host side, so let's do as intended.
auto mappings = shared_memory->channel_mapping.data();
ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count));
ASSERT(sample_rate >= 0);
ASSERT(buffer_size >= OpusMultiStreamDecodeObject::GetWorkBufferSize(
total_stream_count, stereo_stream_count));
auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
shared_memory->dsp_return_data[0] = decoder_object.InitializeDecoder(
sample_rate, total_stream_count, channel_count, stereo_stream_count, mappings);
Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK);
} break;
case ShutdownMultiStreamDecodeObject: {
auto buffer = shared_memory->host_send_data[0];
[[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
shared_memory->dsp_return_data[0] = decoder_object.Shutdown();
Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK);
} break;
case DecodeInterleavedForMultiStream: {
auto start_time = system.CoreTiming().GetGlobalTimeUs();
auto buffer = shared_memory->host_send_data[0];
auto input_data = shared_memory->host_send_data[1];
auto input_data_size = shared_memory->host_send_data[2];
auto output_data = shared_memory->host_send_data[3];
auto output_data_size = shared_memory->host_send_data[4];
auto final_range = static_cast<u32>(shared_memory->host_send_data[5]);
auto reset_requested = shared_memory->host_send_data[6];
u32 decoded_samples{0};
auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
s32 error_code{OPUS_OK};
if (reset_requested) {
error_code = decoder_object.ResetDecoder();
}
if (error_code == OPUS_OK) {
error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size,
input_data, input_data_size);
}
if (error_code == OPUS_OK) {
if (final_range && decoder_object.GetFinalRange() != final_range) {
error_code = OPUS_INVALID_PACKET;
}
}
auto end_time = system.CoreTiming().GetGlobalTimeUs();
shared_memory->dsp_return_data[0] = error_code;
shared_memory->dsp_return_data[1] = decoded_samples;
shared_memory->dsp_return_data[2] = (end_time - start_time).count();
Send(Direction::Host, Message::DecodeInterleavedForMultiStreamOK);
} break;
default:
LOG_ERROR(Service_Audio, "Invalid OpusDecoder command {}", msg);
continue;
}
}
}
} // namespace AudioCore::ADSP::OpusDecoder
+4 -3
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -72,6 +69,10 @@ private:
* Initializing thread, launched at audio_core boot to avoid blocking the main emu boot thread.
*/
void Init(std::stop_token stop_token);
/**
* Main OpusDecoder thread, responsible for processing the incoming Opus packets.
*/
void Main(std::stop_token stop_token);
/// Core system
Core::System& system;
@@ -7,40 +7,16 @@
#include <span>
#include <vector>
#include <SDL3/SDL.h>
#include <SDL.h>
#include "audio_core/common/common.h"
#include "audio_core/sink/sdl3_sink.h"
#include "audio_core/sink/sdl2_sink.h"
#include "audio_core/sink/sink_stream.h"
#include "common/logging.h"
#include "common/scope_exit.h"
#include "core/core.h"
namespace AudioCore::Sink {
namespace {
SDL_AudioDeviceID FindAudioDeviceByName(const std::string& device_name, bool capture) {
int device_count = 0;
SDL_AudioDeviceID* devices = capture ? SDL_GetAudioRecordingDevices(&device_count)
: SDL_GetAudioPlaybackDevices(&device_count);
if (devices == nullptr) {
return capture ? SDL_AUDIO_DEVICE_DEFAULT_RECORDING : SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
}
SDL_AudioDeviceID selected = capture ? SDL_AUDIO_DEVICE_DEFAULT_RECORDING
: SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
for (int i = 0; i < device_count; ++i) {
const char* current_name = SDL_GetAudioDeviceName(devices[i]);
if (current_name != nullptr && device_name == current_name) {
selected = devices[i];
break;
}
}
SDL_free(devices);
return selected;
}
} // Anonymous namespace
/**
* SDL sink stream, responsible for sinking samples to hardware.
*/
@@ -63,10 +39,13 @@ public:
system_channels = system_channels_;
device_channels = device_channels_;
SDL_AudioSpec spec{};
SDL_AudioSpec spec;
spec.freq = TargetSampleRate;
spec.channels = static_cast<u8>(device_channels);
spec.format = SDL_AUDIO_S16;
spec.format = AUDIO_S16SYS;
spec.samples = TargetSampleCount * 2;
spec.callback = &SDLSinkStream::DataCallback;
spec.userdata = this;
std::string device_name{output_device};
bool capture{false};
@@ -75,28 +54,22 @@ public:
capture = true;
}
const SDL_AudioDeviceID audio_device =
device_name.empty() ? (capture ? SDL_AUDIO_DEVICE_DEFAULT_RECORDING
: SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK)
: FindAudioDeviceByName(device_name, capture);
SDL_AudioSpec obtained;
if (device_name.empty()) {
device = SDL_OpenAudioDevice(nullptr, capture, &spec, &obtained, false);
} else {
device = SDL_OpenAudioDevice(device_name.c_str(), capture, &spec, &obtained, false);
}
stream = SDL_OpenAudioDeviceStream(audio_device, &spec, &SDLSinkStream::DataCallback,
this);
if (stream == nullptr) {
if (device == 0) {
LOG_CRITICAL(Audio_Sink, "Error opening SDL audio device: {}", SDL_GetError());
return;
}
SDL_AudioSpec stream_in{};
SDL_AudioSpec stream_out{};
static_cast<void>(SDL_GetAudioStreamFormat(stream, &stream_in, &stream_out));
LOG_INFO(Service_Audio,
"Opening SDL stream {} with: rate {} channels {} (system channels {}) "
" format {}",
static_cast<const void*>(stream), stream_out.freq, stream_out.channels,
system_channels, static_cast<int>(stream_out.format));
" samples {}",
device, obtained.freq, obtained.channels, system_channels, obtained.samples);
}
/**
@@ -111,14 +84,13 @@ public:
* Finalize the sink stream.
*/
void Finalize() override {
if (stream == nullptr) {
if (device == 0) {
return;
}
Stop();
SDL_ClearAudioStream(stream);
SDL_DestroyAudioStream(stream);
stream = nullptr;
SDL_ClearQueuedAudio(device);
SDL_CloseAudioDevice(device);
}
/**
@@ -128,23 +100,23 @@ public:
* Default false.
*/
void Start(bool resume = false) override {
if (stream == nullptr || !paused) {
if (device == 0 || !paused) {
return;
}
paused = false;
static_cast<void>(SDL_ResumeAudioStreamDevice(stream));
SDL_PauseAudioDevice(device, 0);
}
/**
* Stop the sink stream.
*/
void Stop() override {
if (stream == nullptr || paused) {
if (device == 0 || paused) {
return;
}
SignalPause();
static_cast<void>(SDL_PauseAudioStreamDevice(stream));
SDL_PauseAudioDevice(device, 1);
}
private:
@@ -156,8 +128,7 @@ private:
* @param stream - Buffer of samples to be filled or read.
* @param len - Length of the stream in bytes.
*/
static void DataCallback(void* userdata, SDL_AudioStream* stream, int additional_amount,
int total_amount) {
static void DataCallback(void* userdata, Uint8* stream, int len) {
auto* impl = static_cast<SDLSinkStream*>(userdata);
if (!impl) {
@@ -166,46 +137,25 @@ private:
const std::size_t num_channels = impl->GetDeviceChannels();
const std::size_t frame_size = num_channels;
const std::size_t num_frames{len / num_channels / sizeof(s16)};
if (impl->type == StreamType::In) {
const int bytes_available = SDL_GetAudioStreamAvailable(stream);
if (bytes_available <= 0) {
return;
}
std::vector<s16> input(bytes_available / static_cast<int>(sizeof(s16)));
const int bytes_read = SDL_GetAudioStreamData(stream, input.data(), bytes_available);
if (bytes_read <= 0) {
return;
}
const std::size_t num_frames =
static_cast<std::size_t>(bytes_read) / sizeof(s16) / frame_size;
std::span<const s16> input_buffer{input.data(),
static_cast<std::size_t>(bytes_read) / sizeof(s16)};
std::span<const s16> input_buffer{reinterpret_cast<const s16*>(stream),
num_frames * frame_size};
impl->ProcessAudioIn(input_buffer, num_frames);
} else {
if (additional_amount <= 0 && total_amount <= 0) {
return;
}
const int bytes_requested = additional_amount > 0 ? additional_amount : total_amount;
std::vector<s16> output(bytes_requested / static_cast<int>(sizeof(s16)));
const std::size_t num_frames =
static_cast<std::size_t>(bytes_requested) / sizeof(s16) / frame_size;
std::span<s16> output_buffer{output.data(), output.size()};
std::span<s16> output_buffer{reinterpret_cast<s16*>(stream), num_frames * frame_size};
impl->ProcessAudioOutAndRender(output_buffer, num_frames);
static_cast<void>(SDL_PutAudioStreamData(stream, output.data(), bytes_requested));
}
}
/// SDL stream attached to an opened input/output device
SDL_AudioStream* stream{};
/// SDL device id of the opened input/output device
SDL_AudioDeviceID device{};
};
SDLSink::SDLSink(std::string_view target_device_name) {
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
return;
}
@@ -268,26 +218,18 @@ std::vector<std::string> ListSDLSinkDevices(bool capture) {
std::vector<std::string> device_list;
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
return {};
}
}
int device_count = 0;
SDL_AudioDeviceID* devices =
capture ? SDL_GetAudioRecordingDevices(&device_count)
: SDL_GetAudioPlaybackDevices(&device_count);
if (devices == nullptr) {
return device_list;
}
const int device_count = SDL_GetNumAudioDevices(capture);
for (int i = 0; i < device_count; ++i) {
if (const char* name = SDL_GetAudioDeviceName(devices[i])) {
if (const char* name = SDL_GetAudioDeviceName(i, capture)) {
device_list.emplace_back(name);
}
}
SDL_free(devices);
return device_list;
}
@@ -300,7 +242,7 @@ u32 GetSDLLatency() {
// REVERTED back to 3833 - Below function IsSDLSuitable() removed, reverting to GetSDLLatency() above. - DIABLO 3 FIX
/*
bool IsSDLSuitable() {
#if !defined(HAVE_SDL3)
#if !defined(HAVE_SDL2)
return false;
#else
// Check SDL can init
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+6 -6
View File
@@ -16,8 +16,8 @@
#ifdef HAVE_CUBEB
#include "audio_core/sink/cubeb_sink.h"
#endif
#ifdef HAVE_SDL3
#include "audio_core/sink/sdl3_sink.h"
#ifdef HAVE_SDL2
#include "audio_core/sink/sdl2_sink.h"
#endif
#include "audio_core/sink/null_sink.h"
#include "common/logging.h"
@@ -71,9 +71,9 @@ constexpr SinkDetails sink_details[] = {
&GetCubebLatency,
},
#endif
#ifdef HAVE_SDL3
#ifdef HAVE_SDL2
SinkDetails{
Settings::AudioEngine::Sdl3,
Settings::AudioEngine::Sdl2,
[](std::string_view device_id) -> std::unique_ptr<Sink> {
return std::make_unique<SDLSink>(device_id);
},
@@ -115,10 +115,10 @@ const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) {
// BEGIN REINTRODUCED FROM 3833 - REPLACED CODE BLOCK ABOVE - DIABLO 3 FIX
// Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which
// causes audio issues, in that case go with SDL.
#if defined(HAVE_CUBEB) && defined(HAVE_SDL3)
#if defined(HAVE_CUBEB) && defined(HAVE_SDL2)
iter = find_backend(Settings::AudioEngine::Cubeb);
if (iter->latency() > TargetSampleCount * 3) {
iter = find_backend(Settings::AudioEngine::Sdl3);
iter = find_backend(Settings::AudioEngine::Sdl2);
}
#else
iter = std::begin(sink_details);
+15 -5
View File
@@ -141,12 +141,12 @@ add_library(
vector_math.h
virtual_buffer.cpp
virtual_buffer.h
wall_clock.cpp
wall_clock.h
zstd_compression.cpp
zstd_compression.h
fs/ryujinx_compat.h fs/ryujinx_compat.cpp
fs/symlink.h fs/symlink.cpp
cpu_features.cpp
cpu_features.h
httplib.h
net/net.h net/net.cpp)
@@ -180,13 +180,23 @@ endif()
if(ARCHITECTURE_x86_64)
target_sources(
common
PRIVATE
PRIVATE x64/cpu_detect.cpp
x64/cpu_detect.h
x64/cpu_wait.cpp
x64/cpu_wait.h
x64/native_clock.cpp
x64/native_clock.h
x64/rdtsc.cpp
x64/rdtsc.h
x64/xbyak.h)
x64/xbyak_abi.h
x64/xbyak_util.h)
target_link_libraries(common PRIVATE xbyak::xbyak)
endif()
if(HAS_NCE)
target_sources(common PRIVATE arm64/native_clock.cpp arm64/native_clock.h)
endif()
if(MSVC)
target_compile_definitions(
common
@@ -231,7 +241,7 @@ if(CXX_CLANG)
endif()
if (BOOST_NO_HEADERS)
target_link_libraries(common PUBLIC Boost::algorithm Boost::heap Boost::icl Boost::pool)
target_link_libraries(common PUBLIC Boost::algorithm Boost::icl Boost::pool)
else()
target_link_libraries(common PUBLIC Boost::headers)
endif()
+87
View File
@@ -0,0 +1,87 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#ifdef ANDROID
#include <sys/system_properties.h>
#endif
#include "common/arm64/native_clock.h"
namespace Common::Arm64 {
namespace {
NativeClock::FactorType GetFixedPointFactor(u64 num, u64 den) {
return (static_cast<NativeClock::FactorType>(num) << 64) / den;
}
u64 MultiplyHigh(u64 m, NativeClock::FactorType factor) {
return static_cast<u64>((m * factor) >> 64);
}
} // namespace
NativeClock::NativeClock() {
const u64 host_cntfrq = GetHostCNTFRQ();
ns_cntfrq_factor = GetFixedPointFactor(NsRatio::den, host_cntfrq);
us_cntfrq_factor = GetFixedPointFactor(UsRatio::den, host_cntfrq);
ms_cntfrq_factor = GetFixedPointFactor(MsRatio::den, host_cntfrq);
guest_cntfrq_factor = GetFixedPointFactor(CNTFRQ, host_cntfrq);
gputick_cntfrq_factor = GetFixedPointFactor(GPUTickFreq, host_cntfrq);
}
std::chrono::nanoseconds NativeClock::GetTimeNS() const {
return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_cntfrq_factor)};
}
std::chrono::microseconds NativeClock::GetTimeUS() const {
return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_cntfrq_factor)};
}
std::chrono::milliseconds NativeClock::GetTimeMS() const {
return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_cntfrq_factor)};
}
s64 NativeClock::GetCNTPCT() const {
return MultiplyHigh(GetUptime(), guest_cntfrq_factor);
}
s64 NativeClock::GetGPUTick() const {
return MultiplyHigh(GetUptime(), gputick_cntfrq_factor);
}
s64 NativeClock::GetUptime() const {
s64 cntvct_el0 = 0;
asm volatile("dsb ish\n\t"
"mrs %[cntvct_el0], cntvct_el0\n\t"
"dsb ish\n\t"
: [cntvct_el0] "=r"(cntvct_el0));
return cntvct_el0;
}
bool NativeClock::IsNative() const {
return true;
}
s64 NativeClock::GetHostCNTFRQ() {
u64 cntfrq_el0 = 0;
std::string_view board{""};
#ifdef ANDROID
char buffer[PROP_VALUE_MAX];
int len{__system_property_get("ro.product.board", buffer)};
board = std::string_view(buffer, static_cast<size_t>(len));
#endif
if (board == "s5e9925") { // Exynos 2200
cntfrq_el0 = 25600000;
} else if (board == "exynos2100") { // Exynos 2100
cntfrq_el0 = 26000000;
} else if (board == "exynos9810") { // Exynos 9810
cntfrq_el0 = 26000000;
} else if (board == "s5e8825") { // Exynos 1280
cntfrq_el0 = 26000000;
} else {
asm("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0));
}
return cntfrq_el0;
}
} // namespace Common::Arm64
+45
View File
@@ -0,0 +1,45 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/wall_clock.h"
namespace Common::Arm64 {
class NativeClock final : public WallClock {
public:
explicit NativeClock();
std::chrono::nanoseconds GetTimeNS() const override;
std::chrono::microseconds GetTimeUS() const override;
std::chrono::milliseconds GetTimeMS() const override;
s64 GetCNTPCT() const override;
s64 GetGPUTick() const override;
s64 GetUptime() const override;
bool IsNative() const override;
static s64 GetHostCNTFRQ();
public:
using FactorType = unsigned __int128;
FactorType GetGuestCNTFRQFactor() const {
return guest_cntfrq_factor;
}
private:
FactorType ns_cntfrq_factor;
FactorType us_cntfrq_factor;
FactorType ms_cntfrq_factor;
FactorType guest_cntfrq_factor;
FactorType gputick_cntfrq_factor;
};
} // namespace Common::Arm64
-187
View File
@@ -1,187 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <optional>
#include <string_view>
#include <chrono>
#include <memory>
#include <ratio>
#include "common/common_types.h"
namespace Common {
class WallClock {
public:
static constexpr u64 CNTFRQ = 19'200'000; // CNTPCT_EL0 Frequency = 19.2 MHz
static constexpr u64 GPUTickFreq = 614'400'000; // GM20B GPU Tick Frequency = 614.4 MHz
static constexpr u64 CPUTickFreq = 1'020'000'000; // T210/4 A57 CPU Tick Frequency = 1020.0 MHz
explicit WallClock(bool invariant, u64 rdtsc_frequency_) noexcept;
/// @returns The time in nanoseconds since the construction of this clock.
std::chrono::nanoseconds GetTimeNS() const;
/// @returns The time in microseconds since the construction of this clock.
std::chrono::microseconds GetTimeUS() const;
/// @returns The time in milliseconds since the construction of this clock.
std::chrono::milliseconds GetTimeMS() const;
/// @returns The guest CNTPCT ticks since the construction of this clock.
s64 GetCNTPCT() const;
/// @returns The guest GPU ticks since the construction of this clock.
s64 GetGPUTick() const;
/// @returns The raw host timer ticks since an indeterminate epoch.
s64 GetUptime() const;
/// @returns Whether the clock directly uses the host's hardware clock.
bool IsNative() const;
// @returns Nanoseconds to native ticks
u64 NsToTicks(std::chrono::nanoseconds ns) const;
static inline u64 NSToCNTPCT(u64 ns) {
return ns * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
}
static inline u64 NSToGPUTick(u64 ns) {
return ns * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
}
// Cycle Timing
static inline u64 CPUTickToNS(u64 cpu_tick) {
return cpu_tick * CPUTickToNsRatio::num / CPUTickToNsRatio::den;
}
static inline u64 CPUTickToUS(u64 cpu_tick) {
return cpu_tick * CPUTickToUsRatio::num / CPUTickToUsRatio::den;
}
static inline u64 CPUTickToCNTPCT(u64 cpu_tick) {
return cpu_tick * CPUTickToCNTPCTRatio::num / CPUTickToCNTPCTRatio::den;
}
static inline u64 CPUTickToGPUTick(u64 cpu_tick) {
return cpu_tick * CPUTickToGPUTickRatio::num / CPUTickToGPUTickRatio::den;
}
using NsRatio = std::nano;
using UsRatio = std::micro;
using MsRatio = std::milli;
using NsToUsRatio = std::ratio_divide<std::nano, std::micro>;
using NsToMsRatio = std::ratio_divide<std::nano, std::milli>;
using NsToCNTPCTRatio = std::ratio<CNTFRQ, std::nano::den>;
using NsToGPUTickRatio = std::ratio<GPUTickFreq, std::nano::den>;
// Cycle Timing
using CPUTickToNsRatio = std::ratio<std::nano::den, CPUTickFreq>;
using CPUTickToUsRatio = std::ratio<std::micro::den, CPUTickFreq>;
using CPUTickToCNTPCTRatio = std::ratio<CNTFRQ, CPUTickFreq>;
using CPUTickToGPUTickRatio = std::ratio<GPUTickFreq, CPUTickFreq>;
#if defined(ARCHITECTURE_x86_64)
u64 rdtsc_frequency;
u64 ns_rdtsc_factor;
u64 us_rdtsc_factor;
u64 ms_rdtsc_factor;
u64 rdtsc_ns_factor;
u64 cntpct_rdtsc_factor;
u64 gputick_rdtsc_factor;
bool invariant;
#elif defined(HAS_NCE)
using FactorType = unsigned __int128;
[[nodiscard]] inline FactorType GetGuestCNTFRQFactor() const {
return guest_cntfrq_factor;
}
FactorType ns_cntfrq_factor;
FactorType us_cntfrq_factor;
FactorType ms_cntfrq_factor;
FactorType cntfrq_ns_factor;
FactorType guest_cntfrq_factor;
FactorType gputick_cntfrq_factor;
#endif
};
#ifdef ARCHITECTURE_x86_64
/// x86/x64 CPU capabilities that may be detected by this module
struct CPUCaps {
enum class Manufacturer : u8 {
Unknown = 0,
Intel = 1,
AMD = 2,
Hygon = 3,
};
static Manufacturer ParseManufacturer(std::string_view brand_string);
Manufacturer manufacturer;
char brand_string[13];
char cpu_string[48];
u32 base_frequency;
u32 max_frequency;
u32 bus_frequency;
u32 tsc_crystal_ratio_denominator;
u32 tsc_crystal_ratio_numerator;
u32 crystal_frequency;
u64 tsc_frequency; // Derived from the above three values
bool sse3 : 1;
bool ssse3 : 1;
bool sse4_1 : 1;
bool sse4_2 : 1;
bool avx : 1;
bool avx2 : 1;
bool avx512f : 1;
bool avx512dq : 1;
bool avx512cd : 1;
bool avx512bw : 1;
bool avx512vl : 1;
bool avx512vbmi : 1;
bool avx512bitalg : 1;
bool aes : 1;
bool bmi1 : 1;
bool bmi2 : 1;
bool f16c : 1;
bool fma : 1;
bool gfni : 1;
bool invariant_tsc : 1;
bool lzcnt : 1;
bool monitorx : 1;
bool movbe : 1;
bool pclmulqdq : 1;
bool popcnt : 1;
bool sha : 1;
bool waitpkg : 1;
};
#else
struct CPUCaps {
bool padding;
};
#endif
/// Detects CPU core count
std::optional<int> GetProcessorCount();
/// @brief Global cpu caps
extern const CPUCaps g_cpu_caps;
/// @brief Global wall clock
extern const WallClock g_wall_clock;
} // namespace Common
+1 -4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -63,7 +60,7 @@ public:
template <typename Func>
void ForEachItemBelow(TickType tick, Func&& func) {
static constexpr bool RETURNS_BOOL =
std::is_same_v<std::invoke_result_t<Func, ObjectType>, bool>;
std::is_same_v<std::invoke_result<Func, ObjectType>, bool>;
Item* iterator = first_item;
while (iterator) {
if (static_cast<s64>(tick) - static_cast<s64>(iterator->tick) < 0) {
-12
View File
@@ -435,16 +435,4 @@ void ToggleSlowMode() {
SetSpeedMode(SpeedMode::Standard);
}
bool IsOpenGL() {
const auto backend = Settings::values.renderer_backend.GetValue();
switch (backend) {
case RendererBackend::OpenGL_GLSL:
case RendererBackend::OpenGL_GLASM:
case RendererBackend::OpenGL_SPIRV:
return true;
default:
return false;
}
}
} // namespace Settings
-9
View File
@@ -545,13 +545,6 @@ struct Values {
Specialization::Default,
true,
true};
SwitchableSetting<bool> antiflicker{linkage,
false,
"antiflicker",
Category::RendererHacks,
Specialization::Default,
true,
true};
SwitchableSetting<bool> async_presentation{linkage,
#ifdef ANDROID
false,
@@ -870,8 +863,6 @@ bool IsFastmemEnabled();
void SetNceEnabled(bool is_64bit);
bool IsNceEnabled();
bool IsOpenGL();
bool IsDockedMode();
float Volume();
+3 -5
View File
@@ -92,13 +92,11 @@ struct EnumMetadata {
// AudioEngine must be specified discretely due to having existing but slightly different
// canonicalizations
// TODO (lat9nq): Remove explicit definition of AudioEngine/sink_id
enum class AudioEngine : u32 { Auto, Cubeb, Sdl3, Null, Oboe, };
enum class AudioEngine : u32 { Auto, Cubeb, Sdl2, Null, Oboe, };
template<>
inline std::vector<std::pair<std::string_view, AudioEngine>> EnumMetadata<AudioEngine>::Canonicalizations() {
return {
{"auto", AudioEngine::Auto},
{"cubeb", AudioEngine::Cubeb},
{"sdl3", AudioEngine::Sdl3},
{"auto", AudioEngine::Auto}, {"cubeb", AudioEngine::Cubeb}, {"sdl2", AudioEngine::Sdl2},
{"null", AudioEngine::Null}, {"oboe", AudioEngine::Oboe},
};
}
@@ -145,7 +143,7 @@ ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never);
ENUM(FullscreenMode, Borderless, Exclusive);
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(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, Lanczos, ScaleForce, Fsr, Area, ZeroTangent, BSpline, Mitchell, Spline1, Mmpx, Sgsr, SgsrEdge, MaxEnum);
ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, Lanczos, ScaleForce, Fsr, Area, ZeroTangent, BSpline, Mitchell, Spline1, Mmpx, MaxEnum);
ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum);
ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
ENUM(ConsoleMode, Handheld, Docked);
+18 -20
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -20,14 +20,10 @@
namespace Common {
struct SlotId {
static constexpr u32 TAGGED_MASK = 0x7fffffff;
static constexpr u32 TAGGED_VALUE = 0x80000000;
static constexpr u32 INVALID_INDEX = (std::numeric_limits<u32>::max)();
constexpr u32 Value() const noexcept {
return index & (~TAGGED_VALUE);
}
constexpr auto operator<=>(const SlotId&) const noexcept = default;
constexpr explicit operator bool() const noexcept {
return index != INVALID_INDEX;
}
@@ -51,12 +47,12 @@ public:
Iterator& operator++() noexcept {
const u64* const bitset = slot_vector->stored_bitset.data();
const u32 size = static_cast<u32>(slot_vector->stored_bitset.size()) * 64;
if (id.Value() < size) {
if (id.index < size) {
do {
++id.index;
} while (id.Value() < size && !IsValid(bitset));
if (id.Value() == size) {
id = SlotId{};
} while (id.index < size && !IsValid(bitset));
if (id.index == size) {
id.index = SlotId::INVALID_INDEX;
}
}
return *this;
@@ -89,7 +85,7 @@ public:
: slot_vector{slot_vector_}, id{id_} {}
bool IsValid(const u64* bitset) const noexcept {
return ((bitset[id.Value() / 64] >> (id.Value() % 64)) & 1) != 0;
return ((bitset[id.index / 64] >> (id.index % 64)) & 1) != 0;
}
SlotVector<T>* slot_vector;
@@ -111,12 +107,12 @@ public:
[[nodiscard]] T& operator[](SlotId id) noexcept {
ValidateIndex(id);
return values[id.Value()].object;
return values[id.index].object;
}
[[nodiscard]] const T& operator[](SlotId id) const noexcept {
ValidateIndex(id);
return values[id.Value()].object;
return values[id.index].object;
}
template <typename... Args>
@@ -129,9 +125,9 @@ public:
}
void erase(SlotId id) noexcept {
values[id.Value()].object.~T();
free_list.push_back(id.Value());
ResetStorageBit(id.Value());
values[id.index].object.~T();
free_list.push_back(id.index);
ResetStorageBit(id.index);
}
[[nodiscard]] Iterator begin() noexcept {
@@ -145,7 +141,7 @@ public:
}
[[nodiscard]] Iterator end() noexcept {
return Iterator(this, SlotId{});
return Iterator(this, SlotId{SlotId::INVALID_INDEX});
}
[[nodiscard]] size_t size() const noexcept {
@@ -179,8 +175,8 @@ private:
void ValidateIndex(SlotId id) const noexcept {
DEBUG_ASSERT(id);
DEBUG_ASSERT(id.Value() / 64 < stored_bitset.size());
DEBUG_ASSERT(((stored_bitset[id.Value() / 64] >> (id.Value() % 64)) & 1) != 0);
DEBUG_ASSERT(id.index / 64 < stored_bitset.size());
DEBUG_ASSERT(((stored_bitset[id.index / 64] >> (id.index % 64)) & 1) != 0);
}
[[nodiscard]] u32 FreeValueIndex() noexcept {
@@ -212,7 +208,9 @@ private:
const size_t old_free_size = free_list.size();
free_list.resize(old_free_size + (new_capacity - values_capacity));
std::iota(free_list.begin() + old_free_size, free_list.end(), u32(values_capacity));
std::iota(free_list.begin() + old_free_size, free_list.end(),
static_cast<u32>(values_capacity));
delete[] values;
values = new_values;
values_capacity = new_capacity;
+2 -113
View File
@@ -4,8 +4,6 @@
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <chrono>
#include <limits>
#include <string>
#include <thread>
@@ -20,35 +18,24 @@
#elif defined(_WIN32)
#include <windows.h>
#include "common/string_util.h"
#include "common/windows/timer_resolution.h"
#else
#if defined(__FreeBSD__)
#include <sys/cpuset.h>
#include <sys/_cpuset.h>
#include <pthread_np.h>
// Compatibility with CPUset
#define cpu_set_t cpuset_t
#elif defined(__DragonFly__) || defined(__OpenBSD__) || defined(__Bitrig__)
#include <pthread_np.h>
#endif
#include <pthread.h>
#include <sched.h>
#endif
#ifndef _WIN32
#include <unistd.h>
#endif
#include "common/cpu_features.h"
#ifdef ARCHITECTURE_x86_64
#ifdef _MSC_VER
#include <intrin.h>
#else
#include <x86intrin.h>
#ifdef __FreeBSD__
# define cpu_set_t cpuset_t
#endif
#include "common/x64/rdtsc.h"
#endif
#include "core/core_timing.h"
namespace Common {
@@ -157,102 +144,4 @@ void PinCurrentThreadToPerformanceCore(size_t core_id) {
}
}
#ifdef ARCHITECTURE_x86_64
// On Linux and UNIX systems, a futex would nominally be used to cover the costs
// the idea is that it's intuitivelly cheaper to use a direct instruction as opposed to a full futex call
// the underlying libc++ implementation uses pthread_cond_timedwait which MAY invoke a futex
// Let's pretend the OS is too expensive to jump into, and avoid ANY context switches
// this should *IN THEORY* lower CPU usage while just waiting for stuff effectively
// For windows the minimal quanta resolution is about 500us, and normal CRT cond var is 1.5ms(?)
// so may as well avoid that too
// Let's just give ALL platforms the same mechanisms (almost) for when they have umonitor OR waitpkg
#ifdef __clang__
__attribute__((target("waitpkg,mwaitx")))
#elif defined(__GNUC__)
#pragma GCC target("waitpkg")
#pragma GCC target("mwaitx")
#endif
bool Event::WaitFor(const std::chrono::nanoseconds time) {
#ifdef _WIN32
auto const start = Common::X64::FencedRDTSC();
auto const& caps = Common::g_cpu_caps;
[[maybe_unused]] auto const end = start + Common::g_wall_clock.NsToTicks(time);
if (caps.monitorx) {
while (true) {
// Armed monitor, as per manual, MWAITX must be conditional if the condition isn't satisfied
// to prevent a race condition.
_mm_monitorx(reinterpret_cast<u64*>(std::addressof(is_set)), 0, 0);
if (!is_set.load()) {
// RDTSC may be fenced here due to atomic load
#ifdef _MSC_VER
auto const now = __rdtsc();
#else
auto const now = _rdtsc();
#endif
if (end > now) {
u32 const cycles = std::min<u32>((std::numeric_limits<u32>::max)(), s64(end) - s64(now));
// See here: https://github.com/torvalds/linux/blob/948a64995aca6820abefd17f1a4258f5835c5ad9/arch/x86/lib/delay.c#L93
// MWAITX accepts a 32-bit input timer which determines the total number of cycles to wait for
// NOT THE TOTAL ABSOLUTE TSC VALUE, it's just a delta
// BIT[1] = use a timer
// Hint = 0: Use C1 state when sleepy (means slower wakeup but better savings)
_mm_mwaitx(1 << 1, 0u, cycles);
if (!is_set.load())
return false;
} else
return false; //timeout
}
bool expected = true;
if (is_set.compare_exchange_weak(expected, false, std::memory_order_release))
return true;
}
} else if (caps.waitpkg) {
// #UD If CPUID.7.0:ECX.WAITPKG[bit 5]=0.
while (true) {
_umonitor(std::addressof(is_set));
if (!is_set.load() && !_umwait(0, end)) //umwait is absolute time!!!
return false;
bool expected = true;
if (is_set.compare_exchange_weak(expected, false, std::memory_order_release))
return true;
}
} else {
#ifdef _MSC_VER
while (!is_set.load() && end > __rdtsc())
Common::Windows::SleepForOneTick();
#else
while (!is_set.load() && end > _rdtsc())
Common::Windows::SleepForOneTick();
#endif
if (is_set.load())
Reset();
return true;
}
#else
std::unique_lock lk{mutex};
if (!condvar.wait_for(lk, time, [this] { return is_set.load(); }))
return false;
is_set = false;
return true;
#endif
}
#else
bool Event::WaitFor(const std::chrono::nanoseconds time) {
#ifdef _WIN32
auto const end = Common::g_wall_clock.GetTimeNS() + time;
while (!is_set.load() && end > Common::g_wall_clock.GetTimeNS())
Common::Windows::SleepForOneTick();
if (is_set.load())
Reset();
return true;
#else
std::unique_lock lk{mutex};
if (!condvar.wait_for(lk, time, [this] { return is_set.load(); }))
return false;
is_set = false;
return true;
#endif
}
#endif
} // namespace Common
+10 -4
View File
@@ -34,10 +34,16 @@ public:
is_set = false;
}
bool WaitFor(const std::chrono::nanoseconds time);
bool WaitFor(const std::chrono::nanoseconds& time) {
std::unique_lock lk{mutex};
if (!condvar.wait_for(lk, time, [this] { return is_set.load(); }))
return false;
is_set = false;
return true;
}
template<class Clock, class Duration>
bool WaitUntil(const std::chrono::time_point<Clock, Duration> time) {
template <class Clock, class Duration>
bool WaitUntil(const std::chrono::time_point<Clock, Duration>& time) {
std::unique_lock lk{mutex};
if (!condvar.wait_until(lk, time, [this] { return is_set.load(); }))
return false;
@@ -57,9 +63,9 @@ public:
}
private:
alignas(64) std::atomic<bool> is_set{false};
std::condition_variable condvar;
std::mutex mutex;
std::atomic_bool is_set{false};
};
class Barrier {
+78
View File
@@ -0,0 +1,78 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/steady_clock.h"
#include "common/wall_clock.h"
#ifdef ARCHITECTURE_x86_64
#include "common/x64/cpu_detect.h"
#include "common/x64/native_clock.h"
#include "common/x64/rdtsc.h"
#endif
#ifdef HAS_NCE
#include "common/arm64/native_clock.h"
#endif
namespace Common {
class StandardWallClock final : public WallClock {
public:
explicit StandardWallClock() {}
std::chrono::nanoseconds GetTimeNS() const override {
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::system_clock::now().time_since_epoch());
}
std::chrono::microseconds GetTimeUS() const override {
return std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::system_clock::now().time_since_epoch());
}
std::chrono::milliseconds GetTimeMS() const override {
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch());
}
s64 GetCNTPCT() const override {
return GetUptime() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
}
s64 GetGPUTick() const override {
return GetUptime() * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
}
s64 GetUptime() const override {
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count();
}
bool IsNative() const override {
return false;
}
};
std::unique_ptr<WallClock> CreateOptimalClock() {
#if defined(ARCHITECTURE_x86_64)
const auto& caps = GetCPUCaps();
if (caps.invariant_tsc && caps.tsc_frequency >= std::nano::den) {
return std::make_unique<X64::NativeClock>(caps.tsc_frequency);
} else {
// Fallback to StandardWallClock if the hardware TSC
// - Is not invariant
// - Is not more precise than 1 GHz (1ns resolution)
return std::make_unique<StandardWallClock>();
}
#elif defined(HAS_NCE)
return std::make_unique<Arm64::NativeClock>();
#else
return std::make_unique<StandardWallClock>();
#endif
}
} // namespace Common
+92
View File
@@ -0,0 +1,92 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <chrono>
#include <memory>
#include <ratio>
#include "common/common_types.h"
namespace Common {
class WallClock {
public:
static constexpr u64 CNTFRQ = 19'200'000; // CNTPCT_EL0 Frequency = 19.2 MHz
static constexpr u64 GPUTickFreq = 614'400'000; // GM20B GPU Tick Frequency = 614.4 MHz
static constexpr u64 CPUTickFreq = 1'020'000'000; // T210/4 A57 CPU Tick Frequency = 1020.0 MHz
virtual ~WallClock() = default;
/// @returns The time in nanoseconds since the construction of this clock.
virtual std::chrono::nanoseconds GetTimeNS() const = 0;
/// @returns The time in microseconds since the construction of this clock.
virtual std::chrono::microseconds GetTimeUS() const = 0;
/// @returns The time in milliseconds since the construction of this clock.
virtual std::chrono::milliseconds GetTimeMS() const = 0;
/// @returns The guest CNTPCT ticks since the construction of this clock.
virtual s64 GetCNTPCT() const = 0;
/// @returns The guest GPU ticks since the construction of this clock.
virtual s64 GetGPUTick() const = 0;
/// @returns The raw host timer ticks since an indeterminate epoch.
virtual s64 GetUptime() const = 0;
/// @returns Whether the clock directly uses the host's hardware clock.
virtual bool IsNative() const = 0;
static inline u64 NSToCNTPCT(u64 ns) {
return ns * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
}
static inline u64 NSToGPUTick(u64 ns) {
return ns * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
}
// Cycle Timing
static inline u64 CPUTickToNS(u64 cpu_tick) {
return cpu_tick * CPUTickToNsRatio::num / CPUTickToNsRatio::den;
}
static inline u64 CPUTickToUS(u64 cpu_tick) {
return cpu_tick * CPUTickToUsRatio::num / CPUTickToUsRatio::den;
}
static inline u64 CPUTickToCNTPCT(u64 cpu_tick) {
return cpu_tick * CPUTickToCNTPCTRatio::num / CPUTickToCNTPCTRatio::den;
}
static inline u64 CPUTickToGPUTick(u64 cpu_tick) {
return cpu_tick * CPUTickToGPUTickRatio::num / CPUTickToGPUTickRatio::den;
}
protected:
using NsRatio = std::nano;
using UsRatio = std::micro;
using MsRatio = std::milli;
using NsToUsRatio = std::ratio_divide<std::nano, std::micro>;
using NsToMsRatio = std::ratio_divide<std::nano, std::milli>;
using NsToCNTPCTRatio = std::ratio<CNTFRQ, std::nano::den>;
using NsToGPUTickRatio = std::ratio<GPUTickFreq, std::nano::den>;
// Cycle Timing
using CPUTickToNsRatio = std::ratio<std::nano::den, CPUTickFreq>;
using CPUTickToUsRatio = std::ratio<std::micro::den, CPUTickFreq>;
using CPUTickToCNTPCTRatio = std::ratio<CNTFRQ, CPUTickFreq>;
using CPUTickToGPUTickRatio = std::ratio<GPUTickFreq, CPUTickFreq>;
};
[[nodiscard]] std::unique_ptr<WallClock> CreateOptimalClock();
} // namespace Common
@@ -10,32 +10,34 @@
#include <fstream>
#include <iterator>
#include <optional>
#include <algorithm>
#include <string_view>
#include <thread>
#include <vector>
#ifdef _WIN32
#include <windows.h>
#elif defined(__DragonFly__) || defined(__FreeBSD__)
#include <sys/types.h>
#include <machine/cpufunc.h>
#elif defined(__ANDROID__)
#include <sys/system_properties.h>
#endif
#include "common/steady_clock.h"
#include "common/uint128.h"
#include "common/bit_util.h"
#include "common/common_types.h"
#include "common/cpu_features.h"
#include "common/logging.h"
#ifdef ARCHITECTURE_x86_64
#include "common/x64/cpu_detect.h"
#include "common/x64/rdtsc.h"
#ifdef _WIN32
#include <windows.h>
#endif
#ifdef _MSC_VER
#include <intrin.h>
static inline u64 xgetbv(u32 index) { return _xgetbv(index); }
static inline u64 xgetbv(u32 index) {
return _xgetbv(index);
}
#else
#if defined(__DragonFly__) || defined(__FreeBSD__)
// clang-format off
#include <sys/types.h>
#include <machine/cpufunc.h>
// clang-format on
#endif
static inline void __cpuidex(int info[4], u32 function_id, u32 subfunction_id) {
#if defined(__DragonFly__) || defined(__FreeBSD__)
// Despite the name, this is just do_cpuid() with ECX as second input.
@@ -48,7 +50,11 @@ static inline void __cpuidex(int info[4], u32 function_id, u32 subfunction_id) {
: "a"(function_id), "c"(subfunction_id));
#endif
}
static inline void __cpuid(int info[4], u32 function_id) { return __cpuidex(info, function_id, 0); }
static inline void __cpuid(int info[4], u32 function_id) {
return __cpuidex(info, function_id, 0);
}
#define _XCR_XFEATURE_ENABLED_MASK 0
static inline u64 xgetbv(u32 index) {
u32 eax, edx;
@@ -56,10 +62,9 @@ static inline u64 xgetbv(u32 index) {
return ((u64)edx << 32) | eax;
}
#endif // _MSC_VER
#endif
namespace Common {
#ifdef ARCHITECTURE_x86_64
CPUCaps::Manufacturer CPUCaps::ParseManufacturer(std::string_view brand_string) {
if (brand_string == "GenuineIntel") {
return Manufacturer::Intel;
@@ -71,6 +76,142 @@ CPUCaps::Manufacturer CPUCaps::ParseManufacturer(std::string_view brand_string)
return Manufacturer::Unknown;
}
// Detects the various CPU features
static CPUCaps Detect() {
CPUCaps caps = {};
// Assumes the CPU supports the CPUID instruction. Those that don't would likely not support
// yuzu at all anyway
int cpu_id[4];
// Detect CPU's CPUID capabilities and grab manufacturer string
__cpuid(cpu_id, 0x00000000);
const u32 max_std_fn = cpu_id[0]; // EAX
std::memset(caps.brand_string, 0, std::size(caps.brand_string));
std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(u32));
std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(u32));
std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(u32));
caps.manufacturer = CPUCaps::ParseManufacturer(caps.brand_string);
// Set reasonable default cpu string even if brand string not available
std::strncpy(caps.cpu_string, caps.brand_string, std::size(caps.brand_string));
__cpuid(cpu_id, 0x80000000);
const u32 max_ex_fn = cpu_id[0];
// Detect family and other miscellaneous features
if (max_std_fn >= 1) {
__cpuid(cpu_id, 0x00000001);
caps.sse = Common::Bit<25>(cpu_id[3]);
caps.sse2 = Common::Bit<26>(cpu_id[3]);
caps.sse3 = Common::Bit<0>(cpu_id[2]);
caps.pclmulqdq = Common::Bit<1>(cpu_id[2]);
caps.ssse3 = Common::Bit<9>(cpu_id[2]);
caps.sse4_1 = Common::Bit<19>(cpu_id[2]);
caps.sse4_2 = Common::Bit<20>(cpu_id[2]);
caps.movbe = Common::Bit<22>(cpu_id[2]);
caps.popcnt = Common::Bit<23>(cpu_id[2]);
caps.aes = Common::Bit<25>(cpu_id[2]);
caps.f16c = Common::Bit<29>(cpu_id[2]);
// AVX support requires 3 separate checks:
// - Is the AVX bit set in CPUID?
// - Is the XSAVE bit set in CPUID?
// - XGETBV result has the XCR bit set.
if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) {
if ((xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
caps.avx = true;
if (Common::Bit<12>(cpu_id[2]))
caps.fma = true;
}
}
if (max_std_fn >= 7) {
__cpuidex(cpu_id, 0x00000007, 0x00000000);
// Can't enable AVX{2,512} unless the XSAVE/XGETBV checks above passed
if (caps.avx) {
caps.avx2 = Common::Bit<5>(cpu_id[1]);
caps.avx512f = Common::Bit<16>(cpu_id[1]);
caps.avx512dq = Common::Bit<17>(cpu_id[1]);
caps.avx512cd = Common::Bit<28>(cpu_id[1]);
caps.avx512bw = Common::Bit<30>(cpu_id[1]);
caps.avx512vl = Common::Bit<31>(cpu_id[1]);
caps.avx512vbmi = Common::Bit<1>(cpu_id[2]);
caps.avx512bitalg = Common::Bit<12>(cpu_id[2]);
}
caps.bmi1 = Common::Bit<3>(cpu_id[1]);
caps.bmi2 = Common::Bit<8>(cpu_id[1]);
caps.sha = Common::Bit<29>(cpu_id[1]);
caps.waitpkg = Common::Bit<5>(cpu_id[2]);
caps.gfni = Common::Bit<8>(cpu_id[2]);
__cpuidex(cpu_id, 0x00000007, 0x00000001);
caps.avx_vnni = caps.avx && Common::Bit<4>(cpu_id[0]);
}
}
if (max_ex_fn >= 0x80000004) {
// Extract CPU model string
__cpuid(cpu_id, 0x80000002);
std::memcpy(caps.cpu_string, cpu_id, sizeof(cpu_id));
__cpuid(cpu_id, 0x80000003);
std::memcpy(caps.cpu_string + 16, cpu_id, sizeof(cpu_id));
__cpuid(cpu_id, 0x80000004);
std::memcpy(caps.cpu_string + 32, cpu_id, sizeof(cpu_id));
}
if (max_ex_fn >= 0x80000001) {
// Check for more features
__cpuid(cpu_id, 0x80000001);
caps.lzcnt = Common::Bit<5>(cpu_id[2]);
caps.fma4 = Common::Bit<16>(cpu_id[2]);
caps.monitorx = Common::Bit<29>(cpu_id[2]);
}
if (max_ex_fn >= 0x80000007) {
__cpuid(cpu_id, 0x80000007);
caps.invariant_tsc = Common::Bit<8>(cpu_id[3]);
}
if (max_std_fn >= 0x15) {
__cpuid(cpu_id, 0x15);
caps.tsc_crystal_ratio_denominator = cpu_id[0];
caps.tsc_crystal_ratio_numerator = cpu_id[1];
caps.crystal_frequency = cpu_id[2];
// Some CPU models might not return a crystal frequency.
// The CPU model can be detected to use the values from turbostat
// https://github.com/torvalds/linux/blob/master/tools/power/x86/turbostat/turbostat.c#L5569
// but it's easier to just estimate the TSC tick rate for these cases.
if (caps.tsc_crystal_ratio_denominator) {
caps.tsc_frequency = static_cast<u64>(caps.crystal_frequency) *
caps.tsc_crystal_ratio_numerator /
caps.tsc_crystal_ratio_denominator;
} else {
caps.tsc_frequency = X64::EstimateRDTSCFrequency();
}
}
if (max_std_fn >= 0x16) {
__cpuid(cpu_id, 0x16);
caps.base_frequency = cpu_id[0];
caps.max_frequency = cpu_id[1];
caps.bus_frequency = cpu_id[2];
}
return caps;
}
const CPUCaps& GetCPUCaps() {
static CPUCaps caps = Detect();
return caps;
}
std::optional<int> GetProcessorCount() {
#if defined(_WIN32)
// Get the buffer length.
@@ -112,315 +253,4 @@ std::optional<int> GetProcessorCount() {
#endif
}
/// @brief Detects the various CPU features
const CPUCaps g_cpu_caps = [] {
CPUCaps caps = {};
// Assumes the CPU supports the CPUID instruction. Those that don't would likely not support
// yuzu at all anyway
int cpu_id[4];
// Detect CPU's CPUID capabilities and grab manufacturer string
__cpuid(cpu_id, 0x00000000);
const u32 max_std_fn = cpu_id[0]; // EAX
std::memset(caps.brand_string, 0, std::size(caps.brand_string));
std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(u32));
std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(u32));
std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(u32));
caps.manufacturer = CPUCaps::ParseManufacturer(caps.brand_string);
// Set reasonable default cpu string even if brand string not available
std::strncpy(caps.cpu_string, caps.brand_string, std::size(caps.brand_string));
__cpuid(cpu_id, 0x80000000);
const u32 max_ex_fn = cpu_id[0];
// Detect family and other miscellaneous features
if (max_std_fn >= 1) {
__cpuid(cpu_id, 0x00000001);
caps.sse3 = Common::Bit<0>(cpu_id[2]);
caps.pclmulqdq = Common::Bit<1>(cpu_id[2]);
caps.ssse3 = Common::Bit<9>(cpu_id[2]);
caps.sse4_1 = Common::Bit<19>(cpu_id[2]);
caps.sse4_2 = Common::Bit<20>(cpu_id[2]);
caps.movbe = Common::Bit<22>(cpu_id[2]);
caps.popcnt = Common::Bit<23>(cpu_id[2]);
caps.aes = Common::Bit<25>(cpu_id[2]);
caps.f16c = Common::Bit<29>(cpu_id[2]);
// AVX support requires 3 separate checks:
// - Is the AVX bit set in CPUID?
// - Is the XSAVE bit set in CPUID?
// - XGETBV result has the XCR bit set.
if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) {
if ((xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
caps.avx = true;
if (Common::Bit<12>(cpu_id[2]))
caps.fma = true;
}
}
if (max_std_fn >= 7) {
__cpuidex(cpu_id, 0x00000007, 0x00000000);
// Can't enable AVX{2,512} unless the XSAVE/XGETBV checks above passed
if (caps.avx) {
caps.avx2 = Common::Bit<5>(cpu_id[1]);
caps.avx512f = Common::Bit<16>(cpu_id[1]);
caps.avx512dq = Common::Bit<17>(cpu_id[1]);
caps.avx512cd = Common::Bit<28>(cpu_id[1]);
caps.avx512bw = Common::Bit<30>(cpu_id[1]);
caps.avx512vl = Common::Bit<31>(cpu_id[1]);
caps.avx512vbmi = Common::Bit<1>(cpu_id[2]);
caps.avx512bitalg = Common::Bit<12>(cpu_id[2]);
}
caps.bmi1 = Common::Bit<3>(cpu_id[1]);
caps.bmi2 = Common::Bit<8>(cpu_id[1]);
caps.sha = Common::Bit<29>(cpu_id[1]);
caps.waitpkg = Common::Bit<5>(cpu_id[2]);
caps.gfni = Common::Bit<8>(cpu_id[2]);
}
}
if (max_ex_fn >= 0x80000004) {
// Extract CPU model string
__cpuid(cpu_id, 0x80000002);
std::memcpy(caps.cpu_string, cpu_id, sizeof(cpu_id));
__cpuid(cpu_id, 0x80000003);
std::memcpy(caps.cpu_string + 16, cpu_id, sizeof(cpu_id));
__cpuid(cpu_id, 0x80000004);
std::memcpy(caps.cpu_string + 32, cpu_id, sizeof(cpu_id));
}
if (max_ex_fn >= 0x80000001) {
// Check for more features
__cpuid(cpu_id, 0x80000001);
caps.lzcnt = Common::Bit<5>(cpu_id[2]);
caps.monitorx = Common::Bit<29>(cpu_id[2]);
}
if (max_ex_fn >= 0x80000007) {
__cpuid(cpu_id, 0x80000007);
caps.invariant_tsc = Common::Bit<8>(cpu_id[3]);
}
if (max_std_fn >= 0x15) {
__cpuid(cpu_id, 0x15);
caps.tsc_crystal_ratio_denominator = cpu_id[0];
caps.tsc_crystal_ratio_numerator = cpu_id[1];
caps.crystal_frequency = cpu_id[2];
// Some CPU models might not return a crystal frequency.
// The CPU model can be detected to use the values from turbostat
// https://github.com/torvalds/linux/blob/master/tools/power/x86/turbostat/turbostat.c#L5569
// but it's easier to just estimate the TSC tick rate for these cases.
if (caps.tsc_crystal_ratio_denominator) {
caps.tsc_frequency = u64(caps.crystal_frequency)
* caps.tsc_crystal_ratio_numerator / caps.tsc_crystal_ratio_denominator;
} else {
caps.tsc_frequency = X64::EstimateRDTSCFrequency();
}
}
if (max_std_fn >= 0x16) {
__cpuid(cpu_id, 0x16);
caps.base_frequency = cpu_id[0];
caps.max_frequency = cpu_id[1];
caps.bus_frequency = cpu_id[2];
}
return caps;
}();
#else
#endif
#if defined(ARCHITECTURE_x86_64)
WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept
: rdtsc_frequency{rdtsc_frequency_}
, ns_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency_) : 0}
, us_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency_) : 0}
, ms_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency_) : 0}
, rdtsc_ns_factor{invariant_ ? GetFixedPoint64Factor(rdtsc_frequency_, NsRatio::den) : 1}
, cntpct_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency_) : 0}
, gputick_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency_) : 0}
, invariant{invariant_}
{}
std::chrono::nanoseconds WallClock::GetTimeNS() const {
if (!invariant)
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch());
return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_rdtsc_factor)};
}
std::chrono::microseconds WallClock::GetTimeUS() const {
if (!invariant)
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now().time_since_epoch());
return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_rdtsc_factor)};
}
std::chrono::milliseconds WallClock::GetTimeMS() const {
if (!invariant)
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch());
return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_rdtsc_factor)};
}
s64 WallClock::GetCNTPCT() const {
if (!invariant)
return GetUptime() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
return MultiplyHigh(GetUptime(), cntpct_rdtsc_factor);
}
s64 WallClock::GetGPUTick() const {
if (!invariant)
return GetUptime() * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
return MultiplyHigh(GetUptime(), gputick_rdtsc_factor);
}
s64 WallClock::GetUptime() const {
if (!invariant)
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
return s64(Common::X64::FencedRDTSC());
}
bool WallClock::IsNative() const {
return invariant;
}
u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
return invariant ? MultiplyHigh(ns.count(), rdtsc_ns_factor) : ns.count();
}
#elif defined(HAS_NCE)
namespace {
[[nodiscard]] Common::WallClock::FactorType GetFixedPointFactor(u64 num, u64 den) noexcept {
return (Common::WallClock::FactorType(num) << 64) / den;
}
[[nodiscard]] u64 MultiplyHigh(u64 m, Common::WallClock::FactorType factor) noexcept {
return static_cast<u64>((m * factor) >> 64);
}
[[nodiscard]] s64 GetHostCNTFRQ() noexcept {
u64 cntfrq_el0 = 0;
#ifdef ANDROID
std::string_view board{""};
char buffer[PROP_VALUE_MAX];
int len{__system_property_get("ro.product.board", buffer)};
board = std::string_view(buffer, static_cast<size_t>(len));
if (board == "s5e9925") { // Exynos 2200
cntfrq_el0 = 25600000;
} else if (board == "exynos2100") { // Exynos 2100
cntfrq_el0 = 26000000;
} else if (board == "exynos9810") { // Exynos 9810
cntfrq_el0 = 26000000;
} else if (board == "s5e8825") { // Exynos 1280
cntfrq_el0 = 26000000;
} else {
asm volatile("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0));
}
return cntfrq_el0;
#else
asm volatile("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0));
return cntfrq_el0;
#endif
}
} // namespace
WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept {
const u64 host_cntfrq = std::max<u64>(GetHostCNTFRQ(), 1);
ns_cntfrq_factor = GetFixedPointFactor(NsRatio::den, host_cntfrq);
us_cntfrq_factor = GetFixedPointFactor(UsRatio::den, host_cntfrq);
ms_cntfrq_factor = GetFixedPointFactor(MsRatio::den, host_cntfrq);
cntfrq_ns_factor = GetFixedPointFactor(host_cntfrq, NsRatio::den);
guest_cntfrq_factor = GetFixedPointFactor(CNTFRQ, host_cntfrq);
gputick_cntfrq_factor = GetFixedPointFactor(GPUTickFreq, host_cntfrq);
}
std::chrono::nanoseconds WallClock::GetTimeNS() const {
return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_cntfrq_factor)};
}
std::chrono::microseconds WallClock::GetTimeUS() const {
return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_cntfrq_factor)};
}
std::chrono::milliseconds WallClock::GetTimeMS() const {
return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_cntfrq_factor)};
}
s64 WallClock::GetCNTPCT() const {
return MultiplyHigh(GetUptime(), guest_cntfrq_factor);
}
s64 WallClock::GetGPUTick() const {
return MultiplyHigh(GetUptime(), gputick_cntfrq_factor);
}
s64 WallClock::GetUptime() const {
s64 cntvct_el0 = 0;
asm volatile(
"dsb ish\n\t"
"mrs %[cntvct_el0], cntvct_el0\n\t"
"dsb ish\n\t"
: [cntvct_el0] "=r"(cntvct_el0)
);
return cntvct_el0;
}
bool WallClock::IsNative() const {
return true;
}
u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
return MultiplyHigh(ns.count(), cntfrq_ns_factor);
}
#else
WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept {}
std::chrono::nanoseconds WallClock::GetTimeNS() const {
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch());
}
std::chrono::microseconds WallClock::GetTimeUS() const {
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now().time_since_epoch());
}
std::chrono::milliseconds WallClock::GetTimeMS() const {
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch());
}
s64 WallClock::GetCNTPCT() const {
return GetUptime() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
}
s64 WallClock::GetGPUTick() const {
return GetUptime() * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
}
s64 WallClock::GetUptime() const {
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
}
bool WallClock::IsNative() const {
return false;
}
u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
return ns.count();
}
#endif
// Wall clock MUST be initialized AFTER g_cpu_caps
// C++ only guarantees ctor init in the order they appear in TU
const WallClock g_wall_clock = [] {
#if defined(ARCHITECTURE_x86_64)
auto const& caps = Common::g_cpu_caps;
return WallClock(caps.invariant_tsc && caps.tsc_frequency >= std::nano::den, caps.tsc_frequency);
#elif defined(HAS_NCE)
return WallClock(false, 1);
#else
return WallClock(true, 1);
#endif
}();
} // namespace Common
+83
View File
@@ -0,0 +1,83 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <optional>
#include <string_view>
#include "common/common_types.h"
namespace Common {
/// x86/x64 CPU capabilities that may be detected by this module
struct CPUCaps {
enum class Manufacturer : u8 {
Unknown = 0,
Intel = 1,
AMD = 2,
Hygon = 3,
};
static Manufacturer ParseManufacturer(std::string_view brand_string);
Manufacturer manufacturer;
char brand_string[13];
char cpu_string[48];
u32 base_frequency;
u32 max_frequency;
u32 bus_frequency;
u32 tsc_crystal_ratio_denominator;
u32 tsc_crystal_ratio_numerator;
u32 crystal_frequency;
u64 tsc_frequency; // Derived from the above three values
bool sse : 1;
bool sse2 : 1;
bool sse3 : 1;
bool ssse3 : 1;
bool sse4_1 : 1;
bool sse4_2 : 1;
bool avx : 1;
bool avx_vnni : 1;
bool avx2 : 1;
bool avx512f : 1;
bool avx512dq : 1;
bool avx512cd : 1;
bool avx512bw : 1;
bool avx512vl : 1;
bool avx512vbmi : 1;
bool avx512bitalg : 1;
bool aes : 1;
bool bmi1 : 1;
bool bmi2 : 1;
bool f16c : 1;
bool fma : 1;
bool fma4 : 1;
bool gfni : 1;
bool invariant_tsc : 1;
bool lzcnt : 1;
bool monitorx : 1;
bool movbe : 1;
bool pclmulqdq : 1;
bool popcnt : 1;
bool sha : 1;
bool waitpkg : 1;
};
/**
* Gets the supported capabilities of the host CPU
* @return Reference to a CPUCaps struct with the detected host CPU capabilities
*/
const CPUCaps& GetCPUCaps();
/// Detects CPU core count
std::optional<int> GetProcessorCount();
} // namespace Common
+75
View File
@@ -0,0 +1,75 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <thread>
#ifdef _MSC_VER
#include <intrin.h>
#endif
#include "common/x64/cpu_detect.h"
#include "common/x64/cpu_wait.h"
#include "common/x64/rdtsc.h"
namespace Common::X64 {
namespace {
// 100,000 cycles is a reasonable amount of time to wait to save on CPU resources.
// For reference:
// At 1 GHz, 100K cycles is 100us
// At 2 GHz, 100K cycles is 50us
// At 4 GHz, 100K cycles is 25us
constexpr auto PauseCycles = 100'000U;
} // Anonymous namespace
#if defined(_MSC_VER) && !defined(__clang__)
__forceinline static void TPAUSE() {
static constexpr auto RequestC02State = 0U;
_tpause(RequestC02State, FencedRDTSC() + PauseCycles);
}
__forceinline static void MWAITX() {
static constexpr auto EnableWaitTimeFlag = 1U << 1;
static constexpr auto RequestC1State = 0U;
// monitor_var should be aligned to a cache line.
alignas(64) u64 monitor_var{};
_mm_monitorx(&monitor_var, 0, 0);
_mm_mwaitx(EnableWaitTimeFlag, RequestC1State, PauseCycles);
}
#else
static void TPAUSE() {
static constexpr auto RequestC02State = 0U;
const auto tsc = FencedRDTSC() + PauseCycles;
const auto eax = static_cast<u32>(tsc & 0xFFFFFFFF);
const auto edx = static_cast<u32>(tsc >> 32);
asm volatile("tpause %0" : : "r"(RequestC02State), "d"(edx), "a"(eax));
}
static void MWAITX() {
static constexpr auto EnableWaitTimeFlag = 1U << 1;
static constexpr auto RequestC1State = 0U;
// monitor_var should be aligned to a cache line.
alignas(64) u64 monitor_var{};
asm volatile("monitorx" : : "a"(&monitor_var), "c"(0), "d"(0));
asm volatile("mwaitx" : : "a"(RequestC1State), "b"(PauseCycles), "c"(EnableWaitTimeFlag));
}
#endif
void MicroSleep() {
static const bool has_waitpkg = GetCPUCaps().waitpkg;
static const bool has_monitorx = GetCPUCaps().monitorx;
if (has_waitpkg) {
TPAUSE();
} else if (has_monitorx) {
MWAITX();
} else {
std::this_thread::yield();
}
}
} // namespace Common::X64
+10
View File
@@ -0,0 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
namespace Common::X64 {
void MicroSleep();
} // namespace Common::X64
+46
View File
@@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/uint128.h"
#include "common/x64/native_clock.h"
#include "common/x64/rdtsc.h"
namespace Common::X64 {
NativeClock::NativeClock(u64 rdtsc_frequency_)
: rdtsc_frequency{rdtsc_frequency_}, ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den,
rdtsc_frequency)},
us_rdtsc_factor{GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency)},
ms_rdtsc_factor{GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency)},
cntpct_rdtsc_factor{GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency)},
gputick_rdtsc_factor{GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency)} {}
std::chrono::nanoseconds NativeClock::GetTimeNS() const {
return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_rdtsc_factor)};
}
std::chrono::microseconds NativeClock::GetTimeUS() const {
return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_rdtsc_factor)};
}
std::chrono::milliseconds NativeClock::GetTimeMS() const {
return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_rdtsc_factor)};
}
s64 NativeClock::GetCNTPCT() const {
return MultiplyHigh(GetUptime(), cntpct_rdtsc_factor);
}
s64 NativeClock::GetGPUTick() const {
return MultiplyHigh(GetUptime(), gputick_rdtsc_factor);
}
s64 NativeClock::GetUptime() const {
return static_cast<s64>(FencedRDTSC());
}
bool NativeClock::IsNative() const {
return true;
}
} // namespace Common::X64
+38
View File
@@ -0,0 +1,38 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/wall_clock.h"
namespace Common::X64 {
class NativeClock final : public WallClock {
public:
explicit NativeClock(u64 rdtsc_frequency_);
std::chrono::nanoseconds GetTimeNS() const override;
std::chrono::microseconds GetTimeUS() const override;
std::chrono::milliseconds GetTimeMS() const override;
s64 GetCNTPCT() const override;
s64 GetGPUTick() const override;
s64 GetUptime() const override;
bool IsNative() const override;
private:
u64 rdtsc_frequency;
u64 ns_rdtsc_factor;
u64 us_rdtsc_factor;
u64 ms_rdtsc_factor;
u64 cntpct_rdtsc_factor;
u64 gputick_rdtsc_factor;
};
} // namespace Common::X64
@@ -1,37 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <type_traits>
#include <bitset>
#include <initializer_list>
#include <xbyak/xbyak.h>
#include "common/assert.h"
// xbyak hates human beings
#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wconversion"
#pragma GCC diagnostic ignored "-Wshadow"
#endif
#ifdef __clang__
#pragma clang diagnostic ignored "-Wconversion"
#pragma clang diagnostic ignored "-Wshadow"
#endif
// You must ensure this matches with src/common/x64/xbyak.h on root dir
#include <ankerl/unordered_dense.h>
#include <boost/unordered_map.hpp>
#define XBYAK_STD_UNORDERED_SET ankerl::unordered_dense::set
#define XBYAK_STD_UNORDERED_MAP ankerl::unordered_dense::map
#define XBYAK_STD_UNORDERED_MULTIMAP boost::unordered_multimap
#include <xbyak/xbyak.h>
#include <xbyak/xbyak_util.h>
#include <xbyak/xbyak.h>
namespace Common::X64 {
constexpr size_t RegToIndex(const Xbyak::Reg& reg) {
@@ -198,13 +174,12 @@ inline ABIFrameInfo ABI_CalculateFrameSize(std::bitset<32> regs, size_t rsp_alig
rsp_alignment -= subtraction;
subtraction += rsp_alignment & 0xF;
return ABIFrameInfo{
s32(subtraction),
s32(subtraction - xmm_base_subtraction)
};
return ABIFrameInfo{static_cast<s32>(subtraction),
static_cast<s32>(subtraction - xmm_base_subtraction)};
}
inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs, size_t rsp_alignment, size_t needed_frame_size = 0) {
inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs,
size_t rsp_alignment, size_t needed_frame_size = 0) {
auto frame_info = ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size);
for (size_t i = 0; i < regs.size(); ++i) {
@@ -227,7 +202,8 @@ inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::b
return ABI_SHADOW_SPACE;
}
inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs, size_t rsp_alignment, size_t needed_frame_size = 0) {
inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs,
size_t rsp_alignment, size_t needed_frame_size = 0) {
auto frame_info = ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size);
for (size_t i = 0; i < regs.size(); ++i) {
@@ -250,38 +226,4 @@ inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bits
}
}
// Constants for use with cmpps/cmpss
enum {
CMP_EQ = 0,
CMP_LT = 1,
CMP_LE = 2,
CMP_UNORD = 3,
CMP_NEQ = 4,
CMP_NLT = 5,
CMP_NLE = 6,
CMP_ORD = 7,
};
constexpr bool IsWithin2G(uintptr_t ref, uintptr_t target) {
const u64 distance = target - (ref + 5);
return !(distance >= 0x8000'0000ULL && distance <= ~0x8000'0000ULL);
}
inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
return IsWithin2G(reinterpret_cast<uintptr_t>(code.getCurr()), target);
}
template <typename T>
inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
size_t addr = reinterpret_cast<size_t>(f);
if (IsWithin2G(code, addr)) {
code.call(f);
} else {
// ABI_RETURN is a safe temp register to use before a call
code.mov(ABI_RETURN, addr);
code.call(ABI_RETURN);
}
}
} // namespace Common::X64
+46
View File
@@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <type_traits>
#include <xbyak/xbyak.h>
#include "common/x64/xbyak_abi.h"
namespace Common::X64 {
// Constants for use with cmpps/cmpss
enum {
CMP_EQ = 0,
CMP_LT = 1,
CMP_LE = 2,
CMP_UNORD = 3,
CMP_NEQ = 4,
CMP_NLT = 5,
CMP_NLE = 6,
CMP_ORD = 7,
};
constexpr bool IsWithin2G(uintptr_t ref, uintptr_t target) {
const u64 distance = target - (ref + 5);
return !(distance >= 0x8000'0000ULL && distance <= ~0x8000'0000ULL);
}
inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
return IsWithin2G(reinterpret_cast<uintptr_t>(code.getCurr()), target);
}
template <typename T>
inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
size_t addr = reinterpret_cast<size_t>(f);
if (IsWithin2G(code, addr)) {
code.call(f);
} else {
// ABI_RETURN is a safe temp register to use before a call
code.mov(ABI_RETURN, addr);
code.call(ABI_RETURN);
}
}
} // namespace Common::X64
+2 -2
View File
@@ -1200,7 +1200,7 @@ else()
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-cast-function-type>
$<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>)
# pre-clang19 will spam with "OH DID YOU MEAN THIS?" otherwise...
if (CXX_CLANG AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 20)
if (CXX_CLANG AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19)
target_compile_options(core PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-cast-function-type-mismatch>)
endif()
endif()
@@ -1246,7 +1246,7 @@ if (HAS_NCE)
target_link_libraries(core PRIVATE merry::oaknut)
endif()
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_loongarch64)
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64)
target_sources(core PRIVATE
arm/dynarmic/arm_dynarmic.h
arm/dynarmic/arm_dynarmic_64.cpp
+6 -3
View File
@@ -113,7 +113,8 @@ void DynarmicCallbacks32::CallSVC(u32 swi) {
}
void DynarmicCallbacks32::AddTicks(u64 ticks) {
ASSERT(!m_parent.m_uses_wall_clock && "Dynarmic ticking disabled");
ASSERT_MSG(!m_parent.m_uses_wall_clock, "Dynarmic ticking disabled");
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
// rough approximation of the amount of executed ticks in the system, it may be thrown off
// if not all cores are doing a similar amount of work. Instead of doing this, we should
@@ -122,12 +123,14 @@ void DynarmicCallbacks32::AddTicks(u64 ticks) {
u64 amortized_ticks = ticks / Core::Hardware::NUM_CPU_CORES;
// Always execute at least one tick.
amortized_ticks = std::max<u64>(amortized_ticks, 1);
m_parent.m_system.CoreTiming().AddTicks(amortized_ticks);
}
u64 DynarmicCallbacks32::GetTicksRemaining() {
ASSERT(!m_parent.m_uses_wall_clock && "Dynarmic ticking disabled");
return std::max<s64>(m_parent.m_system.CoreTiming().downcount, 0);
ASSERT_MSG(!m_parent.m_uses_wall_clock, "Dynarmic ticking disabled");
return std::max<s64>(m_parent.m_system.CoreTiming().GetDowncount(), 0);
}
bool DynarmicCallbacks32::CheckMemoryAccess(u64 addr, u64 size, Kernel::DebugWatchpointType type) {
+4 -2
View File
@@ -150,7 +150,8 @@ void DynarmicCallbacks64::CallSVC(u32 svc) {
}
void DynarmicCallbacks64::AddTicks(u64 ticks) {
ASSERT(!m_parent.m_uses_wall_clock && "Dynarmic ticking disabled");
ASSERT_MSG(!m_parent.m_uses_wall_clock, "Dynarmic ticking disabled");
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
// rough approximation of the amount of executed ticks in the system, it may be thrown off
// if not all cores are doing a similar amount of work. Instead of doing this, we should
@@ -159,12 +160,13 @@ void DynarmicCallbacks64::AddTicks(u64 ticks) {
u64 amortized_ticks = ticks / Core::Hardware::NUM_CPU_CORES;
// Always execute at least one tick.
amortized_ticks = std::max<u64>(amortized_ticks, 1);
m_parent.m_system.CoreTiming().AddTicks(amortized_ticks);
}
u64 DynarmicCallbacks64::GetTicksRemaining() {
ASSERT(!m_parent.m_uses_wall_clock && "Dynarmic ticking disabled");
return std::max<s64>(m_parent.m_system.CoreTiming().downcount, 0);
return std::max<s64>(m_parent.m_system.CoreTiming().GetDowncount(), 0);
}
u64 DynarmicCallbacks64::GetCNTPCT() {
+2 -6
View File
@@ -3,7 +3,7 @@
#include <numeric>
#include <bit>
#include "common/cpu_features.h"
#include "common/arm64/native_clock.h"
#include "common/alignment.h"
#include "common/literals.h"
#include "core/arm/nce/arm_nce.h"
@@ -578,11 +578,7 @@ void Patcher::WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg,
}
void Patcher::WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::VectorCodeGenerator& cg) {
#if defined(HAS_NCE)
static Common::WallClock clock(false, 1);
#else
static Common::WallClock clock(true, 1);
#endif
static Common::Arm64::NativeClock clock{};
const auto factor = clock.GetGuestCNTFRQFactor();
const auto raw_factor = std::bit_cast<std::array<u64, 2>>(factor);
+94 -55
View File
@@ -8,13 +8,15 @@
#include <mutex>
#include <string>
#include <tuple>
#include "common/cpu_features.h"
#include "common/cpu_features.h"
#ifdef _WIN32
#include "common/windows/timer_resolution.h"
#endif
#ifdef ARCHITECTURE_x86_64
#include "common/x64/cpu_wait.h"
#endif
#include "common/settings.h"
#include "core/core_timing.h"
#include "core/hardware_properties.h"
@@ -45,7 +47,8 @@ struct CoreTiming::Event {
}
};
CoreTiming::CoreTiming() = default;
CoreTiming::CoreTiming() : clock{Common::CreateOptimalClock()} {}
CoreTiming::~CoreTiming() {
Reset();
}
@@ -54,36 +57,15 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
Reset();
on_thread_init = std::move(on_thread_init_);
event_fifo_id = 0;
shutting_down = false;
cpu_ticks = 0;
if (is_multicore) {
timer_thread = std::jthread([this](std::stop_token stop_token) {
timer_thread.emplace([](CoreTiming& instance) {
Common::SetCurrentThreadName("HostTiming");
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
on_thread_init();
has_started = true;
// base frequency in MHz: 1ns (10^-9) = 1GHz (10^9)
while (!stop_token.stop_requested()) {
while (!paused && !stop_token.stop_requested()) {
paused_set = false;
if (auto const next_time = Advance(); next_time) {
// There are more events left in the queue, wait until the next event.
auto const wait_time = *next_time - GetGlobalTimeNs().count();
if (wait_time > 0) {
event.WaitFor(std::chrono::nanoseconds(wait_time));
}
} else {
// Queue is empty, wait until another event is scheduled and signals us to
// continue.
wait_set = true;
event.Wait();
}
wait_set = false;
}
paused_set = true;
pause_event.Wait();
}
});
instance.on_thread_init();
instance.ThreadLoop();
}, std::ref(*this));
}
}
@@ -108,7 +90,7 @@ void CoreTiming::SyncPause(bool is_paused) {
}
Pause(is_paused);
if (timer_thread.joinable()) {
if (timer_thread) {
if (!is_paused) {
pause_event.Set();
}
@@ -208,22 +190,33 @@ void CoreTiming::ResetTicks() {
}
u64 CoreTiming::GetClockTicks() const {
u64 fres = is_multicore ? Common::g_wall_clock.GetCNTPCT() : Common::WallClock::CPUTickToCNTPCT(cpu_ticks);
if (auto const overclock = Settings::values.fast_cpu_time.GetValue(); overclock != Settings::CpuClock::Off) {
fres = u64(f64(fres) * (1.7 + 0.3 * u32(overclock)));
u64 fres;
if (is_multicore) [[likely]] {
fres = clock->GetCNTPCT();
} else {
fres = Common::WallClock::CPUTickToCNTPCT(cpu_ticks);
}
if (::Settings::values.sync_core_speed.GetValue()) {
auto const ticks = f64(fres);
auto const speed_limit = f64(Settings::SpeedLimit()) * 0.01;
return u64(ticks / speed_limit);
}
return fres;
const auto overclock = Settings::values.fast_cpu_time.GetValue();
if (overclock != Settings::CpuClock::Off) {
fres = (u64) ((double) fres * (1.7 + 0.3 * u32(overclock)));
}
if (Settings::values.sync_core_speed.GetValue()) {
const auto ticks = double(fres);
const auto speed_limit = double(Settings::SpeedLimit())*0.01;
return u64(ticks/speed_limit);
} else {
return fres;
}
}
u64 CoreTiming::GetGPUTicks() const {
return is_multicore
? Common::g_wall_clock.GetGPUTick()
: Common::WallClock::CPUTickToGPUTick(cpu_ticks);
if (is_multicore) [[likely]] {
return clock->GetGPUTick();
}
return Common::WallClock::CPUTickToGPUTick(cpu_ticks);
}
std::optional<s64> CoreTiming::Advance() {
@@ -285,29 +278,75 @@ std::optional<s64> CoreTiming::Advance() {
}
}
void CoreTiming::ThreadLoop() {
has_started = true;
while (!shutting_down) {
while (!paused) {
paused_set = false;
const auto next_time = Advance();
if (next_time) {
// There are more events left in the queue, wait until the next event.
auto wait_time = *next_time - GetGlobalTimeNs().count();
if (wait_time > 0) {
#ifdef _WIN32
while (!paused && !event.IsSet() && wait_time > 0) {
wait_time = *next_time - GetGlobalTimeNs().count();
if (wait_time >= timer_resolution_ns) {
Common::Windows::SleepForOneTick();
} else {
#ifdef ARCHITECTURE_x86_64
Common::X64::MicroSleep();
#else
std::this_thread::yield();
#endif
}
}
if (event.IsSet()) {
event.Reset();
}
#else
event.WaitFor(std::chrono::nanoseconds(wait_time));
#endif
}
} else {
// Queue is empty, wait until another event is scheduled and signals us to
// continue.
wait_set = true;
event.Wait();
}
wait_set = false;
}
paused_set = true;
pause_event.Wait();
}
}
void CoreTiming::Reset() {
paused = true;
shutting_down = true;
pause_event.Set();
event.Set();
if (timer_thread.joinable()) {
timer_thread.request_stop();
timer_thread.join();
if (timer_thread) {
timer_thread->join();
}
timer_thread.reset();
has_started = false;
}
/// @brief Returns current time in nanoseconds.
std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const noexcept {
return is_multicore
? Common::g_wall_clock.GetTimeNS()
: std::chrono::nanoseconds{Common::WallClock::CPUTickToNS(cpu_ticks)};
std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const {
if (is_multicore) [[likely]] {
return clock->GetTimeNS();
}
return std::chrono::nanoseconds{Common::WallClock::CPUTickToNS(cpu_ticks)};
}
/// @brief Returns current time in microseconds.
std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const noexcept {
return is_multicore
? Common::g_wall_clock.GetTimeUS()
: std::chrono::microseconds{Common::WallClock::CPUTickToUS(cpu_ticks)};
std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const {
if (is_multicore) [[likely]] {
return clock->GetTimeUS();
}
return std::chrono::microseconds{Common::WallClock::CPUTickToUS(cpu_ticks)};
}
#ifdef _WIN32
+28 -13
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -19,7 +19,7 @@
#include "common/common_types.h"
#include "common/thread.h"
#include "common/cpu_features.h"
#include "common/wall_clock.h"
namespace Core::Timing {
@@ -118,7 +118,7 @@ public:
void Idle();
s64 GetDowncount() const noexcept {
s64 GetDowncount() const {
return downcount;
}
@@ -128,8 +128,11 @@ public:
/// Returns the current GPU tick value.
u64 GetGPUTicks() const;
[[nodiscard]] std::chrono::microseconds GetGlobalTimeUs() const noexcept;
[[nodiscard]] std::chrono::nanoseconds GetGlobalTimeNs() const noexcept;
/// Returns current time in microseconds.
std::chrono::microseconds GetGlobalTimeUs() const;
/// Returns current time in nanoseconds.
std::chrono::nanoseconds GetGlobalTimeNs() const;
/// Checks for events manually and returns time in nanoseconds for next event, threadsafe.
std::optional<s64> Advance();
@@ -138,32 +141,44 @@ public:
void SetTimerResolutionNs(std::chrono::nanoseconds ns);
#endif
private:
struct Event;
void ThreadLoop();
void Reset();
using heap_t = boost::heap::fibonacci_heap<CoreTiming::Event, boost::heap::compare<std::greater<>>>;
heap_t event_queue;
std::unique_ptr<Common::WallClock> clock;
s64 global_timer = 0;
#ifdef _WIN32
s64 timer_resolution_ns;
#endif
using heap_t =
boost::heap::fibonacci_heap<CoreTiming::Event, boost::heap::compare<std::greater<>>>;
heap_t event_queue;
u64 event_fifo_id = 0;
s64 pause_end_time{};
/// Cycle timing
u64 cpu_ticks{};
s64 downcount{};
Common::Event event{};
Common::Event pause_event{};
std::function<void()> on_thread_init{};
std::jthread timer_thread;
mutable std::mutex basic_lock;
std::mutex advance_lock;
std::optional<std::jthread> timer_thread;
std::atomic<bool> paused{};
std::atomic<bool> paused_set{};
std::atomic<bool> wait_set{};
std::atomic<bool> shutting_down{};
std::atomic<bool> has_started{};
std::function<void()> on_thread_init{};
bool is_multicore{};
s64 pause_end_time{};
/// Cycle timing
u64 cpu_ticks{};
s64 downcount{};
};
/// Creates a core timing event with the given name and callback.
@@ -1,48 +1,86 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <span>
#include <vector>
#include "common/swap.h"
#include "core/file_sys/system_archive/time_zone_binary.h"
#include "core/file_sys/vfs/vfs_static.h"
#include "core/file_sys/vfs/vfs_types.h"
#include "core/file_sys/vfs/vfs_vector.h"
#include "nx_tzdb.h"
namespace FileSys::SystemArchive {
const static std::map<std::string, const std::map<const char*, const std::vector<u8>>&>
tzdb_zoneinfo_dirs = {{"Africa", NxTzdb::africa},
{"America", NxTzdb::america},
{"Antarctica", NxTzdb::antarctica},
{"Arctic", NxTzdb::arctic},
{"Asia", NxTzdb::asia},
{"Atlantic", NxTzdb::atlantic},
{"Australia", NxTzdb::australia},
{"Brazil", NxTzdb::brazil},
{"Canada", NxTzdb::canada},
{"Chile", NxTzdb::chile},
{"Etc", NxTzdb::etc},
{"Europe", NxTzdb::europe},
{"Indian", NxTzdb::indian},
{"Mexico", NxTzdb::mexico},
{"Pacific", NxTzdb::pacific},
{"US", NxTzdb::us}};
const static std::map<std::string, const std::map<const char*, const std::vector<u8>>&>
tzdb_america_dirs = {{"Argentina", NxTzdb::america_argentina},
{"Indiana", NxTzdb::america_indiana},
{"Kentucky", NxTzdb::america_kentucky},
{"North_Dakota", NxTzdb::america_north_dakota}};
static void GenerateFiles(std::vector<VirtualFile>& directory,
const std::map<const char*, const std::vector<u8>>& files) {
for (const auto& [filename, data] : files) {
const auto data_copy{data};
const std::string filename_copy{filename};
VirtualFile file{
std::make_shared<VectorVfsFile>(std::move(data_copy), std::move(filename_copy))};
directory.push_back(file);
}
}
static std::vector<VirtualFile> GenerateZoneinfoFiles() {
std::vector<VirtualFile> zoneinfo_files;
GenerateFiles(zoneinfo_files, NxTzdb::zoneinfo);
return zoneinfo_files;
}
VirtualDir TimeZoneBinary() {
std::vector<VirtualDir> america_sub_dirs;
america_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_america_argentina(), std::vector<VirtualDir>{}, "Argentina"));
america_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_america_indiana(), std::vector<VirtualDir>{}, "Indiana"));
america_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_america_kentucky(), std::vector<VirtualDir>{}, "Kentucky"));
america_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_america_north_dakota(), std::vector<VirtualDir>{}, "North_Dakota"));
for (const auto& [dir_name, files] : tzdb_america_dirs) {
std::vector<VirtualFile> vfs_files;
GenerateFiles(vfs_files, files);
america_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(
std::move(vfs_files), std::vector<VirtualDir>{}, dir_name));
}
std::vector<VirtualDir> zoneinfo_sub_dirs;
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_africa(), std::vector<VirtualDir>{}, "Africa"));
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_america(), std::move(america_sub_dirs), "America"));
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_antarctica(), std::vector<VirtualDir>{}, "Antarctica"));
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_arctic(), std::vector<VirtualDir>{}, "Arctic"));
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_asia(), std::vector<VirtualDir>{}, "Asia"));
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_atlantic(), std::vector<VirtualDir>{}, "Atlantic"));
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_australia(), std::vector<VirtualDir>{}, "Australia"));
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_brazil(), std::vector<VirtualDir>{}, "Brazil"));
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_canada(), std::vector<VirtualDir>{}, "Canada"));
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_chile(), std::vector<VirtualDir>{}, "Chile"));
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_etc(), std::vector<VirtualDir>{}, "Etc"));
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_europe(), std::vector<VirtualDir>{}, "Europe"));
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_indian(), std::vector<VirtualDir>{}, "Indian"));
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_mexico(), std::vector<VirtualDir>{}, "Mexico"));
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_pacific(), std::vector<VirtualDir>{}, "Pacific"));
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_us(), std::vector<VirtualDir>{}, "US"));
std::vector<VirtualDir> zoneinfo_dir{std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_zoneinfo(), std::move(zoneinfo_sub_dirs), "zoneinfo")};
// last files (root)
return std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_base(), std::move(zoneinfo_dir), "data");
for (const auto& [dir_name, files] : tzdb_zoneinfo_dirs) {
std::vector<VirtualFile> vfs_files;
GenerateFiles(vfs_files, files);
if (dir_name == "America") {
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(
std::move(vfs_files), std::move(america_sub_dirs), dir_name));
} else {
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(
std::move(vfs_files), std::vector<VirtualDir>{}, dir_name));
}
}
std::vector<VirtualDir> zoneinfo_dir{std::make_shared<VectorVfsDirectory>(
GenerateZoneinfoFiles(), std::move(zoneinfo_sub_dirs), "zoneinfo")};
std::vector<VirtualFile> root_files;
GenerateFiles(root_files, NxTzdb::base);
return std::make_shared<VectorVfsDirectory>(std::move(root_files), std::move(zoneinfo_dir),
"data");
}
} // namespace FileSys::SystemArchive
-9
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -19,12 +16,6 @@
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/vfs/vfs_types.h"
#undef CreateFile
#undef CopyFile
#undef MoveFile
#undef DeleteFile
#undef CreateDirectory
namespace FileSys {
// An enumeration representing what can be at the end of a path in a VfsFilesystem
-13
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -16,16 +13,6 @@ namespace IPC {
/// Size of the command buffer area, in 32-bit words.
constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32);
/// Must match bitfields
constexpr std::size_t MAX_BUFFER_DESCRIPTORS = 16;
constexpr std::size_t MAX_INCOMING_MOVE_HANDLERS = 16;
constexpr std::size_t MAX_INCOMING_COPY_HANDLERS = 16;
/// Doesn't need to match bitfields but usually not big enough
constexpr std::size_t MAX_OUTGOING_COPY_OBJECTS = 16;
constexpr std::size_t MAX_OUTGOING_MOVE_OBJECTS = 16;
constexpr std::size_t MAX_OUTGOING_DOMAIN_OBJECTS = 16;
enum class ControlCommand : u32 {
ConvertSessionToDomain = 0,
ConvertDomainToSession = 1,
+1 -5
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -271,8 +268,7 @@ Result KCapabilities::SetHandleTableCapability(const u32 cap) {
Result KCapabilities::SetDebugFlagsCapability(const u32 cap) {
// Validate.
const DebugFlags pack{cap};
// TODO: Enabling this breaks compatibility with HBloader and such
//R_UNLESS(pack.reserved == 0, ResultReservedUsed);
R_UNLESS(pack.reserved == 0, ResultReservedUsed);
DebugFlags debug_capabilities{m_debug_capabilities};
debug_capabilities.allow_debug.Assign(pack.allow_debug);
+2 -5
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -15,8 +12,8 @@ Result DebugActiveProcess(Core::System& system, Handle* out_handle, uint64_t pro
}
Result BreakDebugProcess(Core::System& system, Handle debug_handle) {
LOG_WARNING(Service, "(STUBBED) called");
R_SUCCEED();
UNIMPLEMENTED();
R_THROW(ResultNotImplemented);
}
Result TerminateDebugProcess(Core::System& system, Handle debug_handle) {
+6 -8
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -18,11 +15,12 @@ void LoopProcess(Core::System& system) {
auto module = std::make_shared<Module>();
auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("apm", std::make_shared<APM>(system, module, system.GetAPMController(), "apm"));
server_manager->RegisterNamedService("apm:am", std::make_shared<APM>(system, module, system.GetAPMController(), "apm:am"));
// Removed on [+8.0.0] but kept for compatibility
server_manager->RegisterNamedService("apm:p", std::make_shared<APM>(system, module, system.GetAPMController(), "apm:p"));
server_manager->RegisterNamedService("apm:sys", std::make_shared<APM_Sys>(system, system.GetAPMController()));
server_manager->RegisterNamedService(
"apm", std::make_shared<APM>(system, module, system.GetAPMController(), "apm"));
server_manager->RegisterNamedService(
"apm:am", std::make_shared<APM>(system, module, system.GetAPMController(), "apm:am"));
server_manager->RegisterNamedService(
"apm:sys", std::make_shared<APM_Sys>(system, system.GetAPMController()));
ServerManager::RunServer(std::move(server_manager));
}
+12 -6
View File
@@ -128,12 +128,10 @@ Result SessionRequestManager::HandleDomainSyncRequest(Kernel::KServerSession* se
return ResultSuccess;
}
HLERequestContext::HLERequestContext(Kernel::KernelCore& kernel_, Core::Memory::Memory& memory_, Kernel::KServerSession* server_session_, Kernel::KThread* thread_)
: server_session(server_session_)
, thread(thread_)
, kernel{kernel_}
, memory{memory_}
{
HLERequestContext::HLERequestContext(Kernel::KernelCore& kernel_, Core::Memory::Memory& memory_,
Kernel::KServerSession* server_session_,
Kernel::KThread* thread_)
: server_session(server_session_), thread(thread_), kernel{kernel_}, memory{memory_} {
cmd_buf[0] = 0;
}
@@ -157,6 +155,9 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
}
if (incoming) {
// Populate the object lists with the data in the IPC request.
incoming_copy_handles.reserve(handle_descriptor_header->num_handles_to_copy);
incoming_move_handles.reserve(handle_descriptor_header->num_handles_to_move);
for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) {
incoming_copy_handles.push_back(rp.Pop<Handle>());
}
@@ -171,6 +172,11 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
}
}
buffer_x_descriptors.reserve(command_header->num_buf_x_descriptors);
buffer_a_descriptors.reserve(command_header->num_buf_a_descriptors);
buffer_b_descriptors.reserve(command_header->num_buf_b_descriptors);
buffer_w_descriptors.reserve(command_header->num_buf_w_descriptors);
for (u32 i = 0; i < command_header->num_buf_x_descriptors; ++i) {
buffer_x_descriptors.push_back(rp.PopRaw<IPC::BufferDescriptorX>());
}
+26 -26
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -14,7 +11,6 @@
#include <string>
#include <type_traits>
#include <vector>
#include <boost/container/static_vector.hpp>
#include "common/assert.h"
#include "common/common_types.h"
@@ -185,7 +181,8 @@ private:
*/
class HLERequestContext {
public:
explicit HLERequestContext(Kernel::KernelCore& kernel, Core::Memory::Memory& memory, Kernel::KServerSession* session, Kernel::KThread* thread);
explicit HLERequestContext(Kernel::KernelCore& kernel, Core::Memory::Memory& memory,
Kernel::KServerSession* session, Kernel::KThread* thread);
~HLERequestContext();
/// Returns a pointer to the IPC command buffer for this request.
@@ -236,19 +233,19 @@ public:
return data_payload_offset;
}
[[nodiscard]] const boost::container::static_vector<IPC::BufferDescriptorX, 16>& BufferDescriptorX() const {
[[nodiscard]] const std::vector<IPC::BufferDescriptorX>& BufferDescriptorX() const {
return buffer_x_descriptors;
}
[[nodiscard]] const boost::container::static_vector<IPC::BufferDescriptorABW, 16>& BufferDescriptorA() const {
[[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorA() const {
return buffer_a_descriptors;
}
[[nodiscard]] const boost::container::static_vector<IPC::BufferDescriptorABW, 16>& BufferDescriptorB() const {
[[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorB() const {
return buffer_b_descriptors;
}
[[nodiscard]] const boost::container::static_vector<IPC::BufferDescriptorC, 16>& BufferDescriptorC() const {
[[nodiscard]] const std::vector<IPC::BufferDescriptorC>& BufferDescriptorC() const {
return buffer_c_descriptors;
}
@@ -402,35 +399,38 @@ private:
Kernel::KHandleTable* client_handle_table{};
Kernel::KThread* thread{};
boost::container::static_vector<IPC::BufferDescriptorX, IPC::MAX_BUFFER_DESCRIPTORS> buffer_x_descriptors;
boost::container::static_vector<IPC::BufferDescriptorABW, IPC::MAX_BUFFER_DESCRIPTORS> buffer_a_descriptors;
boost::container::static_vector<IPC::BufferDescriptorABW, IPC::MAX_BUFFER_DESCRIPTORS> buffer_b_descriptors;
boost::container::static_vector<IPC::BufferDescriptorABW, IPC::MAX_BUFFER_DESCRIPTORS> buffer_w_descriptors;
boost::container::static_vector<IPC::BufferDescriptorC, IPC::MAX_BUFFER_DESCRIPTORS> buffer_c_descriptors;
boost::container::static_vector<Handle, IPC::MAX_INCOMING_MOVE_HANDLERS> incoming_move_handles;
boost::container::static_vector<Handle, IPC::MAX_INCOMING_COPY_HANDLERS> incoming_copy_handles;
boost::container::static_vector<Kernel::KAutoObject*, IPC::MAX_OUTGOING_MOVE_OBJECTS> outgoing_move_objects;
boost::container::static_vector<Kernel::KAutoObject*, IPC::MAX_OUTGOING_COPY_OBJECTS> outgoing_copy_objects;
boost::container::static_vector<SessionRequestHandlerPtr, IPC::MAX_OUTGOING_DOMAIN_OBJECTS> outgoing_domain_objects;
std::vector<Handle> incoming_move_handles;
std::vector<Handle> incoming_copy_handles;
mutable std::array<Common::ScratchBuffer<u8>, 3> read_buffer_data_a{};
mutable std::array<Common::ScratchBuffer<u8>, 3> read_buffer_data_x{};
std::vector<Kernel::KAutoObject*> outgoing_move_objects;
std::vector<Kernel::KAutoObject*> outgoing_copy_objects;
std::vector<SessionRequestHandlerPtr> outgoing_domain_objects;
std::optional<IPC::CommandHeader> command_header;
std::optional<IPC::HandleDescriptorHeader> handle_descriptor_header;
std::optional<IPC::DataPayloadHeader> data_payload_header;
std::optional<IPC::DomainMessageHeader> domain_message_header;
std::weak_ptr<SessionRequestManager> manager{};
Kernel::KernelCore& kernel;
Core::Memory::Memory& memory;
std::vector<IPC::BufferDescriptorX> buffer_x_descriptors;
std::vector<IPC::BufferDescriptorABW> buffer_a_descriptors;
std::vector<IPC::BufferDescriptorABW> buffer_b_descriptors;
std::vector<IPC::BufferDescriptorABW> buffer_w_descriptors;
std::vector<IPC::BufferDescriptorC> buffer_c_descriptors;
u64 pid{};
u32_le command{};
u64 pid{};
u32 write_size{};
u32 data_payload_offset{};
u32 handles_offset{};
u32 domain_offset{};
bool is_deferred = false;
std::weak_ptr<SessionRequestManager> manager{};
bool is_deferred{false};
Kernel::KernelCore& kernel;
Core::Memory::Memory& memory;
mutable std::array<Common::ScratchBuffer<u8>, 3> read_buffer_data_a{};
mutable std::array<Common::ScratchBuffer<u8>, 3> read_buffer_data_x{};
};
} // namespace Service
+16 -11
View File
@@ -277,17 +277,7 @@ private:
state.store(State::Processing);
evt_processing->Signal();
worker = std::thread([this]() {
using namespace std::chrono_literals;
scan_results = Network::ScanWifiNetworks(3s);
{
std::scoped_lock lk{g_scan_mtx};
g_last_scan_results = scan_results;
}
// choose result code
const bool ok = !scan_results.empty();
Finish(ok ? ResultSuccess : ResultPendingConnection);
});
worker = std::thread(&IScanRequest::WorkerThread, this);
IPC::ResponseBuilder{ctx, 2}.Push(ResultSuccess);
}
@@ -318,6 +308,21 @@ private:
enum class State { Idle, Processing, Finished };
void WorkerThread() {
using namespace std::chrono_literals;
scan_results = Network::ScanWifiNetworks(3s);
{
std::scoped_lock lk{g_scan_mtx};
g_last_scan_results = scan_results;
}
// choose result code
const bool ok = !scan_results.empty();
Finish(ok ? ResultSuccess : ResultPendingConnection);
}
void Finish(Result rc) {
worker_result.store(rc);
state.store(State::Finished);
@@ -79,9 +79,7 @@ void nvdisp_disp0::Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers
});
for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) {
if (layer.acquire_fence.fences[i].id >= 0) {
output_fences.push_back(layer.acquire_fence.fences[i]);
}
output_fences.push_back(layer.acquire_fence.fences[i]);
}
}
@@ -10,7 +10,6 @@
#include "common/assert.h"
#include "common/logging.h"
#include "common/settings.h"
#include "common/cpu_features.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/kernel.h"
@@ -27,10 +26,8 @@ namespace Service::android {
BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
std::shared_ptr<BufferQueueCore> buffer_queue_core_,
Service::Nvidia::NvCore::NvMap& nvmap_)
: service_context{service_context_}, core{std::move(buffer_queue_core_)}
, slots(core->slots)
, nvmap(nvmap_)
{
: service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots),
clock{Common::CreateOptimalClock()}, nvmap(nvmap_) {
buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
}
@@ -488,7 +485,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
slots[slot].buffer_state = BufferState::Queued;
slots[slot].frame_number = core->frame_counter;
slots[slot].queue_time = timestamp;
slots[slot].presentation_time = Common::g_wall_clock.GetTimeNS().count();
slots[slot].presentation_time = clock->GetTimeNS().count();
slots[slot].fence = fence;
item.slot = slot;
@@ -14,7 +14,7 @@
#include <mutex>
#include "common/common_funcs.h"
#include "common/cpu_features.h"
#include "common/wall_clock.h"
#include "core/hle/service/nvdrv/nvdata.h"
#include "core/hle/service/nvnflinger/binder.h"
#include "core/hle/service/nvnflinger/buffer_queue_defs.h"
@@ -89,6 +89,8 @@ private:
s32 next_callback_ticket{};
s32 current_callback_ticket{};
std::condition_variable_any callback_condition;
std::unique_ptr<Common::WallClock> clock;
Service::Nvidia::NvCore::NvMap& nvmap;
};
+4 -4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2012 The Android Open Source Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -18,8 +15,10 @@ namespace Service::android {
class Fence {
public:
constexpr Fence() = default;
static constexpr Fence NoFence() {
Fence fence{};
Fence fence;
fence.fences[0].id = -1;
fence.fences[1].id = -1;
fence.fences[2].id = -1;
@@ -27,6 +26,7 @@ public:
return fence;
}
public:
u32 num_fences{};
std::array<Service::Nvidia::NvFence, 4> fences{};
};
+2 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -13,7 +13,7 @@
#include "common/common_types.h"
#include "common/intrusive_list.h"
#include "common/uuid.h"
#include "common/cpu_features.h"
#include "common/wall_clock.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/psc/time/errors.h"
+3 -3
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -20,7 +20,6 @@
#include "common/settings.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/result.h"
@@ -125,7 +124,8 @@ json GetFullDataAuto(const std::string& timestamp, u64 title_id, Core::System& s
}
template <bool read_value, typename DescriptorType>
json GetHLEBufferDescriptorData(const boost::container::static_vector<DescriptorType, IPC::MAX_BUFFER_DESCRIPTORS>& buffer, Core::Memory::Memory& memory) {
json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer,
Core::Memory::Memory& memory) {
auto buffer_out = json::array();
for (const auto& desc : buffer) {
auto entry = json{
+2 -1
View File
@@ -7,7 +7,8 @@
add_library(yuzu-room STATIC EXCLUDE_FROM_ALL
yuzu_room.cpp
yuzu_room.h
yuzu_room.rc)
yuzu_room.rc
)
target_link_libraries(yuzu-room PRIVATE common network)
if (ENABLE_WEB_SERVICE)

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