Compare commits

..

8 Commits

Author SHA1 Message Date
lizzie 9b76d602a0 Fix license headers 2026-09-04 21:08:22 +02:00
lizzie e2095c2712 evil 2026-09-04 21:08:22 +02:00
lizzie b7391f9398 param vec 2026-09-04 21:08:22 +02:00
lizzie bce9486e5f [common] remove unused vector_math.h fluff
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-04 21:08:22 +02:00
lizzie f6e7686038 [ci] Fix macOS "no type named 'free' in namespace 'std' (#4347)
Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4347
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-04 13:32:36 +02:00
xbzk 1dcc574591 [gdb] fix conn state to use same pipe (instead of a copy) (#4339)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

I was trying to use GDB stub to investigate MHR and it was not connecting.
Error was suggesting it was not available for connection although i saw it booting on logs.
Debugged and noticed signal_pipe_ was not the correct one, and followed the example of client_socket_.
GDB stub is now working,  on windows, at least.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4339
Reviewed-by: Lizzie and Samuel <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-02 14:04:35 +02:00
lizzie 5c20f244c9 [cmake] stop building OpenGL on Android (#4342)
Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

we don't even support OpenGL on android

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4342
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-02 04:07:24 +02:00
crueter aa4e494c08 [android] Replace Oboe audio with SDL3 audio (#4338)
Supersedes #4335.

Replaces the Oboe audio backend with SDL3, since we already support it.
A few caveats:

- SDL defines a JVM OnLoad method already, so we have to instead hijack
  NativeLibrary for id_cache initialization
- This also means some internal fields in Double/Integer/Boolean (namely
  `value`) can't be accessed, so I switched those to methods for now.
- SDL Java sources are pulled in from the CI package.
- Since we can't use SDLActivity, `SDL.setupJNI()` handles everything

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

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4338
Reviewed-by: Lizzie and Samuel <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-02 00:29:58 +02:00
76 changed files with 691 additions and 2369 deletions
-92
View File
@@ -1,92 +0,0 @@
#!/bin/sh -ex
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
NUM_JOBS=$(nproc 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || echo 2)
: "${CCACHE:=false}"
RETURN=0
usage() {
cat <<EOF
Usage: $0 [-b|--build-type BUILD_TYPE] [-o|--outdir OUTPUT_DIRECTORY]
Build script for Emscripten (using wasm64).
Options:
--build-type Set the CMake build type (Release|RelWithDebInfo|MinSizeRel|Debug)
Default: Release
--outdir Set the output directory
Default: build
EOF
exit "$RETURN"
}
die() {
echo "-- ! $*" >&2
RETURN=1 usage
}
type() {
[ -z "$1" ] && die "You must specify a valid type."
TYPE="$1"
}
outdir() {
[ -z "$1" ] && die "You must specify a valid output directory."
OUTDIR="$1"
}
while true; do
case "$1" in
-r|--release) DEVEL=false ;;
-b|--build-type) type "$2"; shift ;;
-o|--outdir) outdir "$2"; shift ;;
-h|--help) usage ;;
*) break ;;
esac
shift
done
: "${TYPE:=Release}"
: "${DEVEL:=true}"
: "${OUTDIR:=build}"
# -sMEMORY64 must be specified twice, see below
# The CMake toolchain file will match against MEMORY64 but will fail to match if:
# - it's either -sMEMORY64
# - or it's either -sMEMORY64=1
# The line in question:
# if (CMAKE_C_FLAGS MATCHES "MEMORY64")
# However why need to specify -sMEMORY64=1 then? Oh that's because if you didn't set
# the =1, it would assume you meant =0, which equates to not specifying it at all
# This seems to be fixed in later versions but occurs atleast on 4.0.3-git and below.
emcmake cmake -B "$OUTDIR" -G "Unix Makefiles" \
-DCMAKE_BUILD_TYPE=${TYPE} \
-DENABLE_OPENGL=OFF \
-DENABLE_LTO=OFF \
-DENABLE_QT=OFF \
-DENABLE_UNITY_BUILD=OFF \
-DENABLE_QT_TRANSLATION=OFF \
-DENABLE_CUBEB=OFF \
-DENABLE_LIBUSB=OFF \
-DENABLE_UPDATE_CHECKER=OFF \
-DENABLE_WEB_SERVICE=OFF \
-DUSE_DISCORD_PRESENCE=OFF \
-DENABLE_WIFI_SCAN=OFF \
-DUSE_FASTER_LINKER=ON \
-DYUZU_STATIC_BUILD=ON \
-DYUZU_USE_BUNDLED_OPENSSL=OFF \
-DYUZU_USE_EXTERNAL_FFMPEG=ON \
-Dzstd_FORCE_BUNDLED=ON \
-DOpenSSL_FORCE_BUNDLED=ON \
-DEMSCRIPTEN_SYSTEM_PROCESSOR=wasm \
-DCMAKE_C_FLAGS="-s MEMORY64 -m64 -pipe -sMEMORY64=1" \
-DCMAKE_CXX_FLAGS="-s MEMORY64 -m64 -pipe -sMEMORY64=1" \
-DCMAKE_EXE_LINKER_FLAGS="-sMEMORY64=1 -m64 -Wl,-mwasm64 -sASYNCIFY=1" \
-DCMAKE_C_LINK_FLAGS="-sMEMORY64=1 -m64 -Wl,-mwasm64 -sASYNCIFY=1" \
-DCMAKE_CXX_LINK_FLAGS="-sMEMORY64=1 -m64 -Wl,-mwasm64 -sASYNCIFY=1"
cmake --build "$OUTDIR" -- -j$NUM_JOBS
+3
View File
@@ -63,3 +63,6 @@ artifacts
/install* /install*
vulkansdk*.exe vulkansdk*.exe
*.tar.zst *.tar.zst
# Build-time generated external sources
externals/generated
@@ -1,40 +0,0 @@
diff --git a/cmake/ConfigureOpenSSL.cmake b/cmake/ConfigureOpenSSL.cmake
index 3012e05..2ae23ff 100644
--- a/cmake/ConfigureOpenSSL.cmake
+++ b/cmake/ConfigureOpenSSL.cmake
@@ -108,7 +108,8 @@ function(configure_openssl)
)
if(NOT "${CONFIGURE_OPTIONS_OLD}" STREQUAL "")
- if(CONFIGURE_OPTIONS STREQUAL CONFIGURE_OPTIONS_OLD)
+ # TODO(lizzie): Emscripten has issues with rebuilding due to the wrapper it uses
+ if(CMAKE_SYSTEM_NAME MATCHES "Emscripten" OR CONFIGURE_OPTIONS STREQUAL CONFIGURE_OPTIONS_OLD)
message(STATUS "Found previous configure results. Don't perform configuration")
return()
endif()
@@ -134,10 +135,24 @@ function(configure_openssl)
set(VERBOSE_OPTION OUTPUT_QUIET)
endif()
+ if (CMAKE_SYSTEM_NAME MATCHES "Emscripten")
+ set(EMSCRIPTEN_CMAKE_WRAPPER "emcmake")
+ find_program(EMCC emcc REQUIRED)
+ set(EMSCRIPTEN_LINKER ${EMCC})
+ list(APPEND CONFIGURE_COMMAND wasm64)
+ else()
+ set(EMSCRIPTEN_CMAKE_WRAPPER "")
+ set(EMSCRIPTEN_LINKER ${CMAKE_LINKER})
+ endif ()
+
execute_process(
- COMMAND ${CMAKE_COMMAND} -E env
+ COMMAND ${EMSCRIPTEN_CMAKE_WRAPPER} ${CMAKE_COMMAND} -E env
"CFLAGS=${CMAKE_C_FLAGS}"
"CXXFLAGS=${CMAKE_CXX_FLAGS}"
+ "LDFLAGS=${CMAKE_CXX_LINK_FLAGS}"
+ "CC=${CMAKE_C_COMPILER}"
+ "CXX=${CMAKE_CXX_COMPILER}"
+ "LD=${EMSCRIPTEN_LINKER}"
${CONFIGURE_COMMAND}
WORKING_DIRECTORY ${CONFIGURE_BUILD_DIR}
${VERBOSE_OPTION}
-112
View File
@@ -1,112 +0,0 @@
diff --git a/Configurations/10-main.conf b/Configurations/10-main.conf
index e62721e..243feb4 100644
--- a/Configurations/10-main.conf
+++ b/Configurations/10-main.conf
@@ -1970,6 +1970,26 @@ my %targets = (
multilib => "64",
},
+ "wasm32" => {
+ inherit_from => [ "BASE_unix" ],
+ CC => "emcc",
+ CXX => "emc++",
+ cflags => combine("--target=wasm32-unknown-emscripten", threads("-pthread")),
+ cxxflags => combine("--target=wasm32-unknown-emscripten", threads("-pthread")),
+ lib_cppflags => add("-DL_ENDIAN"),
+ bn_ops => "THIRTY_TWO_BIT",
+ },
+ "wasm64" => {
+ inherit_from => [ "BASE_unix" ],
+ CC => "emcc",
+ CXX => "emc++",
+ cflags => combine("--target=wasm64-unknown-emscripten", threads("-pthread")),
+ cxxflags => combine("--target=wasm64-unknown-emscripten", threads("-pthread")),
+ lib_cppflags => add("-DL_ENDIAN"),
+ bn_ops => "SIXTY_FOUR_BIT_LONG",
+ },
+
+
#### uClinux
"uClinux-dist" => {
inherit_from => [ "BASE_unix" ],
diff --git a/crypto/rand/rand_lib.c b/crypto/rand/rand_lib.c
index d9e8f02..7faf347 100644
--- a/crypto/rand/rand_lib.c
+++ b/crypto/rand/rand_lib.c
@@ -379,17 +379,27 @@ void RAND_add(const void *buf, int num, double randomness)
#if !defined(OPENSSL_NO_DEPRECATED_1_1_0)
int RAND_pseudo_bytes(unsigned char *buf, int num)
{
+#if defined(__wasi__)
+ arc4random_buf(buf, num);
+ return 1;
+#elif defined(__EMSCRIPTEN__)
+ return 1;
+#else
const RAND_METHOD *meth = RAND_get_rand_method();
if (meth != NULL && meth->pseudorand != NULL)
return meth->pseudorand(buf, num);
ERR_raise(ERR_LIB_RAND, RAND_R_FUNC_NOT_IMPLEMENTED);
return -1;
+#endif
}
#endif
int RAND_status(void)
{
+#if defined(__EMSCRIPTEN__) || defined(__wasi__)
+ return 1;
+#else
EVP_RAND_CTX *rand;
#ifndef OPENSSL_NO_DEPRECATED_3_0
const RAND_METHOD *meth = RAND_get_rand_method();
@@ -401,6 +411,7 @@ int RAND_status(void)
if ((rand = RAND_get0_primary(NULL)) == NULL)
return 0;
return EVP_RAND_get_state(rand) == EVP_RAND_STATE_READY;
+#endif
}
#else /* !FIPS_MODULE */
@@ -420,6 +431,12 @@ const RAND_METHOD *RAND_get_rand_method(void)
int RAND_priv_bytes_ex(OSSL_LIB_CTX *ctx, unsigned char *buf, size_t num,
unsigned int strength)
{
+#if defined(__wasi__)
+ arc4random_buf(buf, num);
+ return 1;
+#elif defined(__EMSCRIPTEN__)
+ return 1;
+#else
RAND_GLOBAL *dgbl;
EVP_RAND_CTX *rand;
#if !defined(OPENSSL_NO_DEPRECATED_3_0) && !defined(FIPS_MODULE)
@@ -451,6 +468,7 @@ int RAND_priv_bytes_ex(OSSL_LIB_CTX *ctx, unsigned char *buf, size_t num,
return EVP_RAND_generate(rand, buf, num, strength, 0, NULL, 0);
return 0;
+#endif
}
int RAND_priv_bytes(unsigned char *buf, int num)
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
index 3d21801..e026f8f 100644
--- a/ssl/ssl_cert.c
+++ b/ssl/ssl_cert.c
@@ -941,6 +941,7 @@ done:
return ret;
}
+#ifndef OPENSSL_NO_POSIX_IO
int SSL_add_dir_cert_subjects_to_stack(STACK_OF(X509_NAME) *stack,
const char *dir)
{
@@ -1016,6 +1017,7 @@ err:
return ret;
}
+#endif
static int add_uris_recursive(STACK_OF(X509_NAME) *stack,
const char *uri, int depth)
+8 -24
View File
@@ -214,8 +214,6 @@ if(MSVC)
endif() endif()
# TODO(crueter): Cleanup, each dep that has a bundled option should allow to choose between bundled, external, system # 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)
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON) option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
set(EXT_DEFAULT OFF) set(EXT_DEFAULT OFF)
@@ -223,6 +221,8 @@ if (MSVC OR ANDROID)
set(EXT_DEFAULT ON) set(EXT_DEFAULT ON)
endif() endif()
option(YUZU_USE_BUNDLED_SDL3 "Download bundled SDL3 build" ${EXT_DEFAULT})
# ffmpeg # ffmpeg
option(YUZU_USE_BUNDLED_FFMPEG "Download bundled FFmpeg" ${EXT_DEFAULT}) option(YUZU_USE_BUNDLED_FFMPEG "Download bundled FFmpeg" ${EXT_DEFAULT})
cmake_dependent_option(YUZU_USE_EXTERNAL_FFMPEG "Build FFmpeg from external source" "${SOLARIS}" "NOT WIN32 AND NOT ANDROID" OFF) cmake_dependent_option(YUZU_USE_EXTERNAL_FFMPEG "Build FFmpeg from external source" "${SOLARIS}" "NOT WIN32 AND NOT ANDROID" OFF)
@@ -238,7 +238,7 @@ option(YUZU_USE_BUNDLED_SIRIT "Download bundled sirit" ${BUNDLED_SIRIT_DEFAULT})
# FreeBSD 15+ has libusb, versions below should disable it # FreeBSD 15+ has libusb, versions below should disable it
cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "WIN32 OR LINUX OR FREEBSD OR APPLE" OFF) cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "WIN32 OR LINUX OR FREEBSD OR APPLE" OFF)
cmake_dependent_option(ENABLE_OPENGL "Enable OpenGL" ON "NOT (WIN32 AND ARCHITECTURE_arm64) AND NOT APPLE" OFF) cmake_dependent_option(ENABLE_OPENGL "Enable OpenGL" ON "NOT (WIN32 AND ARCHITECTURE_arm64) AND NOT APPLE AND NOT ANDROID" OFF)
mark_as_advanced(FORCE ENABLE_OPENGL) mark_as_advanced(FORCE ENABLE_OPENGL)
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON) option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
@@ -377,15 +377,6 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
# Prefer the -pthread flag on Linux. # Prefer the -pthread flag on Linux.
set(THREADS_PREFER_PTHREAD_FLAG ON) set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
# It is absolutely promordial to enable on Emscripten
# Not only this allows to use std::thread and std::jthread without exceptions
# but it also fixes several issues related to MT operations.
# ...and CMake doesn't include it by default even when we specify
# that we prefer the pthread flag; why is that? I don't know.
if (EMSCRIPTEN)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-pthread>)
add_link_options($<$<COMPILE_LANGUAGE:C,CXX>:-pthread>)
endif()
find_package(RenderDoc MODULE) find_package(RenderDoc MODULE)
@@ -414,12 +405,7 @@ set(BUILD_TESTING OFF)
set(ENABLE_TESTING OFF) set(ENABLE_TESTING OFF)
# boost # boost
if (EMSCRIPTEN) set(BOOST_INCLUDE_LIBRARIES algorithm icl pool container heap asio headers process filesystem crc variant)
set(BOOST_INCLUDE_LIBRARIES algorithm icl pool container heap headers filesystem crc variant)
set(BOOST_CONTAINER_HEADER_ONLY ON)
else()
set(BOOST_INCLUDE_LIBRARIES algorithm icl pool container heap asio headers process filesystem crc variant)
endif()
AddJsonPackage(boost) AddJsonPackage(boost)
@@ -440,12 +426,10 @@ if (Boost_ADDED)
target_compile_options(boost_heap INTERFACE $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-shadow>) target_compile_options(boost_heap INTERFACE $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-shadow>)
target_compile_options(boost_icl INTERFACE $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-shadow>) target_compile_options(boost_icl INTERFACE $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-shadow>)
# May not exist (i.e emscripten) target_compile_options(boost_asio INTERFACE
if (TARGET boost_asio) $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-conversion>
target_compile_options(boost_asio INTERFACE $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-implicit-fallthrough>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-conversion> )
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-implicit-fallthrough>)
endif()
endif() endif()
endif() endif()
+3 -11
View File
@@ -115,7 +115,7 @@
"find_args": "MODULE GLOBAL", "find_args": "MODULE GLOBAL",
"hash": "159ed94965018f2a371d45a3bfc1961e5fb1549e501ded70a6b4532d7fe99d0579c18b5195aff6e35f96f399b426cea2650ec9fb75ef80d4c9edeccb51f2e6c9", "hash": "159ed94965018f2a371d45a3bfc1961e5fb1549e501ded70a6b4532d7fe99d0579c18b5195aff6e35f96f399b426cea2650ec9fb75ef80d4c9edeccb51f2e6c9",
"options": [ "options": [
"HTTPLIB_REQUIRE_OPENSSL OFF", "HTTPLIB_REQUIRE_OPENSSL ON",
"HTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES ON" "HTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES ON"
], ],
"patches": [ "patches": [
@@ -186,19 +186,12 @@
"repo": "eden-emulator/oaknut", "repo": "eden-emulator/oaknut",
"version": "v2.0.3" "version": "v2.0.3"
}, },
"oboe": {
"bundled": true,
"hash": "ce4011afe7345370d4ead3b891cd69a5ef224b129535783586c0ca75051d303ed446e6c7f10bde8da31fff58d6e307f1732a3ffd03b249f9ef1fd48fd4132715",
"repo": "google/oboe",
"version": "1.10.0"
},
"openssl": { "openssl": {
"hash": "29002ce50cb95a4f4f1d0e9d3f684401fbd4eac34203dc2eef3b6334af5d44aa46bf788b63a6f5c139c383eafb7269ae87a58a9a3ad5912903b9773e545ccc0a", "hash": "29002ce50cb95a4f4f1d0e9d3f684401fbd4eac34203dc2eef3b6334af5d44aa46bf788b63a6f5c139c383eafb7269ae87a58a9a3ad5912903b9773e545ccc0a",
"min_version": "3.0.0", "min_version": "3.0.0",
"package": "OpenSSL", "package": "OpenSSL",
"patches": [ "patches": [
"0001-add-bundled-cert.patch", "0001-add-bundled-cert.patch"
"0002-wasm-support.patch"
], ],
"repo": "openssl/openssl", "repo": "openssl/openssl",
"version": "openssl-3.6.2" "version": "openssl-3.6.2"
@@ -220,8 +213,7 @@
"0001-cpmutil-compat.patch", "0001-cpmutil-compat.patch",
"0002-use-ccache.patch", "0002-use-ccache.patch",
"0003-use-cmake-compiler-flags.patch", "0003-use-cmake-compiler-flags.patch",
"0004-use-shell-wrapper.patch", "0004-use-shell-wrapper.patch"
"0005-wasm-support.patch"
], ],
"repo": "jimmy-park/openssl-cmake", "repo": "jimmy-park/openssl-cmake",
"version": "3.6.2" "version": "3.6.2"
-17
View File
@@ -12,7 +12,6 @@
- [NetBSD](#netbsd) - [NetBSD](#netbsd)
- [MSYS2](#msys2) - [MSYS2](#msys2)
- [RedoxOS](#redoxos) - [RedoxOS](#redoxos)
- [WebAssembly](#webassembly)
<!-- /TOC --> <!-- /TOC -->
## Arch Linux ## Arch Linux
@@ -243,19 +242,3 @@ find ./*/ -name "*.dll" | while read -r dll; do deps "$dll"; done
The package install may randomly hang at times, in which case it has to be restarted. ALWAYS do a `sudo pkg update` or the chances of it hanging will be close to 90%. If "multiple" installs fail at once, try installing 1 by 1 the packages. The package install may randomly hang at times, in which case it has to be restarted. ALWAYS do a `sudo pkg update` or the chances of it hanging will be close to 90%. If "multiple" installs fail at once, try installing 1 by 1 the packages.
When CMake invokes certain file syscalls - it may sometimes cause crashes or corruptions on the (kernel?) address space - so reboot the system if there is a "hang" in CMake. When CMake invokes certain file syscalls - it may sometimes cause crashes or corruptions on the (kernel?) address space - so reboot the system if there is a "hang" in CMake.
## WebAssembly
**It doesn't run on a browser yet.**
WebAssembly or "WASM" for short is a *mainly 32-bit* virtual "architecture" which we only bootstrap on 64-bit only. This means not only the program runs 2x slower than it would due to using JS BigInt, it also means we need to go out of our way to enable proper 64-bit support via `-sMEMORY64=1`, however once again, some Emscripten quirks force us to specify `-s MEMORY64` and `-sMEMORY64=1` at the same time: see the [CI build script](../.ci/wasm/build.sh).
The WebAssembly target is very heavy on resources and requires at least 4 times the normal amount of resources that a native build would. Additionally, the only supported environment is Firefox at the moment, node.js and `wasmtime are not supported (PRs welcome!)
If running under Firefox and you hit "out of memory" on dev console, close the entire tab, then open it back again, see [this issue](https://github.com/emscripten-core/emscripten/issues/8126).
To run the binary (after building) you should be fine with `node ./eden-cli.js`. For obvious reasons no Qt frontend is available on WASM, support for Vulkan is done charily via [llvmpipe2wasm](https://github.com/Devsh-Graphics-Programming/llvmpipe2wasm).
If you run into the error "acorn.js can't be found" check [this associated issue](https://github.com/emscripten-core/emscripten/issues/13368), the fix in short is `npm --global install acorn`. On FreeBSD you could run `npm` under root, or you could do the sane thing and do `sudo chown -R $USER /usr/local/lib/node_modules/ /usr/local/bin/` (remember to restore permissions afterwards!) unless you wish to run `npm` under root which is generally a bad idea.
2026-06-09: As of writing, no Dynarmic-based JIT is possible on this target, full interpreted emulation is the only reasonable option. While there is some efforts on making a JIT like [here](https://github.com/wingo/wasm-jit) or [here](https://wingolog.org/archives/2022/08/18/just-in-time-code-generation-within-webassembly), the result is so latency expensive we're better off using an interpreter instead.
-8
View File
@@ -361,14 +361,6 @@ pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel b
[Caveats](./Caveats.md#haikuos). [Caveats](./Caveats.md#haikuos).
</details>
<details>
<summary>WebAssembly</summary>
Emscripten: The default installation should provide enough.
[Caveats](./Caveats.md#wasm).
</details> </details>
<details> <details>
<summary>RedoxOS</summary> <summary>RedoxOS</summary>
+1 -1
View File
@@ -76,7 +76,7 @@ The following options are desktop only.
- `ENABLE_LIBUSB` (ON) Enable the use of the libusb input backend (HIGHLY RECOMMENDED) - `ENABLE_LIBUSB` (ON) Enable the use of the libusb input backend (HIGHLY RECOMMENDED)
- `ENABLE_OPENGL` (ON) Enable the OpenGL graphics backend - `ENABLE_OPENGL` (ON) Enable the OpenGL graphics backend
- Unavailable on Windows/ARM64 - Unavailable on Windows/ARM64 and on Android
- You probably shouldn't turn this off. - You probably shouldn't turn this off.
### Qt ### Qt
+37 -43
View File
@@ -136,48 +136,49 @@ if(ENABLE_CUBEB)
endif() endif()
endif() endif()
if (NOT ANDROID) if (NOT YUZU_USE_BUNDLED_SDL3)
if (NOT YUZU_USE_BUNDLED_SDL3) if (NOT WIN32)
if (NOT WIN32) # Yuzu itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers
# 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)
# Since 2.0.18 Atomic+Threads required for HIDAPI/libusb (see https://github.com/libsdl-org/SDL/issues/5095) # Yuzu-cmd also needs: Video (depends on Loadso/Dlopen)
# Yuzu-cmd also needs: Video (depends on Loadso/Dlopen) # CPUinfo also required for SDL Audio, at least until 2.28.0 (see https://github.com/libsdl-org/SDL/issues/7809)
# CPUinfo also required for SDL Audio, at least until 2.28.0 (see https://github.com/libsdl-org/SDL/issues/7809) set(SDL_UNUSED_SUBSYSTEMS
set(SDL_UNUSED_SUBSYSTEMS File Filesystem
File Filesystem Locale Power Render)
Locale Power Render) foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS})
foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS}) string(TOUPPER ${_SUB} _OPT)
string(TOUPPER ${_SUB} _OPT) set(SDL_${_OPT} OFF)
set(SDL_${_OPT} OFF) endforeach()
endforeach()
set(HIDAPI ON) set(HIDAPI ON)
endif()
if (APPLE)
set(SDL_FILE ON)
endif()
AddJsonPackage(sdl3)
else()
message(STATUS "Using bundled SDL3")
AddJsonPackage(sdl3-ci)
endif() endif()
# Normalize SDL3 link target across package variants. if (APPLE)
# Some SDL3 packages export only SDL3::SDL3-shared or SDL3::SDL3-static. set(SDL_FILE ON)
if (NOT TARGET SDL3::SDL3) endif()
if (TARGET SDL3::SDL3-shared)
add_library(SDL3::SDL3 ALIAS SDL3::SDL3-shared) AddJsonPackage(sdl3)
elseif (TARGET SDL3::SDL3-static) else()
add_library(SDL3::SDL3 ALIAS SDL3::SDL3-static) message(STATUS "Using bundled SDL3")
else() AddJsonPackage(sdl3-ci)
message(FATAL_ERROR "SDL3 package found, but no usable SDL3 target was exported")
endif() # copy sdl java sources for consumption by Gradle
if (ANDROID)
file(COPY ${SDL3_SOURCE_DIR}/java DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/generated/sdl)
endif() endif()
endif() endif()
set(BUILD_SHARED_LIBS OFF) # 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()
# SPIRV Headers # SPIRV Headers
AddJsonPackage(spirv-headers) AddJsonPackage(spirv-headers)
@@ -385,13 +386,6 @@ if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client)
endif() endif()
endif() endif()
# oboe
if (ANDROID)
AddJsonPackage(oboe)
add_library(oboe::oboe ALIAS oboe)
endif()
if (APPLE) if (APPLE)
# moltenvk # moltenvk
if (NOT YUZU_USE_BUNDLED_MOLTENVK) if (NOT YUZU_USE_BUNDLED_MOLTENVK)
+5 -9
View File
@@ -94,10 +94,6 @@ function(detect_architecture_symbols)
endfunction() endfunction()
# arches here are put in a sane default order of importance # arches here are put in a sane default order of importance
# EXCEPT FOR WASM, which must be probed for FIRST, because some genius
# decided to also allow the host architecture to be defined when building
# for the emscripten target, absolutely lovely detail.
#
# notably, amd64, arm64, and riscv (in order) are BY FAR the most common # notably, amd64, arm64, and riscv (in order) are BY FAR the most common
# mips is pretty popular in embedded # mips is pretty popular in embedded
# ppc64 is pretty popular in supercomputing # ppc64 is pretty popular in supercomputing
@@ -105,11 +101,6 @@ endfunction()
# ia64 exists # ia64 exists
# the rest exist, but are probably less popular than ia64 # the rest exist, but are probably less popular than ia64
detect_architecture_symbols(
ARCH wasm
SYMBOLS
"__EMSCRIPTEN__")
detect_architecture_symbols( detect_architecture_symbols(
ARCH arm64 ARCH arm64
SYMBOLS SYMBOLS
@@ -212,6 +203,11 @@ detect_architecture_symbols(
"__loongarch__" "__loongarch__"
"__loongarch64") "__loongarch64")
detect_architecture_symbols(
ARCH wasm
SYMBOLS
"__EMSCRIPTEN__")
# "generic" target # "generic" target
# If you have reached this point, you're on some as-of-yet unsupported architecture. # If you have reached this point, you're on some as-of-yet unsupported architecture.
# See the docs up above for known unsupported architectures # See the docs up above for known unsupported architectures
-25
View File
@@ -20,9 +20,6 @@ elseif (${CMAKE_SYSTEM_NAME} STREQUAL "managarm")
set(MANAGARM ON) set(MANAGARM ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Haiku") elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Haiku")
set(HAIKUOS ON) set(HAIKUOS ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten")
set(EMSCRIPTEN ON)
message(WARNING "${CMAKE_LIBRARY_ARCHITECTURE} support is highly experimental!!!")
endif() endif()
# BSD # BSD
@@ -100,25 +97,3 @@ if (MINGW)
set(CMAKE_EXE_LINKER_FLAGS_RELEASE set(CMAKE_EXE_LINKER_FLAGS_RELEASE
"${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${MINGW_FLAGS}") "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${MINGW_FLAGS}")
endif() endif()
if (EMSCRIPTEN)
set(EMSCRIPTEN_C_FLAGS "-s MEMORY64 -m64 -pipe -sMEMORY64=1")
set(EMSCRIPTEN_LINK_FLAGS "-sMEMORY64=1 -m64 -Wl,-mwasm64 -sASYNCIFY=1")
# This prevents FFmpeg and other libraries from assuming it's the host's CPU
# Additionally some Emscripten installs may not be very good... generally
set(EMSCRIPTEN_SYSTEM_PROCESSOR wasm)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EMSCRIPTEN_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} ${EMSCRIPTEN_C_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${EMSCRIPTEN_LINK_FLAGS}")
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} ${EMSCRIPTEN_LINK_FLAGS}")
set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} ${EMSCRIPTEN_LINK_FLAGS}")
unset(EMSCRIPTEN_C_FLAGS)
unset(EMSCRIPTEN_LINK_FLAGS)
endif()
# awesome
if (FREEBSD OR DRAGONFLYBSD)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/local/lib")
endif()
+19 -54
View File
@@ -35,37 +35,26 @@ if (NOT YUZU_USE_BUNDLED_FFMPEG)
elseif (NOT (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES CMAKE_SYSTEM_PROCESSOR elseif (NOT (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES CMAKE_SYSTEM_PROCESSOR
AND CMAKE_HOST_SYSTEM_NAME MATCHES CMAKE_SYSTEM_NAME)) AND CMAKE_HOST_SYSTEM_NAME MATCHES CMAKE_SYSTEM_NAME))
string(TOLOWER "${CMAKE_SYSTEM_NAME}" FFmpeg_SYSTEM_NAME) string(TOLOWER "${CMAKE_SYSTEM_NAME}" FFmpeg_SYSTEM_NAME)
# All of these platforms are supported by ffmpeg as native build OSes if (FFmpeg_SYSTEM_NAME STREQUAL "openorbis" OR FFmpeg_SYSTEM_NAME STREQUAL "managarm")
# anything else (like Redox or Managarm or PS4) is NOT natively supported
# hence, assume the "unix like" is just "none" for the sake of OUR sanity.
# If YOUR OS/platform has actual native support:
# 1. fucking congrats
# 2. feel free to add it on the condition below
if (NOT (NETBSD OR SOLARIS OR FREEBSD OR OPENBSD OR DRAGONFLYBSD OR HAIKU
OR LINUX OR MSYS OR WIN32 OR ANDROID OR APPLE))
set(FFmpeg_SYSTEM_NAME "none") set(FFmpeg_SYSTEM_NAME "none")
endif() endif()
# TODO: Can we really do better? Auto-detection? Something clever? # TODO: Can we really do better? Auto-detection? Something clever?
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS list(APPEND FFmpeg_CROSS_COMPILE_FLAGS
--enable-cross-compile --enable-cross-compile
--arch="${CMAKE_SYSTEM_PROCESSOR}" --arch="${CMAKE_SYSTEM_PROCESSOR}"
--target-os="${FFmpeg_SYSTEM_NAME}") --target-os="${FFmpeg_SYSTEM_NAME}"
# Emscripten doesn't use CMAKE_SYSROOT I'm afraid, but it does well use EMSCRIPTEN_SYSROOT --sysroot="${CMAKE_SYSROOT}"
if (EMSCRIPTEN) )
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --sysroot="${EMSCRIPTEN_SYSROOT}")
else()
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --sysroot="${CMAKE_SYSROOT}")
endif()
if (DEFINED FFmpeg_CROSS_PREFIX) if (DEFINED FFmpeg_CROSS_PREFIX)
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --cross-prefix="${FFmpeg_CROSS_PREFIX}") list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --cross-prefix="${FFmpeg_CROSS_PREFIX}")
else() else()
message(WARNING "Please set FFmpeg_CROSS_PREFIX to your cross toolchain prefix, for example: ${CMAKE_STAGING_PREFIX}/bin/${CMAKE_SYSTEM_PROCESSOR}-${CMAKE_SYSTEM_NAME}") message(WARNING "Please set FFmpeg_CROSS_PREFIX to your cross toolchain prefix, for example: \${CMAKE_STAGING_PREFIX}/bin/${CMAKE_SYSTEM_PROCESSOR}-${CMAKE_SYSTEM_NAME}-")
endif() endif()
set(FFmpeg_IS_CROSS_COMPILING TRUE) set(FFmpeg_IS_CROSS_COMPILING TRUE)
endif() endif()
endif() endif()
if (OPENORBIS OR MANAGARM OR EMSCRIPTEN) if (OPENORBIS OR MANAGARM)
# Doesn't support VA-API, don't go thru the embarrassment of trying to enable it # Doesn't support VA-API, don't go thru the embarrassment of trying to enable it
list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vaapi) list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vaapi)
elseif (ANDROID) elseif (ANDROID)
@@ -173,7 +162,8 @@ if (OPENORBIS)
-lSceUserService -lSceUserService
-lSceSysmodule -lSceSysmodule
-lSceNet -lSceNet
-lSceLibcInternal) -lSceLibcInternal
)
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS list(APPEND FFmpeg_CROSS_COMPILE_FLAGS
--disable-pthreads --disable-pthreads
--extra-cflags=${CMAKE_SYSROOT}/usr/include --extra-cflags=${CMAKE_SYSROOT}/usr/include
@@ -184,18 +174,8 @@ elseif (MANAGARM)
# Required for proper stuff # Required for proper stuff
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS list(APPEND FFmpeg_CROSS_COMPILE_FLAGS
--disable-pthreads --disable-pthreads
--extra-libs="${FFmpeg_CROSS_COMPILE_LIBS}") --extra-libs="${FFmpeg_CROSS_COMPILE_LIBS}"
endif() )
# Usually used by Emscripten
if (DEFINED CMAKE_AR)
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --ar=${CMAKE_AR})
endif()
if (DEFINED CMAKE_NM)
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --nm=${CMAKE_NM})
endif()
if (DEFINED CMAKE_RANLIB)
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --ranlib=${CMAKE_RANLIB})
endif() endif()
if (YUZU_USE_BUNDLED_FFMPEG) if (YUZU_USE_BUNDLED_FFMPEG)
@@ -225,7 +205,6 @@ else()
# Build FFmpeg from externals # Build FFmpeg from externals
message(STATUS "Using FFmpeg from externals") message(STATUS "Using FFmpeg from externals")
set(FFmpeg_LD ${CMAKE_LINKER})
if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86_64|amd64)") if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86_64|amd64)")
# FFmpeg has source that requires one of nasm or yasm to assemble it. # FFmpeg has source that requires one of nasm or yasm to assemble it.
# REQUIRED throws an error if not found here during configuration rather than during compilation. # REQUIRED throws an error if not found here during configuration rather than during compilation.
@@ -234,12 +213,6 @@ else()
message(FATAL_ERROR "One of either `nasm` or `yasm` not found but is required.") message(FATAL_ERROR "One of either `nasm` or `yasm` not found but is required.")
endif() endif()
endif() endif()
if (CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
# TODO: this is 100% redundant but we need it because CMAKE_LINKER can resolve to ld.lld
# which is NOT compatible
find_program(EMCC NAMES emcc REQUIRED)
set(FFmpeg_LD ${EMCC})
endif()
find_program(AUTOCONF autoconf) find_program(AUTOCONF autoconf)
if ("${AUTOCONF}" STREQUAL "AUTOCONF-NOTFOUND") if ("${AUTOCONF}" STREQUAL "AUTOCONF-NOTFOUND")
@@ -273,13 +246,7 @@ else()
CACHE PATH "Paths to FFmpeg libraries" FORCE) CACHE PATH "Paths to FFmpeg libraries" FORCE)
endforeach() endforeach()
# Some SDKs (especially wasm) require us, actually, WANT us to use their "emconfigure" find_program(BASH_PROGRAM bash REQUIRED)
# otherwise they will cry a billion tears
if (EMSCRIPTEN)
find_program(FFmpeg_CONFIGURE_WRAPPER emconfigure REQUIRED)
else()
find_program(FFmpeg_CONFIGURE_WRAPPER bash REQUIRED)
endif()
# `configure` parameters builds only exactly what yuzu needs from FFmpeg # `configure` parameters builds only exactly what yuzu needs from FFmpeg
# `--disable-vdpau` is needed to avoid linking issues # `--disable-vdpau` is needed to avoid linking issues
@@ -289,7 +256,7 @@ else()
OUTPUT OUTPUT
${FFmpeg_MAKEFILE} ${FFmpeg_MAKEFILE}
COMMAND COMMAND
${FFmpeg_CONFIGURE_WRAPPER} ${FFmpeg_PREFIX}/configure ${BASH_PROGRAM} ${FFmpeg_PREFIX}/configure
--disable-avdevice --disable-avdevice
--disable-avformat --disable-avformat
--disable-doc --disable-doc
@@ -298,10 +265,6 @@ else()
--disable-ffprobe --disable-ffprobe
--disable-network --disable-network
--disable-swresample --disable-swresample
--disable-autodetect
--disable-runtime-cpudetect
--disable-debug
--disable-programs
--enable-decoder=h264 --enable-decoder=h264
--enable-decoder=vp8 --enable-decoder=vp8
--enable-decoder=vp9 --enable-decoder=vp9
@@ -309,7 +272,7 @@ else()
--enable-pic --enable-pic
--cc=${FFmpeg_CC} --cc=${FFmpeg_CC}
--cxx=${FFmpeg_CXX} --cxx=${FFmpeg_CXX}
--ld=${FFmpeg_LD} --ld=${CMAKE_LINKER}
--extra-cflags=${CMAKE_C_FLAGS} --extra-cflags=${CMAKE_C_FLAGS}
--extra-cxxflags=${CMAKE_CXX_FLAGS} --extra-cxxflags=${CMAKE_CXX_FLAGS}
--extra-ldflags=${CMAKE_C_LINK_FLAGS} --extra-ldflags=${CMAKE_C_LINK_FLAGS}
@@ -325,7 +288,11 @@ else()
# Workaround for Ubuntu 18.04's older version of make not being able to call make as a child # Workaround for Ubuntu 18.04's older version of make not being able to call make as a child
# with context of the jobserver. Also helps ninja users. # with context of the jobserver. Also helps ninja users.
cmake_host_system_information(RESULT SYSTEM_THREADS QUERY NUMBER_OF_LOGICAL_CORES) execute_process(
COMMAND
nproc
OUTPUT_VARIABLE
SYSTEM_THREADS)
set(FFmpeg_BUILD_LIBRARIES ${FFmpeg_LIBRARIES}) set(FFmpeg_BUILD_LIBRARIES ${FFmpeg_LIBRARIES})
@@ -333,8 +300,6 @@ else()
if (LINUX OR ANDROID OR APPLE OR WIN32 OR FREEBSD) if (LINUX OR ANDROID OR APPLE OR WIN32 OR FREEBSD)
set(FFmpeg_MAKE_ARGS -j${SYSTEM_THREADS}) set(FFmpeg_MAKE_ARGS -j${SYSTEM_THREADS})
else() else()
# No GNU make implies that this system may be highly non-GNU
find_program(MAKE make required)
set(FFmpeg_MAKE_ARGS "") set(FFmpeg_MAKE_ARGS "")
endif() endif()
@@ -342,7 +307,7 @@ else()
OUTPUT OUTPUT
${FFmpeg_BUILD_LIBRARIES} ${FFmpeg_BUILD_LIBRARIES}
COMMAND COMMAND
${MAKE} ${FFmpeg_MAKE_ARGS} gmake ${FFmpeg_MAKE_ARGS}
WORKING_DIRECTORY WORKING_DIRECTORY
${FFmpeg_BUILD_DIR} ${FFmpeg_BUILD_DIR}
) )
-2
View File
@@ -14,9 +14,7 @@
namespace Tz { namespace Tz {
namespace { namespace {
#ifndef EINVAL
#define EINVAL 22 #define EINVAL 22
#endif
static Rule gmtmem{}; static Rule gmtmem{};
static Rule* const gmtptr = &gmtmem; static Rule* const gmtptr = &gmtmem;
+1
View File
@@ -11,6 +11,7 @@
#include <limits> #include <limits>
#include <span> #include <span>
#include <array> #include <array>
#include <algorithm>
#include <time.h> #include <time.h>
namespace Tz { namespace Tz {
+2 -5
View File
@@ -177,6 +177,7 @@ else()
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-invalid-offsetof> $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-invalid-offsetof>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-unused-parameter> $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-unused-parameter>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-missing-field-initializers>) $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-missing-field-initializers>)
if (CXX_CLANG OR CXX_ICC OR CXX_APPLE) # Clang, AppleClang, or Intel C++ if (CXX_CLANG OR CXX_ICC OR CXX_APPLE) # Clang, AppleClang, or Intel C++
if (NOT MSVC) if (NOT MSVC)
add_compile_options( add_compile_options(
@@ -190,11 +191,7 @@ else()
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-nullability-completeness>) $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-nullability-completeness>)
endif() endif()
if (ARCHITECTURE_wasm) if (ARCHITECTURE_x86_64)
# we are evil but fmt is even more evil
add_compile_options(
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-shorten-64-to-32>)
elseif (ARCHITECTURE_x86_64)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-mcx16>) add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-mcx16>)
if (LINUX OR FREEBSD) if (LINUX OR FREEBSD)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-mtls-dialect=gnu2>) add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-mtls-dialect=gnu2>)
+7
View File
@@ -272,6 +272,13 @@ android {
resValue("string", "app_name_suffixed", "$currentName$suffix") resValue("string", "app_name_suffixed", "$currentName$suffix")
resValue("string", "app_name", "Eden$suffix") resValue("string", "app_name", "Eden$suffix")
} }
sourceSets {
named("main") {
java.srcDir("${edenDir}/externals/generated/sdl/java")
kotlin.srcDir("${edenDir}/externals/generated/sdl/java")
}
}
} }
idea { idea {
+3
View File
@@ -9,6 +9,9 @@
-keep class org.ini4j.spi.IniBuilder -keep class org.ini4j.spi.IniBuilder
-keep class org.ini4j.spi.IniFormatter -keep class org.ini4j.spi.IniFormatter
-keep class org.libsdl.app.** { *; }
-keepclassmembers class org.libsdl.app.** { *; }
# Suppress warnings for R8 # Suppress warnings for R8
-dontwarn org.bouncycastle.jsse.BCSSLParameters -dontwarn org.bouncycastle.jsse.BCSSLParameters
-dontwarn org.bouncycastle.jsse.BCSSLSocket -dontwarn org.bouncycastle.jsse.BCSSLSocket
@@ -16,6 +16,7 @@ import android.widget.TextView
import androidx.annotation.Keep import androidx.annotation.Keep
import androidx.core.net.toUri import androidx.core.net.toUri
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.libsdl.app.SDL
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import org.yuzu.yuzu_emu.activities.EmulationActivity import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.fragments.CoreErrorDialogFragment import org.yuzu.yuzu_emu.fragments.CoreErrorDialogFragment
@@ -52,6 +53,8 @@ object NativeLibrary {
init { init {
try { try {
System.loadLibrary("yuzu-android") System.loadLibrary("yuzu-android")
SDL.setupJNI()
initJvm()
} catch (ex: UnsatisfiedLinkError) { } catch (ex: UnsatisfiedLinkError) {
error("[NativeLibrary] $ex") error("[NativeLibrary] $ex")
} }
@@ -367,6 +370,7 @@ object NativeLibrary {
NetPlayManager.clearChat() NetPlayManager.clearChat()
} }
external fun initJvm()
external fun initMultiplayer() external fun initMultiplayer()
@Keep @Keep
+1 -1
View File
@@ -22,7 +22,7 @@ add_library(yuzu-android SHARED
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
target_link_libraries(yuzu-android PRIVATE audio_core common core input_common frontend_common video_core) target_link_libraries(yuzu-android PRIVATE audio_core common core input_common frontend_common video_core)
target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad jnigraphics log) target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad jnigraphics log GLESv2)
if (ARCHITECTURE_arm64) if (ARCHITECTURE_arm64)
target_link_libraries(yuzu-android PRIVATE adrenotools) target_link_libraries(yuzu-android PRIVATE adrenotools)
endif() endif()
+9
View File
@@ -1730,6 +1730,15 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_loadAmiibo(JNIEnv* env, jobject jobj,
return static_cast<jint>(info); return static_cast<jint>(info);
} }
extern "C" JNIEXPORT void JNICALL
Java_org_yuzu_yuzu_1emu_NativeLibrary_initJvm(JNIEnv *env, jclass clazz) {
JavaVM *vm;
if (env->GetJavaVM(&vm) != JNI_OK)
return;
Common::Android::Initialize(vm, env);
}
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_org_yuzu_yuzu_1emu_NativeLibrary_initMultiplayer( Java_org_yuzu_yuzu_1emu_NativeLibrary_initMultiplayer(
JNIEnv* env, [[maybe_unused]] jobject obj) { JNIEnv* env, [[maybe_unused]] jobject obj) {
@@ -800,7 +800,6 @@
<string name="theme_mode_dark">تاریک</string> <string name="theme_mode_dark">تاریک</string>
<!-- Audio output engines --> <!-- Audio output engines -->
<string name="oboe">oboe</string>
<string name="cubeb">cubeb</string> <string name="cubeb">cubeb</string>
<!-- Anisotropic filtering options --> <!-- Anisotropic filtering options -->
@@ -465,13 +465,13 @@
<string-array name="outputEngineEntries"> <string-array name="outputEngineEntries">
<item>@string/auto</item> <item>@string/auto</item>
<item>@string/oboe</item> <item>@string/sdl3</item>
<item>@string/cubeb</item> <item>@string/cubeb</item>
<item>@string/string_null</item> <item>@string/string_null</item>
</string-array> </string-array>
<integer-array name="outputEngineValues"> <integer-array name="outputEngineValues">
<item>0</item> <item>0</item>
<item>4</item> <item>2</item>
<item>1</item> <item>1</item>
<item>3</item> <item>3</item>
</integer-array> </integer-array>
@@ -1240,7 +1240,7 @@
<string name="theme_mode_dark">Dark</string> <string name="theme_mode_dark">Dark</string>
<!-- Audio output engines --> <!-- Audio output engines -->
<string name="oboe" translatable="false">oboe</string> <string name="sdl3" translatable="false">SDL3</string>
<string name="cubeb" translatable="false">cubeb</string> <string name="cubeb" translatable="false">cubeb</string>
<!-- Anisotropic filtering options --> <!-- Anisotropic filtering options -->
+5 -14
View File
@@ -238,20 +238,11 @@ if (ENABLE_CUBEB)
target_compile_definitions(audio_core PRIVATE HAVE_CUBEB=1) target_compile_definitions(audio_core PRIVATE HAVE_CUBEB=1)
endif() endif()
if(ANDROID) target_sources(audio_core PRIVATE
target_sources(audio_core PRIVATE sink/sdl3_sink.cpp
sink/oboe_sink.cpp sink/sdl3_sink.h)
sink/oboe_sink.h)
target_link_libraries(audio_core PRIVATE oboe) target_link_libraries(audio_core PRIVATE SDL3::SDL3)
target_compile_definitions(audio_core PUBLIC HAVE_OBOE) target_compile_definitions(audio_core PRIVATE HAVE_SDL3)
else()
target_sources(audio_core PRIVATE
sink/sdl3_sink.cpp
sink/sdl3_sink.h)
target_link_libraries(audio_core PRIVATE SDL3::SDL3)
target_compile_definitions(audio_core PRIVATE HAVE_SDL3)
endif()
create_target_directory_groups(audio_core) create_target_directory_groups(audio_core)
-230
View File
@@ -1,230 +0,0 @@
// 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
#include <span>
#include <vector>
#include <oboe/Oboe.h>
#include "audio_core/common/common.h"
#include "audio_core/sink/oboe_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 {
class OboeSinkStream final : public SinkStream,
public oboe::AudioStreamDataCallback,
public oboe::AudioStreamErrorCallback {
public:
explicit OboeSinkStream(Core::System& system_, StreamType type_, const std::string& name_,
u32 system_channels_)
: SinkStream(system_, type_) {
name = name_;
system_channels = system_channels_;
this->OpenStream();
}
~OboeSinkStream() override {
LOG_INFO(Audio_Sink, "Destroyed Oboe stream");
}
void Finalize() override {
this->Stop();
m_stream.reset();
}
void Start(bool resume = false) override {
if (!m_stream || !paused) {
return;
}
paused = false;
if (m_stream->start() != oboe::Result::OK) {
LOG_CRITICAL(Audio_Sink, "Error starting Oboe stream");
}
}
void Stop() override {
if (!m_stream || paused) {
return;
}
this->SignalPause();
if (m_stream->stop() != oboe::Result::OK) {
LOG_CRITICAL(Audio_Sink, "Error stopping Oboe stream");
}
}
public:
static s32 QueryChannelCount(oboe::Direction direction) {
std::shared_ptr<oboe::AudioStream> temp_stream;
oboe::AudioStreamBuilder builder;
const auto result = ConfigureBuilder(builder, direction)->openStream(temp_stream);
if (result == oboe::Result::OK) {
return temp_stream->getChannelCount() >= 6 ? 6 : 2;
}
LOG_ERROR(Audio_Sink, "Failed to open {} stream. Using default channel count 2",
direction == oboe::Direction::Output ? "output" : "input");
return 2;
}
protected:
oboe::DataCallbackResult onAudioReady(oboe::AudioStream*, void* audio_data,
s32 num_buffer_frames) override {
const size_t num_channels = this->GetDeviceChannels();
const size_t frame_size = num_channels;
const size_t num_frames = static_cast<size_t>(num_buffer_frames);
if (type == StreamType::In) {
std::span<const s16> input_buffer{reinterpret_cast<const s16*>(audio_data),
num_frames * frame_size};
this->ProcessAudioIn(input_buffer, num_frames);
} else {
std::span<s16> output_buffer{reinterpret_cast<s16*>(audio_data),
num_frames * frame_size};
this->ProcessAudioOutAndRender(output_buffer, num_frames);
}
return oboe::DataCallbackResult::Continue;
}
void onErrorAfterClose(oboe::AudioStream*, oboe::Result) override {
LOG_INFO(Audio_Sink, "Audio stream closed, reinitializing");
if (this->OpenStream()) {
m_stream->start();
}
}
private:
static oboe::AudioStreamBuilder* ConfigureBuilder(oboe::AudioStreamBuilder& builder,
oboe::Direction direction) {
// TODO: investigate callback delay issues when using AAudio
return builder.setPerformanceMode(oboe::PerformanceMode::LowLatency)
->setAudioApi(oboe::AudioApi::OpenSLES)
->setDirection(direction)
->setSampleRate(TargetSampleRate)
->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::High)
->setFormat(oboe::AudioFormat::I16)
->setFormatConversionAllowed(true)
->setUsage(oboe::Usage::Game)
->setBufferCapacityInFrames(TargetSampleCount * 2);
}
bool OpenStream() {
const auto direction = [&]() {
switch (type) {
case StreamType::In:
return oboe::Direction::Input;
case StreamType::Out:
case StreamType::Render:
return oboe::Direction::Output;
default:
ASSERT(false);
return oboe::Direction::Output;
}
}();
const auto expected_channels = QueryChannelCount(direction);
const auto expected_mask = [&]() {
switch (expected_channels) {
case 1:
return oboe::ChannelMask::Mono;
case 2:
return oboe::ChannelMask::Stereo;
case 6:
return oboe::ChannelMask::CM5Point1;
default:
ASSERT(false);
return oboe::ChannelMask::Unspecified;
}
}();
oboe::AudioStreamBuilder builder;
const auto result = ConfigureBuilder(builder, direction)
->setChannelCount(expected_channels)
->setChannelMask(expected_mask)
->setChannelConversionAllowed(true)
->setDataCallback(this)
->setErrorCallback(this)
->openStream(m_stream);
ASSERT(result == oboe::Result::OK);
return result == oboe::Result::OK && this->SetStreamProperties();
}
bool SetStreamProperties() {
ASSERT(m_stream);
m_stream->setBufferSizeInFrames(TargetSampleCount * 2);
device_channels = m_stream->getChannelCount();
const auto sample_rate = m_stream->getSampleRate();
const auto buffer_capacity = m_stream->getBufferCapacityInFrames();
const auto stream_backend =
m_stream->getAudioApi() == oboe::AudioApi::AAudio ? "AAudio" : "OpenSLES";
LOG_INFO(Audio_Sink, "Opened Oboe {} stream with {} channels sample rate {} capacity {}",
stream_backend, device_channels, sample_rate, buffer_capacity);
return true;
}
std::shared_ptr<oboe::AudioStream> m_stream{};
};
OboeSink::OboeSink() {
// TODO: This is not generally knowable
// The channel count is distinct based on direction and can change
device_channels = OboeSinkStream::QueryChannelCount(oboe::Direction::Output);
}
OboeSink::~OboeSink() = default;
SinkStream* OboeSink::AcquireSinkStream(Core::System& system, u32 system_channels,
const std::string& name, StreamType type) {
SinkStreamPtr& stream = sink_streams.emplace_back(
std::make_unique<OboeSinkStream>(system, type, name, system_channels));
return stream.get();
}
void OboeSink::CloseStream(SinkStream* to_remove) {
sink_streams.remove_if([&](auto& stream) { return stream.get() == to_remove; });
}
void OboeSink::CloseStreams() {
sink_streams.clear();
}
f32 OboeSink::GetDeviceVolume() const {
if (sink_streams.empty()) {
return 1.0f;
}
return sink_streams.front()->GetDeviceVolume();
}
void OboeSink::SetDeviceVolume(f32 volume) {
for (auto& stream : sink_streams) {
stream->SetDeviceVolume(volume);
}
}
void OboeSink::SetSystemVolume(f32 volume) {
for (auto& stream : sink_streams) {
stream->SetSystemVolume(volume);
}
}
} // namespace AudioCore::Sink
-75
View File
@@ -1,75 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <list>
#include <string>
#include "audio_core/sink/sink.h"
namespace Core {
class System;
}
namespace AudioCore::Sink {
class SinkStream;
class OboeSink final : public Sink {
public:
explicit OboeSink();
~OboeSink() override;
/**
* Create a new sink stream.
*
* @param system - Core system.
* @param system_channels - Number of channels the audio system expects.
* May differ from the device's channel count.
* @param name - Name of this stream.
* @param type - Type of this stream, render/in/out.
*
* @return A pointer to the created SinkStream
*/
SinkStream* AcquireSinkStream(Core::System& system, u32 system_channels,
const std::string& name, StreamType type) override;
/**
* Close a given stream.
*
* @param stream - The stream to close.
*/
void CloseStream(SinkStream* stream) override;
/**
* Close all streams.
*/
void CloseStreams() override;
/**
* Get the device volume. Set from calls to the IAudioDevice service.
*
* @return Volume of the device.
*/
f32 GetDeviceVolume() const override;
/**
* Set the device volume. Set from calls to the IAudioDevice service.
*
* @param volume - New volume of the device.
*/
void SetDeviceVolume(f32 volume) override;
/**
* Set the system volume. Comes from the audio system using this stream.
*
* @param volume - New volume of the system.
*/
void SetSystemVolume(f32 volume) override;
private:
/// List of streams managed by this sink
std::list<SinkStreamPtr> sink_streams{};
};
} // namespace AudioCore::Sink
-19
View File
@@ -10,9 +10,6 @@
#include <vector> #include <vector>
#include "audio_core/sink/sink_details.h" #include "audio_core/sink/sink_details.h"
#ifdef HAVE_OBOE
#include "audio_core/sink/oboe_sink.h"
#endif
#ifdef HAVE_CUBEB #ifdef HAVE_CUBEB
#include "audio_core/sink/cubeb_sink.h" #include "audio_core/sink/cubeb_sink.h"
#endif #endif
@@ -43,24 +40,8 @@ struct SinkDetails {
/// SuitableFn is_suitable; // REVERTED FOR LatencyFn latency ABOVE - DIABLO 3 FIX /// SuitableFn is_suitable; // REVERTED FOR LatencyFn latency ABOVE - DIABLO 3 FIX
}; };
// NOTE TO PROBABLY FIX LATER FOR ANDROID - the return value "0u" for the first HAVE_OBOE
// section below was just copied from the null section so there's a somewhat valid value
// being returned, since the previous "true" value probably isn't compatible with the
// previous EA-3833 code. (HAVE_OBOE was introduced in a later release.) Eventually need
// to change "0u" for something else directly from the oboe_sink.cpp functions.
// sink_details is ordered in terms of desirability, with the best choice at the top. // sink_details is ordered in terms of desirability, with the best choice at the top.
constexpr SinkDetails sink_details[] = { constexpr SinkDetails sink_details[] = {
#ifdef HAVE_OBOE
SinkDetails{
Settings::AudioEngine::Oboe,
[](std::string_view device_id) -> std::unique_ptr<Sink> {
return std::make_unique<OboeSink>();
},
[](bool capture) { return std::vector<std::string>{"Default"}; },
[]() { return 0u; },
},
#endif
#ifdef HAVE_CUBEB #ifdef HAVE_CUBEB
SinkDetails{ SinkDetails{
Settings::AudioEngine::Cubeb, Settings::AudioEngine::Cubeb,
+1 -6
View File
@@ -89,7 +89,6 @@ add_library(
param_package.h param_package.h
parent_of_member.h parent_of_member.h
point.h point.h
quaternion.h
range_map.h range_map.h
range_mutex.h range_mutex.h
range_sets.h range_sets.h
@@ -236,11 +235,7 @@ else()
target_link_libraries(common PUBLIC Boost::headers) target_link_libraries(common PUBLIC Boost::headers)
endif() endif()
target_link_libraries(common PRIVATE OpenSSL::SSL) target_link_libraries(common PRIVATE OpenSSL::SSL)
target_link_libraries(common PUBLIC Boost::filesystem httplib::httplib nlohmann_json::nlohmann_json) target_link_libraries(common PUBLIC Boost::filesystem Boost::context httplib::httplib nlohmann_json::nlohmann_json)
if (NOT EMSCRIPTEN)
# Emscripten is: "bring your own implementation", boost doesn't add upstream support sadly
target_link_libraries(common PRIVATE Boost::context)
endif()
if (lz4_ADDED) if (lz4_ADDED)
target_include_directories(common PRIVATE ${lz4_SOURCE_DIR}/lib) target_include_directories(common PRIVATE ${lz4_SOURCE_DIR}/lib)
+4 -4
View File
@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "android_common.h" #include "android_common.h"
@@ -51,7 +51,7 @@ jstring ToJString(JNIEnv* env, std::u16string_view str) {
} }
double GetJDouble(JNIEnv* env, jobject jdouble) { double GetJDouble(JNIEnv* env, jobject jdouble) {
return env->GetDoubleField(jdouble, GetDoubleValueField()); return env->CallDoubleMethod(jdouble, GetDoubleValueMethod());
} }
jobject ToJDouble(JNIEnv* env, double value) { jobject ToJDouble(JNIEnv* env, double value) {
@@ -59,7 +59,7 @@ jobject ToJDouble(JNIEnv* env, double value) {
} }
s32 GetJInteger(JNIEnv* env, jobject jinteger) { s32 GetJInteger(JNIEnv* env, jobject jinteger) {
return env->GetIntField(jinteger, GetIntegerValueField()); return env->CallIntMethod(jinteger, GetIntegerValueMethod());
} }
jobject ToJInteger(JNIEnv* env, s32 value) { jobject ToJInteger(JNIEnv* env, s32 value) {
@@ -67,7 +67,7 @@ jobject ToJInteger(JNIEnv* env, s32 value) {
} }
bool GetJBoolean(JNIEnv* env, jobject jboolean) { bool GetJBoolean(JNIEnv* env, jobject jboolean) {
return env->GetBooleanField(jboolean, GetBooleanValueField()); return env->CallBooleanMethod(jboolean, GetBooleanValueMethod());
} }
jobject ToJBoolean(JNIEnv* env, bool value) { jobject ToJBoolean(JNIEnv* env, bool value) {
+188 -192
View File
@@ -63,15 +63,15 @@ static jfieldID s_patch_title_id_field;
static jclass s_double_class; static jclass s_double_class;
static jmethodID s_double_constructor; static jmethodID s_double_constructor;
static jfieldID s_double_value_field; static jmethodID s_double_value_method;
static jclass s_integer_class; static jclass s_integer_class;
static jmethodID s_integer_constructor; static jmethodID s_integer_constructor;
static jfieldID s_integer_value_field; static jmethodID s_integer_value_method;
static jclass s_boolean_class; static jclass s_boolean_class;
static jmethodID s_boolean_constructor; static jmethodID s_boolean_constructor;
static jfieldID s_boolean_value_field; static jmethodID s_boolean_value_method;
static jclass s_player_input_class; static jclass s_player_input_class;
static jmethodID s_player_input_constructor; static jmethodID s_player_input_constructor;
@@ -103,7 +103,6 @@ static jmethodID s_clear_chat;
static constexpr jint JNI_VERSION = JNI_VERSION_1_6; static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
namespace Common::Android { namespace Common::Android {
JNIEnv *GetEnvForThread() { JNIEnv *GetEnvForThread() {
thread_local static struct OwnedEnv { thread_local static struct OwnedEnv {
OwnedEnv() { OwnedEnv() {
@@ -299,8 +298,8 @@ namespace Common::Android {
return s_double_constructor; return s_double_constructor;
} }
jfieldID GetDoubleValueField() { jmethodID GetDoubleValueMethod() {
return s_double_value_field; return s_double_value_method;
} }
jclass GetIntegerClass() { jclass GetIntegerClass() {
@@ -311,8 +310,8 @@ namespace Common::Android {
return s_integer_constructor; return s_integer_constructor;
} }
jfieldID GetIntegerValueField() { jmethodID GetIntegerValueMethod() {
return s_integer_value_field; return s_integer_value_method;
} }
jclass GetBooleanClass() { jclass GetBooleanClass() {
@@ -323,8 +322,8 @@ namespace Common::Android {
return s_boolean_constructor; return s_boolean_constructor;
} }
jfieldID GetBooleanValueField() { jmethodID GetBooleanValueMethod() {
return s_boolean_value_field; return s_boolean_value_method;
} }
jclass GetPlayerInputClass() { jclass GetPlayerInputClass() {
@@ -429,188 +428,6 @@ namespace Common::Android {
jint InitFFmpegOnLoad(JavaVM *vm); jint InitFFmpegOnLoad(JavaVM *vm);
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
s_java_vm = vm;
InitFFmpegOnLoad(vm);
JNIEnv *env;
if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION) != JNI_OK)
return JNI_ERR;
// Initialize Java classes
const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary");
s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
s_disk_cache_progress_class = reinterpret_cast<jclass>(env->NewGlobalRef(
env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress")));
s_load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
"org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage")));
const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir");
s_game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class));
s_game_dir_constructor = env->GetMethodID(game_dir_class, "<init>",
"(Ljava/lang/String;Z)V");
env->DeleteLocalRef(game_dir_class);
// Initialize methods
s_exit_emulation_activity =
env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V");
s_disk_cache_load_progress =
env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V");
s_copy_to_storage = env->GetStaticMethodID(s_native_library_class, "copyFileToStorage",
"(Ljava/lang/String;Ljava/lang/String;)Z");
s_file_exists = env->GetStaticMethodID(s_native_library_class, "exists",
"(Ljava/lang/String;)Z");
s_file_extension = env->GetStaticMethodID(s_native_library_class, "getFileExtension",
"(Ljava/lang/String;)Ljava/lang/String;");
s_on_emulation_started =
env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V");
s_on_emulation_stopped =
env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V");
s_on_program_changed =
env->GetStaticMethodID(s_native_library_class, "onProgramChanged", "(I)V");
const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game");
s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));
s_game_constructor = env->GetMethodID(game_class, "<init>",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
"String;Ljava/lang/String;Ljava/lang/String;Z)V");
s_game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;");
s_game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;");
s_game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;");
s_game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;");
s_game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;");
s_game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z");
env->DeleteLocalRef(game_class);
const jclass string_class = env->FindClass("java/lang/String");
s_string_class = reinterpret_cast<jclass>(env->NewGlobalRef(string_class));
env->DeleteLocalRef(string_class);
const jclass pair_class = env->FindClass("kotlin/Pair");
s_pair_class = reinterpret_cast<jclass>(env->NewGlobalRef(pair_class));
s_pair_constructor =
env->GetMethodID(pair_class, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V");
s_pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;");
s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
env->DeleteLocalRef(pair_class);
const jclass overlay_control_data_class =
env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData");
s_overlay_control_data_class =
reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
s_overlay_control_data_constructor =
env->GetMethodID(overlay_control_data_class, "<init>",
"(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;F)V");
s_overlay_control_data_id_field =
env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
s_overlay_control_data_enabled_field =
env->GetFieldID(overlay_control_data_class, "enabled", "Z");
s_overlay_control_data_landscape_position_field =
env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;");
s_overlay_control_data_portrait_position_field =
env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
s_overlay_control_data_foldable_position_field =
env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
s_overlay_control_data_individual_scale_field =
env->GetFieldID(overlay_control_data_class, "individualScale", "F");
env->DeleteLocalRef(overlay_control_data_class);
const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch");
s_patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
s_patch_constructor = env->GetMethodID(
patch_class, "<init>",
"(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JI)V");
s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z");
s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;");
s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;");
s_patch_type_field = env->GetFieldID(patch_class, "type", "I");
s_patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;");
s_patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;");
env->DeleteLocalRef(patch_class);
const jclass double_class = env->FindClass("java/lang/Double");
s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
s_double_value_field = env->GetFieldID(double_class, "value", "D");
env->DeleteLocalRef(double_class);
const jclass int_class = env->FindClass("java/lang/Integer");
s_integer_class = reinterpret_cast<jclass>(env->NewGlobalRef(int_class));
s_integer_constructor = env->GetMethodID(int_class, "<init>", "(I)V");
s_integer_value_field = env->GetFieldID(int_class, "value", "I");
env->DeleteLocalRef(int_class);
const jclass boolean_class = env->FindClass("java/lang/Boolean");
s_boolean_class = reinterpret_cast<jclass>(env->NewGlobalRef(boolean_class));
s_boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V");
s_boolean_value_field = env->GetFieldID(boolean_class, "value", "Z");
env->DeleteLocalRef(boolean_class);
const jclass player_input_class =
env->FindClass("org/yuzu/yuzu_emu/features/input/model/PlayerInput");
s_player_input_class = reinterpret_cast<jclass>(env->NewGlobalRef(player_input_class));
s_player_input_constructor = env->GetMethodID(
player_input_class, "<init>",
"(Z[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;ZIJJJJLjava/lang/String;Z)V");
s_player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z");
s_player_input_buttons_field =
env->GetFieldID(player_input_class, "buttons", "[Ljava/lang/String;");
s_player_input_analogs_field =
env->GetFieldID(player_input_class, "analogs", "[Ljava/lang/String;");
s_player_input_motions_field =
env->GetFieldID(player_input_class, "motions", "[Ljava/lang/String;");
s_player_input_vibration_enabled_field =
env->GetFieldID(player_input_class, "vibrationEnabled", "Z");
s_player_input_vibration_strength_field =
env->GetFieldID(player_input_class, "vibrationStrength", "I");
s_player_input_body_color_left_field =
env->GetFieldID(player_input_class, "bodyColorLeft", "J");
s_player_input_body_color_right_field =
env->GetFieldID(player_input_class, "bodyColorRight", "J");
s_player_input_button_color_left_field =
env->GetFieldID(player_input_class, "buttonColorLeft", "J");
s_player_input_button_color_right_field =
env->GetFieldID(player_input_class, "buttonColorRight", "J");
s_player_input_profile_name_field =
env->GetFieldID(player_input_class, "profileName", "Ljava/lang/String;");
s_player_input_use_system_vibrator_field =
env->GetFieldID(player_input_class, "useSystemVibrator", "Z");
env->DeleteLocalRef(player_input_class);
const jclass yuzu_input_device_interface =
env->FindClass("org/yuzu/yuzu_emu/features/input/YuzuInputDevice");
s_yuzu_input_device_interface =
reinterpret_cast<jclass>(env->NewGlobalRef(yuzu_input_device_interface));
s_yuzu_input_device_get_name =
env->GetMethodID(yuzu_input_device_interface, "getName", "()Ljava/lang/String;");
s_yuzu_input_device_get_guid =
env->GetMethodID(yuzu_input_device_interface, "getGUID", "()Ljava/lang/String;");
s_yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort",
"()I");
s_yuzu_input_device_get_supports_vibration =
env->GetMethodID(yuzu_input_device_interface, "getSupportsVibration", "()Z");
s_yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate",
"(F)V");
s_yuzu_input_device_get_axes =
env->GetMethodID(yuzu_input_device_interface, "getAxes", "()[Ljava/lang/Integer;");
s_yuzu_input_device_has_keys =
env->GetMethodID(yuzu_input_device_interface, "hasKeys", "([I)[Z");
env->DeleteLocalRef(yuzu_input_device_interface);
s_add_netplay_message = env->GetStaticMethodID(s_native_library_class, "addNetPlayMessage",
"(ILjava/lang/String;)V");
s_clear_chat = env->GetStaticMethodID(s_native_library_class, "clearChat", "()V");
// Initialize Android Storage
Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
// Initialize applets
Common::Android::SoftwareKeyboard::InitJNI(env);
Common::Android::WebBrowser::InitJNI(env);
return JNI_VERSION;
}
void JNI_OnUnload(JavaVM *vm, void *reserved) { void JNI_OnUnload(JavaVM *vm, void *reserved) {
JNIEnv *env; JNIEnv *env;
if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION) != JNI_OK) { if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION) != JNI_OK) {
@@ -645,4 +462,183 @@ namespace Common::Android {
} }
#endif #endif
void Initialize(JavaVM* vm, JNIEnv *env) {
s_java_vm = vm;
InitFFmpegOnLoad(vm);
if (env->ExceptionCheck()) {
env->ExceptionClear();
}
// Initialize Java classes
const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary");
s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
s_disk_cache_progress_class = reinterpret_cast<jclass>(env->NewGlobalRef(
env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress")));
s_load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
"org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage")));
const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir");
s_game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class));
s_game_dir_constructor = env->GetMethodID(game_dir_class, "<init>",
"(Ljava/lang/String;Z)V");
env->DeleteLocalRef(game_dir_class);
// Initialize methods
s_exit_emulation_activity =
env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V");
s_disk_cache_load_progress =
env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V");
s_copy_to_storage = env->GetStaticMethodID(s_native_library_class, "copyFileToStorage",
"(Ljava/lang/String;Ljava/lang/String;)Z");
s_file_exists = env->GetStaticMethodID(s_native_library_class, "exists",
"(Ljava/lang/String;)Z");
s_file_extension = env->GetStaticMethodID(s_native_library_class, "getFileExtension",
"(Ljava/lang/String;)Ljava/lang/String;");
s_on_emulation_started =
env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V");
s_on_emulation_stopped =
env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V");
s_on_program_changed =
env->GetStaticMethodID(s_native_library_class, "onProgramChanged", "(I)V");
const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game");
s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));
s_game_constructor = env->GetMethodID(game_class, "<init>",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
"String;Ljava/lang/String;Ljava/lang/String;Z)V");
s_game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;");
s_game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;");
s_game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;");
s_game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;");
s_game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;");
s_game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z");
env->DeleteLocalRef(game_class);
const jclass string_class = env->FindClass("java/lang/String");
s_string_class = reinterpret_cast<jclass>(env->NewGlobalRef(string_class));
env->DeleteLocalRef(string_class);
const jclass pair_class = env->FindClass("kotlin/Pair");
s_pair_class = reinterpret_cast<jclass>(env->NewGlobalRef(pair_class));
s_pair_constructor =
env->GetMethodID(pair_class, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V");
s_pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;");
s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
env->DeleteLocalRef(pair_class);
const jclass overlay_control_data_class =
env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData");
s_overlay_control_data_class =
reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
s_overlay_control_data_constructor =
env->GetMethodID(overlay_control_data_class, "<init>",
"(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;F)V");
s_overlay_control_data_id_field =
env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
s_overlay_control_data_enabled_field =
env->GetFieldID(overlay_control_data_class, "enabled", "Z");
s_overlay_control_data_landscape_position_field =
env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;");
s_overlay_control_data_portrait_position_field =
env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
s_overlay_control_data_foldable_position_field =
env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
s_overlay_control_data_individual_scale_field =
env->GetFieldID(overlay_control_data_class, "individualScale", "F");
env->DeleteLocalRef(overlay_control_data_class);
const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch");
s_patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
s_patch_constructor = env->GetMethodID(
patch_class, "<init>",
"(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JI)V");
s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z");
s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;");
s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;");
s_patch_type_field = env->GetFieldID(patch_class, "type", "I");
s_patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;");
s_patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;");
env->DeleteLocalRef(patch_class);
const jclass double_class = env->FindClass("java/lang/Double");
s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
s_double_value_method = env->GetMethodID(double_class, "doubleValue", "()D");
env->DeleteLocalRef(double_class);
const jclass int_class = env->FindClass("java/lang/Integer");
s_integer_class = reinterpret_cast<jclass>(env->NewGlobalRef(int_class));
s_integer_constructor = env->GetMethodID(int_class, "<init>", "(I)V");
s_integer_value_method = env->GetMethodID(int_class, "intValue", "()I");
env->DeleteLocalRef(int_class);
const jclass boolean_class = env->FindClass("java/lang/Boolean");
s_boolean_class = reinterpret_cast<jclass>(env->NewGlobalRef(boolean_class));
s_boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V");
s_boolean_value_method = env->GetMethodID(boolean_class, "booleanValue", "()Z");
env->DeleteLocalRef(boolean_class);
const jclass player_input_class =
env->FindClass("org/yuzu/yuzu_emu/features/input/model/PlayerInput");
s_player_input_class = reinterpret_cast<jclass>(env->NewGlobalRef(player_input_class));
s_player_input_constructor = env->GetMethodID(
player_input_class, "<init>",
"(Z[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;ZIJJJJLjava/lang/String;Z)V");
s_player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z");
s_player_input_buttons_field =
env->GetFieldID(player_input_class, "buttons", "[Ljava/lang/String;");
s_player_input_analogs_field =
env->GetFieldID(player_input_class, "analogs", "[Ljava/lang/String;");
s_player_input_motions_field =
env->GetFieldID(player_input_class, "motions", "[Ljava/lang/String;");
s_player_input_vibration_enabled_field =
env->GetFieldID(player_input_class, "vibrationEnabled", "Z");
s_player_input_vibration_strength_field =
env->GetFieldID(player_input_class, "vibrationStrength", "I");
s_player_input_body_color_left_field =
env->GetFieldID(player_input_class, "bodyColorLeft", "J");
s_player_input_body_color_right_field =
env->GetFieldID(player_input_class, "bodyColorRight", "J");
s_player_input_button_color_left_field =
env->GetFieldID(player_input_class, "buttonColorLeft", "J");
s_player_input_button_color_right_field =
env->GetFieldID(player_input_class, "buttonColorRight", "J");
s_player_input_profile_name_field =
env->GetFieldID(player_input_class, "profileName", "Ljava/lang/String;");
s_player_input_use_system_vibrator_field =
env->GetFieldID(player_input_class, "useSystemVibrator", "Z");
env->DeleteLocalRef(player_input_class);
const jclass yuzu_input_device_interface =
env->FindClass("org/yuzu/yuzu_emu/features/input/YuzuInputDevice");
s_yuzu_input_device_interface =
reinterpret_cast<jclass>(env->NewGlobalRef(yuzu_input_device_interface));
s_yuzu_input_device_get_name =
env->GetMethodID(yuzu_input_device_interface, "getName", "()Ljava/lang/String;");
s_yuzu_input_device_get_guid =
env->GetMethodID(yuzu_input_device_interface, "getGUID", "()Ljava/lang/String;");
s_yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort",
"()I");
s_yuzu_input_device_get_supports_vibration =
env->GetMethodID(yuzu_input_device_interface, "getSupportsVibration", "()Z");
s_yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate",
"(F)V");
s_yuzu_input_device_get_axes =
env->GetMethodID(yuzu_input_device_interface, "getAxes", "()[Ljava/lang/Integer;");
s_yuzu_input_device_has_keys =
env->GetMethodID(yuzu_input_device_interface, "hasKeys", "([I)[Z");
env->DeleteLocalRef(yuzu_input_device_interface);
s_add_netplay_message = env->GetStaticMethodID(s_native_library_class, "addNetPlayMessage",
"(ILjava/lang/String;)V");
s_clear_chat = env->GetStaticMethodID(s_native_library_class, "clearChat", "()V");
// Initialize Android Storage
Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
// Initialize applets
Common::Android::SoftwareKeyboard::InitJNI(env);
Common::Android::WebBrowser::InitJNI(env);
}
} // namespace Common::Android } // namespace Common::Android
+4 -3
View File
@@ -11,6 +11,7 @@
namespace Common::Android { namespace Common::Android {
void Initialize(JavaVM* vm, JNIEnv *env);
JNIEnv* GetEnvForThread(); JNIEnv* GetEnvForThread();
/** /**
@@ -80,15 +81,15 @@ jfieldID GetPatchTitleIdField();
jclass GetDoubleClass(); jclass GetDoubleClass();
jmethodID GetDoubleConstructor(); jmethodID GetDoubleConstructor();
jfieldID GetDoubleValueField(); jmethodID GetDoubleValueMethod();
jclass GetIntegerClass(); jclass GetIntegerClass();
jmethodID GetIntegerConstructor(); jmethodID GetIntegerConstructor();
jfieldID GetIntegerValueField(); jmethodID GetIntegerValueMethod();
jclass GetBooleanClass(); jclass GetBooleanClass();
jmethodID GetBooleanConstructor(); jmethodID GetBooleanConstructor();
jfieldID GetBooleanValueField(); jmethodID GetBooleanValueMethod();
jclass GetPlayerInputClass(); jclass GetPlayerInputClass();
jmethodID GetPlayerInputConstructor(); jmethodID GetPlayerInputConstructor();
+1
View File
@@ -5,6 +5,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <string> #include <string>
#include <cstdlib>
#include <string_view> #include <string_view>
#ifdef _WIN32 #ifdef _WIN32
#include <llvm/Demangle/Demangle.h> #include <llvm/Demangle/Demangle.h>
+11 -84
View File
@@ -11,12 +11,7 @@
#include "common/fiber.h" #include "common/fiber.h"
#include "common/virtual_buffer.h" #include "common/virtual_buffer.h"
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#include <emscripten/fiber.h>
#else
#include <boost/context/detail/fcontext.hpp> #include <boost/context/detail/fcontext.hpp>
#endif
namespace Common { namespace Common {
@@ -27,103 +22,36 @@ constexpr size_t DEFAULT_STACK_SIZE = 512 * 4096;
#endif #endif
constexpr u32 CANARY_VALUE = 0xDEADBEEF; constexpr u32 CANARY_VALUE = 0xDEADBEEF;
#ifdef __EMSCRIPTEN__
struct Fiber::FiberImpl {
FiberImpl() {}
u32 canary_1 = CANARY_VALUE;
std::array<u8, DEFAULT_STACK_SIZE> stack{};
std::array<u8, DEFAULT_STACK_SIZE> astack{};
u32 canary_2 = CANARY_VALUE;
emscripten_fiber_t* context{nullptr};
std::mutex guard;
std::function<void()> entry_point;
std::shared_ptr<Fiber> previous_fiber;
bool is_thread_fiber = false;
bool released = false;
};
Fiber::Fiber(std::function<void()>&& entry_point_func) : impl{std::make_unique<FiberImpl>()} {
impl->entry_point = std::move(entry_point_func);
emscripten_fiber_init(impl->context, [](void *user_data) -> void {
auto* fiber = static_cast<Fiber*>(user_data);
ASSERT(fiber && fiber->impl && fiber->impl->previous_fiber && fiber->impl->previous_fiber->impl);
ASSERT(fiber->impl->canary_1 == CANARY_VALUE);
ASSERT(fiber->impl->canary_2 == CANARY_VALUE);
fiber->impl->previous_fiber->impl->context = fiber->impl->context;
fiber->impl->previous_fiber->impl->guard.unlock();
fiber->impl->previous_fiber.reset();
fiber->impl->entry_point();
UNREACHABLE();
}, impl.get(), impl->stack.data(), impl->stack.size(), impl->astack.data(), impl->astack.size());
}
Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
Fiber::~Fiber() {
if (!impl->released) {
// Make sure the Fiber is not being used
const bool locked = impl->guard.try_lock();
ASSERT(locked && "Destroying a fiber that's still running");
if (locked) {
impl->guard.unlock();
}
}
}
void Fiber::Exit() {
ASSERT(impl->is_thread_fiber && "Exiting non main thread fiber");
if (impl->is_thread_fiber) {
impl->guard.unlock();
impl->released = true;
}
}
void Fiber::YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to) {
to.impl->guard.lock();
to.impl->previous_fiber = weak_from.lock();
emscripten_fiber_swap(to.impl->context, to.impl->previous_fiber->impl->context);
// "from" might no longer be valid if the thread was killed
if (auto from = weak_from.lock()) {
if (from->impl->previous_fiber == nullptr) {
ASSERT(false && "previous_fiber is nullptr!");
} else {
from->impl->previous_fiber->impl->guard.unlock();
from->impl->previous_fiber.reset();
}
}
}
std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
fiber->impl->guard.lock();
fiber->impl->is_thread_fiber = true;
return fiber;
}
#else
struct Fiber::FiberImpl { struct Fiber::FiberImpl {
FiberImpl() {} FiberImpl() {}
u32 canary_1 = CANARY_VALUE; u32 canary_1 = CANARY_VALUE;
std::array<u8, DEFAULT_STACK_SIZE> stack{}; std::array<u8, DEFAULT_STACK_SIZE> stack{};
std::array<u8, DEFAULT_STACK_SIZE> rewind_stack{};
u32 canary_2 = CANARY_VALUE; u32 canary_2 = CANARY_VALUE;
boost::context::detail::fcontext_t context{}; boost::context::detail::fcontext_t context{};
boost::context::detail::fcontext_t rewind_context{};
std::mutex guard; std::mutex guard;
std::function<void()> entry_point; std::function<void()> entry_point;
std::function<void()> rewind_point;
std::shared_ptr<Fiber> previous_fiber; std::shared_ptr<Fiber> previous_fiber;
u8* stack_limit = nullptr; u8* stack_limit = nullptr;
u8* rewind_stack_limit = nullptr;
bool is_thread_fiber = false; bool is_thread_fiber = false;
bool released = false; bool released = false;
}; };
void Fiber::SetRewindPoint(std::function<void()>&& rewind_func) {
impl->rewind_point = std::move(rewind_func);
}
Fiber::Fiber(std::function<void()>&& entry_point_func) : impl{std::make_unique<FiberImpl>()} { Fiber::Fiber(std::function<void()>&& entry_point_func) : impl{std::make_unique<FiberImpl>()} {
impl->entry_point = std::move(entry_point_func); impl->entry_point = std::move(entry_point_func);
impl->stack_limit = impl->stack.data(); impl->stack_limit = impl->stack.data();
impl->rewind_stack_limit = impl->rewind_stack.data();
u8* stack_base = impl->stack_limit + DEFAULT_STACK_SIZE; u8* stack_base = impl->stack_limit + DEFAULT_STACK_SIZE;
impl->context = boost::context::detail::make_fcontext(stack_base, impl->stack.size(), [](boost::context::detail::transfer_t transfer) -> void { impl->context = boost::context::detail::make_fcontext(stack_base, impl->stack.size(), [](boost::context::detail::transfer_t transfer) -> void {
auto* fiber = static_cast<Fiber*>(transfer.data); auto* fiber = static_cast<Fiber*>(transfer.data);
@@ -144,7 +72,7 @@ Fiber::~Fiber() {
if (!impl->released) { if (!impl->released) {
// Make sure the Fiber is not being used // Make sure the Fiber is not being used
const bool locked = impl->guard.try_lock(); const bool locked = impl->guard.try_lock();
ASSERT(locked && "Destroying a fiber that's still running"); ASSERT_MSG(locked, "Destroying a fiber that's still running");
if (locked) { if (locked) {
impl->guard.unlock(); impl->guard.unlock();
} }
@@ -152,7 +80,7 @@ Fiber::~Fiber() {
} }
void Fiber::Exit() { void Fiber::Exit() {
ASSERT(impl->is_thread_fiber && "Exiting non main thread fiber"); ASSERT_MSG(impl->is_thread_fiber, "Exiting non main thread fiber");
if (impl->is_thread_fiber) { if (impl->is_thread_fiber) {
impl->guard.unlock(); impl->guard.unlock();
impl->released = true; impl->released = true;
@@ -182,6 +110,5 @@ std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
fiber->impl->is_thread_fiber = true; fiber->impl->is_thread_fiber = true;
return fiber; return fiber;
} }
#endif
} // namespace Common } // namespace Common
+2 -1
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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -45,6 +45,7 @@ public:
/// Fiber 'from' must be the currently running fiber. /// Fiber 'from' must be the currently running fiber.
static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to); static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to);
[[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber(); [[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
void SetRewindPoint(std::function<void()>&& rewind_func);
/// Only call from main thread's fiber /// Only call from main thread's fiber
void Exit(); void Exit();
private: private:
-5
View File
@@ -136,11 +136,6 @@ public:
eden_path = GetDataDirectory("XDG_DATA_HOME") / EDEN_DIR; eden_path = GetDataDirectory("XDG_DATA_HOME") / EDEN_DIR;
eden_path_cache = GetDataDirectory("XDG_CACHE_HOME") / EDEN_DIR; eden_path_cache = GetDataDirectory("XDG_CACHE_HOME") / EDEN_DIR;
eden_path_config = GetDataDirectory("XDG_CONFIG_HOME") / EDEN_DIR; eden_path_config = GetDataDirectory("XDG_CONFIG_HOME") / EDEN_DIR;
#if defined(__EMSCRIPTEN__) || defined(__wasi__) || defined(__managarm__)
// folders MAY not exist in this distrobution/OS
CreateParentDir(GetDataDirectory("XDG_CONFIG_HOME"));
CreateParentDir(GetDataDirectory("XDG_CACHE_HOME"));
#endif
} else { } else {
eden_path_cache = eden_path / CACHE_DIR; eden_path_cache = eden_path / CACHE_DIR;
eden_path_config = eden_path / CONFIG_DIR; eden_path_config = eden_path / CONFIG_DIR;
+6 -6
View File
@@ -394,7 +394,7 @@ private:
::Common::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset ::Common::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset
}; };
#elif defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__) #elif defined(__OPENORBIS__) || defined(__managarm__)
// None of the luxuries of POSIX, all of the suffering // None of the luxuries of POSIX, all of the suffering
// For managarm: see https://github.com/managarm/managarm/issues/1370 // For managarm: see https://github.com/managarm/managarm/issues/1370
#else // ^^^ Windows ^^^ vvv POSIX vvv #else // ^^^ Windows ^^^ vvv POSIX vvv
@@ -689,7 +689,7 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
: backing_size(backing_size_) : backing_size(backing_size_)
, virtual_size(virtual_size_) , virtual_size(virtual_size_)
{ {
#if defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__) #if defined(__OPENORBIS__) || defined(__managarm__)
LOG_WARNING(HW_Memory, "Platform doesn't support fastmem"); LOG_WARNING(HW_Memory, "Platform doesn't support fastmem");
fallback_buffer.emplace(backing_size); fallback_buffer.emplace(backing_size);
backing_base = fallback_buffer->data(); backing_base = fallback_buffer->data();
@@ -723,7 +723,7 @@ HostMemory::HostMemory(HostMemory&&) noexcept = default;
HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default; HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default;
void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms, bool separate_heap) { void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms, bool separate_heap) {
#if !(defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__)) #if !(defined(__OPENORBIS__) || defined(__managarm__))
ASSERT(virtual_offset % PageAlignment == 0); ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(host_offset % PageAlignment == 0); ASSERT(host_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0); ASSERT(length % PageAlignment == 0);
@@ -737,7 +737,7 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, M
} }
void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap) { void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap) {
#if !(defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__)) #if !(defined(__OPENORBIS__) || defined(__managarm__))
ASSERT(virtual_offset % PageAlignment == 0); ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0); ASSERT(length % PageAlignment == 0);
ASSERT(virtual_offset + length <= virtual_size); ASSERT(virtual_offset + length <= virtual_size);
@@ -749,7 +749,7 @@ void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap)
} }
void HostMemory::Protect(size_t virtual_offset, size_t length, MemoryPermission perm) { void HostMemory::Protect(size_t virtual_offset, size_t length, MemoryPermission perm) {
#if !(defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__)) #if !(defined(__OPENORBIS__) || defined(__managarm__))
ASSERT(virtual_offset % PageAlignment == 0); ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0); ASSERT(length % PageAlignment == 0);
ASSERT(virtual_offset + length <= virtual_size); ASSERT(virtual_offset + length <= virtual_size);
@@ -768,7 +768,7 @@ void HostMemory::ClearBackingRegion(size_t physical_offset, size_t length, u32 f
} }
void HostMemory::EnableDirectMappedAddress() { void HostMemory::EnableDirectMappedAddress() {
#if !(defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__)) #if !(defined(__OPENORBIS__) || defined(__managarm__))
if (impl) { if (impl) {
impl->EnableDirectMappedAddress(); impl->EnableDirectMappedAddress();
virtual_size += reinterpret_cast<uintptr_t>(virtual_base); virtual_size += reinterpret_cast<uintptr_t>(virtual_base);
+1 -1
View File
@@ -77,7 +77,7 @@ private:
size_t backing_size{}; size_t backing_size{};
size_t virtual_size{}; size_t virtual_size{};
#if !(defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__)) #if !(defined(__OPENORBIS__) || defined(__managarm__))
// Low level handler for the platform dependent memory routines // Low level handler for the platform dependent memory routines
class Impl; class Impl;
std::unique_ptr<Impl> impl; std::unique_ptr<Impl> impl;
-79
View File
@@ -1,79 +0,0 @@
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/vector_math.h"
namespace Common {
template <typename T>
class Quaternion {
public:
Vec3<T> xyz;
T w{};
[[nodiscard]] Quaternion<decltype(-T{})> Inverse() const {
return {-xyz, w};
}
[[nodiscard]] Quaternion<decltype(T{} + T{})> operator+(const Quaternion& other) const {
return {xyz + other.xyz, w + other.w};
}
[[nodiscard]] Quaternion<decltype(T{} - T{})> operator-(const Quaternion& other) const {
return {xyz - other.xyz, w - other.w};
}
[[nodiscard]] Quaternion<decltype(T{} * T{} - T{} * T{})> operator*(
const Quaternion& other) const {
return {xyz * other.w + other.xyz * w + Cross(xyz, other.xyz),
w * other.w - Dot(xyz, other.xyz)};
}
[[nodiscard]] Quaternion<T> Normalized() const {
T length = std::sqrt(xyz.Length2() + w * w);
return {xyz / length, w / length};
}
[[nodiscard]] std::array<decltype(-T{}), 16> ToMatrix() const {
const T x2 = xyz[0] * xyz[0];
const T y2 = xyz[1] * xyz[1];
const T z2 = xyz[2] * xyz[2];
const T xy = xyz[0] * xyz[1];
const T wz = w * xyz[2];
const T xz = xyz[0] * xyz[2];
const T wy = w * xyz[1];
const T yz = xyz[1] * xyz[2];
const T wx = w * xyz[0];
return {1.0f - 2.0f * (y2 + z2),
2.0f * (xy + wz),
2.0f * (xz - wy),
0.0f,
2.0f * (xy - wz),
1.0f - 2.0f * (x2 + z2),
2.0f * (yz + wx),
0.0f,
2.0f * (xz + wy),
2.0f * (yz - wx),
1.0f - 2.0f * (x2 + y2),
0.0f,
0.0f,
0.0f,
0.0f,
1.0f};
}
};
template <typename T>
[[nodiscard]] auto QuaternionRotate(const Quaternion<T>& q, const Vec3<T>& v) {
return v + 2 * Cross(q.xyz, Cross(q.xyz, v) + v * q.w);
}
[[nodiscard]] inline Quaternion<float> MakeQuaternion(const Vec3<float>& axis, float angle) {
return {axis * std::sin(angle / 2), std::cos(angle / 2)};
}
} // namespace Common
+3 -3
View File
@@ -92,14 +92,14 @@ struct EnumMetadata {
// AudioEngine must be specified discretely due to having existing but slightly different // AudioEngine must be specified discretely due to having existing but slightly different
// canonicalizations // canonicalizations
// TODO (lat9nq): Remove explicit definition of AudioEngine/sink_id // TODO (lat9nq): Remove explicit definition of AudioEngine/sink_id
enum class AudioEngine : u32 { Auto, Cubeb, Sdl3, Null, Oboe, }; enum class AudioEngine : u32 { Auto, Cubeb, Sdl3, Null, };
template<> template<>
inline std::vector<std::pair<std::string_view, AudioEngine>> EnumMetadata<AudioEngine>::Canonicalizations() { inline std::vector<std::pair<std::string_view, AudioEngine>> EnumMetadata<AudioEngine>::Canonicalizations() {
return { return {
{"auto", AudioEngine::Auto}, {"auto", AudioEngine::Auto},
{"cubeb", AudioEngine::Cubeb}, {"cubeb", AudioEngine::Cubeb},
{"sdl3", AudioEngine::Sdl3}, {"sdl3", AudioEngine::Sdl3},
{"null", AudioEngine::Null}, {"oboe", AudioEngine::Oboe}, {"null", AudioEngine::Null},
}; };
} }
/// @brief This is just a sufficiently large number that is more than the number of other enums declared here /// @brief This is just a sufficiently large number that is more than the number of other enums declared here
@@ -113,7 +113,7 @@ inline AudioEngine EnumMetadata<AudioEngine>::GetFirst() {
} }
template<> template<>
inline AudioEngine EnumMetadata<AudioEngine>::GetLast() { inline AudioEngine EnumMetadata<AudioEngine>::GetLast() {
return AudioEngine::Oboe; return AudioEngine::Null;
} }
ENUM(AudioMode, Mono, Stereo, Surround); ENUM(AudioMode, Mono, Stereo, Surround);
-4
View File
@@ -490,8 +490,6 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
LOG_DEBUG(Common, "Could not set thread nice value to {}: {}", nice_value, LOG_DEBUG(Common, "Could not set thread nice value to {}: {}", nice_value,
GetLastErrorMsg()); GetLastErrorMsg());
} }
#elif defined(__EMSCRIPTEN__)
// TODO: set priority?
#else #else
const s32 max_prio = sched_get_priority_max(SCHED_OTHER); const s32 max_prio = sched_get_priority_max(SCHED_OTHER);
const s32 min_prio = sched_get_priority_min(SCHED_OTHER); const s32 min_prio = sched_get_priority_min(SCHED_OTHER);
@@ -536,8 +534,6 @@ void SetCurrentThreadName(const char* name) {
// See for reference // See for reference
// https://gitlab.freedesktop.org/mesa/mesa/-/blame/main/src/util/u_thread.c?ref_type=heads#L75 // https://gitlab.freedesktop.org/mesa/mesa/-/blame/main/src/util/u_thread.c?ref_type=heads#L75
(void)name; (void)name;
#elif defined(__EMSCRIPTEN__)
// TODO: set thread name?
#else #else
pthread_setname_np(pthread_self(), name); pthread_setname_np(pthread_self(), name);
#endif #endif
+89 -713
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2014 Tony Wasserka // SPDX-FileCopyrightText: 2014 Tony Wasserka
@@ -7,752 +7,128 @@
#pragma once #pragma once
#ifdef __ARM_NEON
#include <arm_neon.h>
#endif
#include <cmath> #include <cmath>
#include <type_traits> #include <type_traits>
namespace Common { namespace Common {
template <typename T> template <typename T, size_t N>
class Vec2; class Vec {
template <typename T>
class Vec3;
template <typename T>
class Vec4;
template <typename T>
class Vec2 {
public: public:
T x{}; std::array<T, N> elems{};
T y{};
constexpr Vec2() = default; constexpr Vec() = default;
constexpr Vec2(const T& x_, const T& y_) : x(x_), y(y_) {} constexpr Vec(T e0) noexcept : elems{e0} {}
constexpr Vec(T e0, T e1) noexcept : elems{e0, e1} {}
constexpr Vec(T e0, T e1, T e2) noexcept : elems{e0, e1, e2} {}
constexpr Vec(T e0, T e1, T e2, T e4) noexcept : elems{e0, e1, e2, e4} {}
//explicit constexpr Vec(const std::initializer_list<T> elems_) noexcept : elems{elems_} {}
template <typename T2> [[nodiscard]] constexpr Vec<decltype(T{} + T{}), N> operator+(const Vec o) const noexcept {
[[nodiscard]] constexpr Vec2<T2> Cast() const { Vec<decltype(T{} + T{}), N> r{};
return Vec2<T2>(static_cast<T2>(x), static_cast<T2>(y)); for (size_t i = 0; i < N; ++i)
r.elems[i] = elems[i] + o.elems[i];
return r;
} }
constexpr Vec<T, N> operator+=(const Vec<T, N> o) noexcept { return *this = *this + o; }
[[nodiscard]] static constexpr Vec2 AssignToAll(const T& f) { [[nodiscard]] constexpr Vec<decltype(T{} - T{}), N> operator-(const Vec o) const noexcept {
return Vec2{f, f}; Vec<decltype(T{} - T{}), N> r{};
} for (size_t i = 0; i < N; ++i)
r.elems[i] = elems[i] - o.elems[i];
[[nodiscard]] constexpr Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const { return r;
return {x + other.x, y + other.y};
}
constexpr Vec2& operator+=(const Vec2& other) {
x += other.x;
y += other.y;
return *this;
}
[[nodiscard]] constexpr Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const {
return {x - other.x, y - other.y};
}
constexpr Vec2& operator-=(const Vec2& other) {
x -= other.x;
y -= other.y;
return *this;
} }
constexpr Vec<T, N> operator-=(const Vec<T, N> o) noexcept { return *this = *this - o; }
template <typename U = T> template <typename U = T>
[[nodiscard]] constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const { [[nodiscard]] constexpr Vec<std::enable_if_t<std::is_signed_v<U>, U>, N> operator-() const noexcept {
return {-x, -y}; Vec<U, N> r{};
} for (size_t i = 0; i < N; ++i)
[[nodiscard]] constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const { r.elems[i] = -elems[i];
return {x * other.x, y * other.y}; return r;
} }
[[nodiscard]] constexpr Vec<decltype(T{} * T{}), N> operator*(const Vec o) const noexcept {
Vec<decltype(T{} * T{}), N> r{};
for (size_t i = 0; i < N; ++i)
r.elems[i] = elems[i] * o.elems[i];
return r;
}
template <typename V> template <typename V>
[[nodiscard]] constexpr Vec2<decltype(T{} * V{})> operator*(const V& f) const { [[nodiscard]] constexpr Vec<decltype(T{} * V{}), N> operator*(const V f) const noexcept {
using TV = decltype(T{} * V{}); using TV = decltype(T{} * V{});
using C = std::common_type_t<T, V>; using C = std::common_type_t<T, V>;
Vec<TV, N> r{};
return { for (size_t i = 0; i < N; ++i)
static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)), r.elems[i] = TV(C(elems[i]) * C(f));
static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)), return r;
};
} }
template <typename V>
constexpr Vec<T, N> operator*=(const V f) noexcept { return *this = *this * f; }
template <typename V> template <typename V>
constexpr Vec2& operator*=(const V& f) { [[nodiscard]] constexpr Vec<decltype(T{} / V{}), N> operator/(const V f) const noexcept {
*this = *this * f;
return *this;
}
template <typename V>
[[nodiscard]] constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const {
using TV = decltype(T{} / V{}); using TV = decltype(T{} / V{});
using C = std::common_type_t<T, V>; using C = std::common_type_t<T, V>;
Vec<TV, N> r{};
return { for (size_t i = 0; i < N; ++i)
static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)), r.elems[i] = TV(C(elems[i]) / C(f));
static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)), return r;
};
} }
template <typename V> template <typename V>
constexpr Vec2& operator/=(const V& f) { constexpr Vec<T, N> operator/=(const V f) noexcept { return *this = *this / f; }
*this = *this / f;
return *this;
}
[[nodiscard]] constexpr T Length2() const { [[nodiscard]] constexpr T Length2() const noexcept {
return x * x + y * y; T r{};
for (size_t i = 0; i < N; ++i)
r += elems[i] * elems[i];
return r;
} }
// Only implemented for T=float // Only implemented for T=float
[[nodiscard]] float Length() const; [[nodiscard]] T Length() const { return T(std::sqrt(float(Length2()))); }
[[nodiscard]] float Normalize(); // returns the previous length, which is often useful [[nodiscard]] Vec<T, N> Normalized() const { return *this / Length(); }
[[nodiscard]] constexpr T& operator[](std::size_t i) noexcept { return elems[i]; }
[[nodiscard]] constexpr const T& operator[](std::size_t i) const noexcept { return elems[i]; }
[[nodiscard]] constexpr T& operator[](std::size_t i) { [[nodiscard]] std::array<decltype(-T{}), 16> ToMatrix() const {
return *((&x) + i); const T x2 = elems[0] * elems[0];
} const T y2 = elems[1] * elems[1];
[[nodiscard]] constexpr const T& operator[](std::size_t i) const { const T z2 = elems[2] * elems[2];
return *((&x) + i);
}
constexpr void SetZero() { const T xy = elems[0] * elems[1];
x = 0; const T wz = elems[3] * elems[2];
y = 0; const T xz = elems[0] * elems[2];
} const T wy = elems[3] * elems[1];
const T yz = elems[1] * elems[2];
// Common aliases: UV (texel coordinates), ST (texture coordinates) const T wx = elems[3] * elems[0];
[[nodiscard]] constexpr T& u() { return {
return x; 1.0f - 2.0f * (y2 + z2),
} 2.0f * (xy + wz),
[[nodiscard]] constexpr T& v() { 2.0f * (xz - wy),
return y; 0.0f,
} 2.0f * (xy - wz),
[[nodiscard]] constexpr T& s() { 1.0f - 2.0f * (x2 + z2),
return x; 2.0f * (yz + wx),
} 0.0f,
[[nodiscard]] constexpr T& t() { 2.0f * (xz + wy),
return y; 2.0f * (yz - wx),
} 1.0f - 2.0f * (x2 + y2),
0.0f,
[[nodiscard]] constexpr const T& u() const { 0.0f,
return x; 0.0f,
} 0.0f,
[[nodiscard]] constexpr const T& v() const { 1.0f
return y; };
}
[[nodiscard]] constexpr const T& s() const {
return x;
}
[[nodiscard]] constexpr const T& t() const {
return y;
}
// swizzlers - create a subvector of specific components
[[nodiscard]] constexpr Vec2 yx() const {
return Vec2(y, x);
}
[[nodiscard]] constexpr Vec2 vu() const {
return Vec2(y, x);
}
[[nodiscard]] constexpr Vec2 ts() const {
return Vec2(y, x);
} }
}; };
template <typename T, typename V> template <typename T, size_t N, typename V>
[[nodiscard]] constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) { [[nodiscard]] constexpr Vec<T, N> operator*(const V f, const Vec<T, N> v) noexcept {
using C = std::common_type_t<T, V>; using C = std::common_type_t<T, V>;
Vec<T, N> r{};
return Vec2<T>(static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.x)), for (size_t i = 0; i < N; ++i)
static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.y))); r.elems[i] = T(C(f) * C(v.elems[i]));
} return r;
using Vec2f = Vec2<float>;
template <>
inline float Vec2<float>::Length() const {
return std::sqrt(x * x + y * y);
}
template <>
inline float Vec2<float>::Normalize() {
float length = Length();
*this /= length;
return length;
}
template <typename T>
class Vec3 {
public:
T x{};
T y{};
T z{};
constexpr Vec3() = default;
constexpr Vec3(const T& x_, const T& y_, const T& z_) : x(x_), y(y_), z(z_) {}
template <typename T2>
[[nodiscard]] constexpr Vec3<T2> Cast() const {
return Vec3<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z));
}
[[nodiscard]] static constexpr Vec3 AssignToAll(const T& f) {
return Vec3(f, f, f);
}
[[nodiscard]] constexpr Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const {
return {x + other.x, y + other.y, z + other.z};
}
constexpr Vec3& operator+=(const Vec3& other) {
x += other.x;
y += other.y;
z += other.z;
return *this;
}
[[nodiscard]] constexpr Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const {
return {x - other.x, y - other.y, z - other.z};
}
constexpr Vec3& operator-=(const Vec3& other) {
x -= other.x;
y -= other.y;
z -= other.z;
return *this;
}
template <typename U = T>
[[nodiscard]] constexpr Vec3<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
return {-x, -y, -z};
}
[[nodiscard]] constexpr Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const {
return {x * other.x, y * other.y, z * other.z};
}
template <typename V>
[[nodiscard]] constexpr Vec3<decltype(T{} * V{})> operator*(const V& f) const {
using TV = decltype(T{} * V{});
using C = std::common_type_t<T, V>;
return {
static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
static_cast<TV>(static_cast<C>(z) * static_cast<C>(f)),
};
}
template <typename V>
constexpr Vec3& operator*=(const V& f) {
*this = *this * f;
return *this;
}
template <typename V>
[[nodiscard]] constexpr Vec3<decltype(T{} / V{})> operator/(const V& f) const {
using TV = decltype(T{} / V{});
using C = std::common_type_t<T, V>;
return {
static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
static_cast<TV>(static_cast<C>(z) / static_cast<C>(f)),
};
}
template <typename V>
constexpr Vec3& operator/=(const V& f) {
*this = *this / f;
return *this;
}
void RotateFromOrigin(float roll, float pitch, float yaw) {
float temp = y;
y = std::cos(roll) * y - std::sin(roll) * z;
z = std::sin(roll) * temp + std::cos(roll) * z;
temp = x;
x = std::cos(pitch) * x + std::sin(pitch) * z;
z = -std::sin(pitch) * temp + std::cos(pitch) * z;
temp = x;
x = std::cos(yaw) * x - std::sin(yaw) * y;
y = std::sin(yaw) * temp + std::cos(yaw) * y;
}
[[nodiscard]] constexpr T Length2() const {
return x * x + y * y + z * z;
}
// Only implemented for T=float
[[nodiscard]] float Length() const;
[[nodiscard]] Vec3 Normalized() const;
[[nodiscard]] float Normalize(); // returns the previous length, which is often useful
[[nodiscard]] constexpr T& operator[](std::size_t i) {
return *((&x) + i);
}
[[nodiscard]] constexpr const T& operator[](std::size_t i) const {
return *((&x) + i);
}
constexpr void SetZero() {
x = 0;
y = 0;
z = 0;
}
// Common aliases: UVW (texel coordinates), RGB (colors), STQ (texture coordinates)
[[nodiscard]] constexpr T& u() {
return x;
}
[[nodiscard]] constexpr T& v() {
return y;
}
[[nodiscard]] constexpr T& w() {
return z;
}
[[nodiscard]] constexpr T& r() {
return x;
}
[[nodiscard]] constexpr T& g() {
return y;
}
[[nodiscard]] constexpr T& b() {
return z;
}
[[nodiscard]] constexpr T& s() {
return x;
}
[[nodiscard]] constexpr T& t() {
return y;
}
[[nodiscard]] constexpr T& q() {
return z;
}
[[nodiscard]] constexpr const T& u() const {
return x;
}
[[nodiscard]] constexpr const T& v() const {
return y;
}
[[nodiscard]] constexpr const T& w() const {
return z;
}
[[nodiscard]] constexpr const T& r() const {
return x;
}
[[nodiscard]] constexpr const T& g() const {
return y;
}
[[nodiscard]] constexpr const T& b() const {
return z;
}
[[nodiscard]] constexpr const T& s() const {
return x;
}
[[nodiscard]] constexpr const T& t() const {
return y;
}
[[nodiscard]] constexpr const T& q() const {
return z;
}
// swizzlers - create a subvector of specific components
// e.g. Vec2 uv() { return Vec2(x,y); }
// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all
// component names (x<->r) and permutations (xy<->yx)
#define _DEFINE_SWIZZLER2(a, b, name) \
[[nodiscard]] constexpr Vec2<T> name() const { return Vec2<T>(a, b); }
#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \
_DEFINE_SWIZZLER2(a, b, a##b); \
_DEFINE_SWIZZLER2(a, b, a2##b2); \
_DEFINE_SWIZZLER2(a, b, a3##b3); \
_DEFINE_SWIZZLER2(a, b, a4##b4); \
_DEFINE_SWIZZLER2(b, a, b##a); \
_DEFINE_SWIZZLER2(b, a, b2##a2); \
_DEFINE_SWIZZLER2(b, a, b3##a3); \
_DEFINE_SWIZZLER2(b, a, b4##a4)
DEFINE_SWIZZLER2(x, y, r, g, u, v, s, t);
DEFINE_SWIZZLER2(x, z, r, b, u, w, s, q);
DEFINE_SWIZZLER2(y, z, g, b, v, w, t, q);
#undef DEFINE_SWIZZLER2
#undef _DEFINE_SWIZZLER2
};
template <typename T, typename V>
[[nodiscard]] constexpr Vec3<T> operator*(const V& f, const Vec3<T>& vec) {
using C = std::common_type_t<T, V>;
return Vec3<T>(static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.x)),
static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.y)),
static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.z)));
}
template <>
inline float Vec3<float>::Length() const {
return std::sqrt(x * x + y * y + z * z);
}
template <>
inline Vec3<float> Vec3<float>::Normalized() const {
return *this / Length();
}
template <>
inline float Vec3<float>::Normalize() {
float length = Length();
*this /= length;
return length;
}
using Vec3f = Vec3<float>;
template <typename T>
class Vec4 {
public:
T x{};
T y{};
T z{};
T w{};
constexpr Vec4() = default;
constexpr Vec4(const T& x_, const T& y_, const T& z_, const T& w_)
: x(x_), y(y_), z(z_), w(w_) {}
template <typename T2>
[[nodiscard]] constexpr Vec4<T2> Cast() const {
return Vec4<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z),
static_cast<T2>(w));
}
[[nodiscard]] static constexpr Vec4 AssignToAll(const T& f) {
return Vec4(f, f, f, f);
}
[[nodiscard]] constexpr Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const {
return {x + other.x, y + other.y, z + other.z, w + other.w};
}
constexpr Vec4& operator+=(const Vec4& other) {
x += other.x;
y += other.y;
z += other.z;
w += other.w;
return *this;
}
[[nodiscard]] constexpr Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const {
return {x - other.x, y - other.y, z - other.z, w - other.w};
}
constexpr Vec4& operator-=(const Vec4& other) {
x -= other.x;
y -= other.y;
z -= other.z;
w -= other.w;
return *this;
}
template <typename U = T>
[[nodiscard]] constexpr Vec4<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
return {-x, -y, -z, -w};
}
[[nodiscard]] constexpr Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const {
return {x * other.x, y * other.y, z * other.z, w * other.w};
}
template <typename V>
[[nodiscard]] constexpr Vec4<decltype(T{} * V{})> operator*(const V& f) const {
using TV = decltype(T{} * V{});
using C = std::common_type_t<T, V>;
return {
static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
static_cast<TV>(static_cast<C>(z) * static_cast<C>(f)),
static_cast<TV>(static_cast<C>(w) * static_cast<C>(f)),
};
}
template <typename V>
constexpr Vec4& operator*=(const V& f) {
*this = *this * f;
return *this;
}
template <typename V>
[[nodiscard]] constexpr Vec4<decltype(T{} / V{})> operator/(const V& f) const {
using TV = decltype(T{} / V{});
using C = std::common_type_t<T, V>;
return {
static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
static_cast<TV>(static_cast<C>(z) / static_cast<C>(f)),
static_cast<TV>(static_cast<C>(w) / static_cast<C>(f)),
};
}
template <typename V>
constexpr Vec4& operator/=(const V& f) {
*this = *this / f;
return *this;
}
[[nodiscard]] constexpr T Length2() const {
return x * x + y * y + z * z + w * w;
}
[[nodiscard]] constexpr T& operator[](std::size_t i) {
return *((&x) + i);
}
[[nodiscard]] constexpr const T& operator[](std::size_t i) const {
return *((&x) + i);
}
constexpr void SetZero() {
x = 0;
y = 0;
z = 0;
w = 0;
}
// Common alias: RGBA (colors)
[[nodiscard]] constexpr T& r() {
return x;
}
[[nodiscard]] constexpr T& g() {
return y;
}
[[nodiscard]] constexpr T& b() {
return z;
}
[[nodiscard]] constexpr T& a() {
return w;
}
[[nodiscard]] constexpr const T& r() const {
return x;
}
[[nodiscard]] constexpr const T& g() const {
return y;
}
[[nodiscard]] constexpr const T& b() const {
return z;
}
[[nodiscard]] constexpr const T& a() const {
return w;
}
// Swizzlers - Create a subvector of specific components
// e.g. Vec2 uv() { return Vec2(x,y); }
// _DEFINE_SWIZZLER2 defines a single such function
// DEFINE_SWIZZLER2_COMP1 defines one-component functions for all component names (x<->r)
// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and
// permutations (xy<->yx)
#define _DEFINE_SWIZZLER2(a, b, name) \
[[nodiscard]] constexpr Vec2<T> name() const { return Vec2<T>(a, b); }
#define DEFINE_SWIZZLER2_COMP1(a, a2) \
_DEFINE_SWIZZLER2(a, a, a##a); \
_DEFINE_SWIZZLER2(a, a, a2##a2)
#define DEFINE_SWIZZLER2_COMP2(a, b, a2, b2) \
_DEFINE_SWIZZLER2(a, b, a##b); \
_DEFINE_SWIZZLER2(a, b, a2##b2); \
_DEFINE_SWIZZLER2(b, a, b##a); \
_DEFINE_SWIZZLER2(b, a, b2##a2)
DEFINE_SWIZZLER2_COMP2(x, y, r, g);
DEFINE_SWIZZLER2_COMP2(x, z, r, b);
DEFINE_SWIZZLER2_COMP2(x, w, r, a);
DEFINE_SWIZZLER2_COMP2(y, z, g, b);
DEFINE_SWIZZLER2_COMP2(y, w, g, a);
DEFINE_SWIZZLER2_COMP2(z, w, b, a);
DEFINE_SWIZZLER2_COMP1(x, r);
DEFINE_SWIZZLER2_COMP1(y, g);
DEFINE_SWIZZLER2_COMP1(z, b);
DEFINE_SWIZZLER2_COMP1(w, a);
#undef DEFINE_SWIZZLER2_COMP1
#undef DEFINE_SWIZZLER2_COMP2
#undef _DEFINE_SWIZZLER2
#define _DEFINE_SWIZZLER3(a, b, c, name) \
[[nodiscard]] constexpr Vec3<T> name() const { return Vec3<T>(a, b, c); }
#define DEFINE_SWIZZLER3_COMP1(a, a2) \
_DEFINE_SWIZZLER3(a, a, a, a##a##a); \
_DEFINE_SWIZZLER3(a, a, a, a2##a2##a2)
#define DEFINE_SWIZZLER3_COMP3(a, b, c, a2, b2, c2) \
_DEFINE_SWIZZLER3(a, b, c, a##b##c); \
_DEFINE_SWIZZLER3(a, c, b, a##c##b); \
_DEFINE_SWIZZLER3(b, a, c, b##a##c); \
_DEFINE_SWIZZLER3(b, c, a, b##c##a); \
_DEFINE_SWIZZLER3(c, a, b, c##a##b); \
_DEFINE_SWIZZLER3(c, b, a, c##b##a); \
_DEFINE_SWIZZLER3(a, b, c, a2##b2##c2); \
_DEFINE_SWIZZLER3(a, c, b, a2##c2##b2); \
_DEFINE_SWIZZLER3(b, a, c, b2##a2##c2); \
_DEFINE_SWIZZLER3(b, c, a, b2##c2##a2); \
_DEFINE_SWIZZLER3(c, a, b, c2##a2##b2); \
_DEFINE_SWIZZLER3(c, b, a, c2##b2##a2)
DEFINE_SWIZZLER3_COMP3(x, y, z, r, g, b);
DEFINE_SWIZZLER3_COMP3(x, y, w, r, g, a);
DEFINE_SWIZZLER3_COMP3(x, z, w, r, b, a);
DEFINE_SWIZZLER3_COMP3(y, z, w, g, b, a);
DEFINE_SWIZZLER3_COMP1(x, r);
DEFINE_SWIZZLER3_COMP1(y, g);
DEFINE_SWIZZLER3_COMP1(z, b);
DEFINE_SWIZZLER3_COMP1(w, a);
#undef DEFINE_SWIZZLER3_COMP1
#undef DEFINE_SWIZZLER3_COMP3
#undef _DEFINE_SWIZZLER3
};
template <typename T, typename V>
[[nodiscard]] constexpr Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) {
using TV = decltype(V{} * T{});
using C = std::common_type_t<T, V>;
return {
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.x)),
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.y)),
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.z)),
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.w)),
};
}
using Vec4f = Vec4<float>;
template <typename T>
constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec2<T>& a, const Vec2<T>& b) {
return a.x * b.x + a.y * b.y;
}
template <typename T>
[[nodiscard]] constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
template <typename T>
[[nodiscard]] constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) {
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
}
template <>
[[nodiscard]] inline float Dot(const Vec4<float>& a, const Vec4<float>& b) {
#ifdef __ARM_NEON
float32x4_t va = vld1q_f32(&a.x);
float32x4_t vb = vld1q_f32(&b.x);
float32x4_t result = vmulq_f32(va, vb);
#if defined(__aarch64__) // Use vaddvq_f32 in ARMv8 architectures
return vaddvq_f32(result);
#else // Use manual addition for older architectures
float32x2_t sum2 = vadd_f32(vget_high_f32(result), vget_low_f32(result));
return vget_lane_f32(vpadd_f32(sum2, sum2), 0);
#endif
#else
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
#endif
}
template <typename T>
[[nodiscard]] constexpr Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a,
const Vec3<T>& b) {
return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
}
// linear interpolation via float: 0.0=begin, 1.0=end
template <typename X>
[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
const float t) {
return begin * (1.f - t) + end * t;
}
// linear interpolation via int: 0=begin, base=end
template <typename X, int base>
[[nodiscard]] constexpr decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin,
const X& end,
const int t) {
return (begin * (base - t) + end * t) / base;
}
// bilinear interpolation. s is for interpolating x00-x01 and x10-x11, and t is for the second
// interpolation.
template <typename X>
[[nodiscard]] constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11,
const float s, const float t) {
auto y0 = Lerp(x00, x01, s);
auto y1 = Lerp(x10, x11, s);
return Lerp(y0, y1, t);
}
// Utility vector factories
template <typename T>
[[nodiscard]] constexpr Vec2<T> MakeVec(const T& x, const T& y) {
return Vec2<T>{x, y};
}
template <typename T>
[[nodiscard]] constexpr Vec3<T> MakeVec(const T& x, const T& y, const T& z) {
return Vec3<T>{x, y, z};
}
template <typename T>
[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) {
return MakeVec(x, y, zw[0], zw[1]);
}
template <typename T>
[[nodiscard]] constexpr Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) {
return MakeVec(xy[0], xy[1], z);
}
template <typename T>
[[nodiscard]] constexpr Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) {
return MakeVec(x, yz[0], yz[1]);
}
template <typename T>
[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) {
return Vec4<T>{x, y, z, w};
}
template <typename T>
[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) {
return MakeVec(xy[0], xy[1], z, w);
}
template <typename T>
[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
return MakeVec(x, yz[0], yz[1], w);
}
// NOTE: This has priority over "Vec2<Vec2<T>> MakeVec(const Vec2<T>& x, const Vec2<T>& y)".
// Even if someone wanted to use an odd object like Vec2<Vec2<T>>, the compiler would error
// out soon enough due to misuse of the returned structure.
template <typename T>
[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) {
return MakeVec(xy[0], xy[1], zw[0], zw[1]);
}
template <typename T>
[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) {
return MakeVec(xyz[0], xyz[1], xyz[2], w);
}
template <typename T>
[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) {
return MakeVec(x, yzw[0], yzw[1], yzw[2]);
} }
} // namespace Common } // namespace Common
+5 -12
View File
@@ -38,6 +38,10 @@ add_library(core STATIC
debugger/debugger.cpp debugger/debugger.cpp
debugger/debugger.h debugger/debugger.h
debugger/debugger_interface.h debugger/debugger_interface.h
debugger/gdbstub.cpp
debugger/gdbstub.h
debugger/gdbstub_arch.cpp
debugger/gdbstub_arch.h
device_memory.cpp device_memory.cpp
device_memory.h device_memory.h
device_memory_manager.h device_memory_manager.h
@@ -1162,14 +1166,6 @@ add_library(core STATIC
tools/freezer.h tools/freezer.h
tools/renderdoc.cpp tools/renderdoc.cpp
tools/renderdoc.h) tools/renderdoc.h)
if (NOT EMSCRIPTEN)
# incompatible with wasm's async model
target_sources(core PRIVATE
debugger/gdbstub.cpp
debugger/gdbstub.h
debugger/gdbstub_arch.cpp
debugger/gdbstub_arch.h)
endif()
if (ENABLE_WIFI_SCAN) if (ENABLE_WIFI_SCAN)
target_sources(core PRIVATE internal_network/wifi_scanner.cpp) target_sources(core PRIVATE internal_network/wifi_scanner.cpp)
@@ -1213,10 +1209,7 @@ target_include_directories(core PRIVATE ${OPUS_INCLUDE_DIRS})
target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz) target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz)
if (BOOST_NO_HEADERS) if (BOOST_NO_HEADERS)
target_link_libraries(core PUBLIC Boost::container Boost::heap Boost::crc) target_link_libraries(core PUBLIC Boost::container Boost::heap Boost::asio Boost::process Boost::crc)
if (NOT EMSCRIPTEN)
target_link_libraries(core PUBLIC Boost::asio Boost::process)
endif()
else() else()
target_link_libraries(core PUBLIC Boost::headers) target_link_libraries(core PUBLIC Boost::headers)
endif() endif()
+2 -4
View File
@@ -6,20 +6,19 @@
#pragma once #pragma once
#ifndef __EMSCRIPTEN__
#include <dynarmic/interface/halt_reason.h> #include <dynarmic/interface/halt_reason.h>
#endif
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
namespace Core { namespace Core {
#ifndef __EMSCRIPTEN__
constexpr Dynarmic::HaltReason StepThread = Dynarmic::HaltReason::Step; constexpr Dynarmic::HaltReason StepThread = Dynarmic::HaltReason::Step;
constexpr Dynarmic::HaltReason DataAbort = Dynarmic::HaltReason::MemoryAbort; constexpr Dynarmic::HaltReason DataAbort = Dynarmic::HaltReason::MemoryAbort;
constexpr Dynarmic::HaltReason BreakLoop = Dynarmic::HaltReason::UserDefined2; constexpr Dynarmic::HaltReason BreakLoop = Dynarmic::HaltReason::UserDefined2;
constexpr Dynarmic::HaltReason SupervisorCall = Dynarmic::HaltReason::UserDefined3; constexpr Dynarmic::HaltReason SupervisorCall = Dynarmic::HaltReason::UserDefined3;
constexpr Dynarmic::HaltReason InstructionBreakpoint = Dynarmic::HaltReason::UserDefined4; constexpr Dynarmic::HaltReason InstructionBreakpoint = Dynarmic::HaltReason::UserDefined4;
constexpr Dynarmic::HaltReason PrefetchAbort = Dynarmic::HaltReason::UserDefined6; constexpr Dynarmic::HaltReason PrefetchAbort = Dynarmic::HaltReason::UserDefined6;
constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) { constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) {
static_assert(u64(HaltReason::StepThread) == u64(StepThread)); static_assert(u64(HaltReason::StepThread) == u64(StepThread));
static_assert(u64(HaltReason::DataAbort) == u64(DataAbort)); static_assert(u64(HaltReason::DataAbort) == u64(DataAbort));
@@ -29,6 +28,5 @@ constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) {
static_assert(u64(HaltReason::PrefetchAbort) == u64(PrefetchAbort)); static_assert(u64(HaltReason::PrefetchAbort) == u64(PrefetchAbort));
return HaltReason(hr); return HaltReason(hr);
} }
#endif
} // namespace Core } // namespace Core
+3 -28
View File
@@ -6,54 +6,30 @@
#include <mutex> #include <mutex>
#include <utility> #include <utility>
#if defined(__EMSCRIPTEN__) || defined(__wasi__)
// TODO: gdb stub compat with emscripten?
#else
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/version.hpp> #include <boost/version.hpp>
#if BOOST_VERSION > 108400 && (!defined(_WINDOWS) && !defined(__ANDROID__)) || defined(YUZU_BOOST_v1) #if BOOST_VERSION > 108400 && (!defined(_WINDOWS) && !defined(__ANDROID__)) || defined(YUZU_BOOST_v1)
#define USE_BOOST_v1 #define USE_BOOST_v1
#endif #endif
#ifdef USE_BOOST_v1 #ifdef USE_BOOST_v1
#include <boost/process/v1/async_pipe.hpp> #include <boost/process/v1/async_pipe.hpp>
#else #else
#include <boost/process/async_pipe.hpp> #include <boost/process/async_pipe.hpp>
#endif #endif
#endif
#include "common/logging.h" #include "common/logging.h"
#include "common/polyfill_thread.h" #include "common/polyfill_thread.h"
#include "common/thread.h" #include "common/thread.h"
#include "core/core.h" #include "core/core.h"
#include "core/debugger/debugger.h" #include "core/debugger/debugger.h"
#if defined(__EMSCRIPTEN__) || defined(__wasi__)
// TODO: gdbstub with emscripten?
#else
#include "core/debugger/debugger_interface.h" #include "core/debugger/debugger_interface.h"
#include "core/debugger/gdbstub.h" #include "core/debugger/gdbstub.h"
#endif
#include "core/hle/kernel/global_scheduler_context.h" #include "core/hle/kernel/global_scheduler_context.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scheduler.h"
#if defined(__EMSCRIPTEN__) || defined(__wasi__)
namespace Core {
// Dummy
class DebuggerImpl {
char pad;
};
Debugger::Debugger(Core::System& system, u16 port) {}
Debugger::~Debugger() = default;
bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) {
return false;
}
bool Debugger::NotifyThreadWatchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) {
return false;
}
void Debugger::NotifyShutdown() {}
} // namespace Core
#else
template <typename Readable, typename Buffer, typename Callback> template <typename Readable, typename Buffer, typename Callback>
static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) { static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
static_assert(std::is_trivial_v<Buffer>); static_assert(std::is_trivial_v<Buffer>);
@@ -382,7 +358,7 @@ private:
ConnectionState(boost::asio::ip::tcp::socket&& client_socket_, async_pipe signal_pipe_, Kernel::KernelCore& kernel) ConnectionState(boost::asio::ip::tcp::socket&& client_socket_, async_pipe signal_pipe_, Kernel::KernelCore& kernel)
: client_socket{std::move(client_socket_)} : client_socket{std::move(client_socket_)}
, signal_pipe{signal_pipe_} , signal_pipe{std::move(signal_pipe_)}
, active_thread{kernel, nullptr} , active_thread{kernel, nullptr}
{} {}
@@ -424,4 +400,3 @@ void Debugger::NotifyShutdown() {
} }
} // namespace Core } // namespace Core
#endif
+6 -10
View File
@@ -7,14 +7,8 @@
#include <random> #include <random>
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/arm/exclusive_monitor.h"
#ifndef __EMSCRIPTEN__
#include "core/arm/dynarmic/arm_dynarmic.h" #include "core/arm/dynarmic/arm_dynarmic.h"
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h" #include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
#include "core/arm/dynarmic/arm_dynarmic_32.h"
#include "core/arm/dynarmic/arm_dynarmic_64.h"
#endif
#include "core/core.h" #include "core/core.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_scoped_resource_reservation.h"
@@ -24,6 +18,8 @@
#include "core/hle/kernel/k_thread_queue.h" #include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/k_worker_task_manager.h" #include "core/hle/kernel/k_worker_task_manager.h"
#include "core/arm/dynarmic/arm_dynarmic_32.h"
#include "core/arm/dynarmic/arm_dynarmic_64.h"
#ifdef HAS_NCE #ifdef HAS_NCE
#include "core/arm/nce/arm_nce.h" #include "core/arm/nce/arm_nce.h"
#endif #endif
@@ -1025,6 +1021,9 @@ Result KProcess::Run(KernelCore& kernel, s32 priority, size_t stack_size) {
// Suspend for debug, if we should. // Suspend for debug, if we should.
if (kernel.System().DebuggerEnabled()) { if (kernel.System().DebuggerEnabled()) {
LOG_INFO(Debug_GDBStub,
"GDB stub enabled; suspending guest process until a debugger continues execution on port {}",
Settings::values.gdbstub_port.GetValue());
main_thread->RequestSuspend(kernel, SuspendType::Debug); main_thread->RequestSuspend(kernel, SuspendType::Debug);
} }
@@ -1304,11 +1303,9 @@ void KProcess::LoadModule(KernelCore& kernel, CodeSet code_set, KProcessAddress
} }
void KProcess::InitializeInterfaces(KernelCore& kernel) { void KProcess::InitializeInterfaces(KernelCore& kernel) {
#ifdef __EMSCRIPTEN__
ASSERT(false && "unimplemented");
#else
m_exclusive_monitor = m_exclusive_monitor =
Core::MakeExclusiveMonitor(this->GetMemory(), Core::Hardware::NUM_CPU_CORES); Core::MakeExclusiveMonitor(this->GetMemory(), Core::Hardware::NUM_CPU_CORES);
#ifdef HAS_NCE #ifdef HAS_NCE
if (this->IsApplication() && Settings::IsNceEnabled()) { if (this->IsApplication() && Settings::IsNceEnabled()) {
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++)
@@ -1328,7 +1325,6 @@ void KProcess::InitializeInterfaces(KernelCore& kernel) {
static_cast<Core::DynarmicExclusiveMonitor&>(*m_exclusive_monitor), i); static_cast<Core::DynarmicExclusiveMonitor&>(*m_exclusive_monitor), i);
} }
} }
#endif
} }
bool KProcess::InsertWatchpoint(KernelCore& kernel, KProcessAddress addr, u64 size, DebugWatchpointType type) { bool KProcess::InsertWatchpoint(KernelCore& kernel, KProcessAddress addr, u64 size, DebugWatchpointType type) {
+3 -13
View File
@@ -88,22 +88,15 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
// BEGONE cold clones of lambdas, for I have merged you all into a SINGLE lambda instead of // BEGONE cold clones of lambdas, for I have merged you all into a SINGLE lambda instead of
// spamming lambdas like it's some kind of lambda calculus class // spamming lambdas like it's some kind of lambda calculus class
std::vector<std::pair<std::string_view, void (*)(Core::System&)>> rt_services{ for (auto const& e : std::vector<std::pair<std::string_view, void (*)(Core::System&)>>{
{"audio", &Audio::LoopProcess}, {"audio", &Audio::LoopProcess},
{"FS", &FileSystem::LoopProcess}, {"FS", &FileSystem::LoopProcess},
{"ldn", &LDN::LoopProcess}, {"ldn", &LDN::LoopProcess},
{"nvservices", &Nvidia::LoopProcess}, {"nvservices", &Nvidia::LoopProcess},
{"bsdsocket", &Sockets::LoopProcess}, {"bsdsocket", &Sockets::LoopProcess},
}; })
#if defined(__EMSCRIPTEN__) || defined(__wasi__) || defined(__OPENORBIS__)
for (auto const& e : rt_services)
kernel.RunOnGuestCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); });
kernel.RunOnGuestCoreProcess("vi", [&, token] { VI::LoopProcess(system, token); });
#else
for (auto const& e : rt_services)
kernel.RunOnHostCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); }).detach(); kernel.RunOnHostCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); }).detach();
kernel.RunOnHostCoreProcess("vi", [&, token] { VI::LoopProcess(system, token); }).detach(); kernel.RunOnHostCoreProcess("vi", [&, token] { VI::LoopProcess(system, token); }).detach();
#endif
// Avoid cold clones of lambdas -- succintly // Avoid cold clones of lambdas -- succintly
for (auto const& e : std::vector<std::pair<std::string_view, void (*)(Core::System&)>>{ for (auto const& e : std::vector<std::pair<std::string_view, void (*)(Core::System&)>>{
{"sm", &SM::LoopProcess}, {"sm", &SM::LoopProcess},
@@ -127,10 +120,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
{"glue", &Glue::LoopProcess}, {"glue", &Glue::LoopProcess},
{"grc", &GRC::LoopProcess}, {"grc", &GRC::LoopProcess},
{"hid", &HID::LoopProcess}, {"hid", &HID::LoopProcess},
// Must match with src/core/CMakeLists.txt for target_source of jit.cpp
#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) || defined(ARCHITECTURE_riscv64) || defined(ARCHITECTURE_loongarch64)
{"jit", &JIT::LoopProcess}, {"jit", &JIT::LoopProcess},
#endif
{"lbl", &LBL::LoopProcess}, {"lbl", &LBL::LoopProcess},
{"Loader", &LDR::LoopProcess}, {"Loader", &LDR::LoopProcess},
{"LogManager.Prod", &LM::LoopProcess}, {"LogManager.Prod", &LM::LoopProcess},
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -30,15 +33,15 @@ struct DeviceSettings {
INSERT_PADDING_BYTES(0x20); // Reserved INSERT_PADDING_BYTES(0x20); // Reserved
// nn::settings::system::ConsoleSixAxisSensorAccelerationBias // nn::settings::system::ConsoleSixAxisSensorAccelerationBias
Common::Vec3<f32> console_six_axis_sensor_acceleration_bias; Common::Vec<f32, 3> console_six_axis_sensor_acceleration_bias;
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias // nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias
Common::Vec3<f32> console_six_axis_sensor_angular_velocity_bias; Common::Vec<f32, 3> console_six_axis_sensor_angular_velocity_bias;
// nn::settings::system::ConsoleSixAxisSensorAccelerationGain // nn::settings::system::ConsoleSixAxisSensorAccelerationGain
std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain; std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain;
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain // nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain; std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain;
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias // nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias
Common::Vec3<f32> console_six_axis_sensor_angular_velocity_time_bias; Common::Vec<f32, 3> console_six_axis_sensor_angular_velocity_time_bias;
// nn::settings::system::ConsoleSixAxisSensorAngularAcceleration // nn::settings::system::ConsoleSixAxisSensorAngularAcceleration
std::array<u8, 0x24> console_six_axis_sensor_angular_acceleration; std::array<u8, 0x24> console_six_axis_sensor_angular_acceleration;
}; };
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -153,15 +153,15 @@ struct SystemSettings {
INSERT_PADDING_BYTES(0x7FF8); // Reserved INSERT_PADDING_BYTES(0x7FF8); // Reserved
// nn::settings::system::ConsoleSixAxisSensorAccelerationBias // nn::settings::system::ConsoleSixAxisSensorAccelerationBias
Common::Vec3<f32> console_six_axis_sensor_acceleration_bias; Common::Vec<f32, 3> console_six_axis_sensor_acceleration_bias;
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias // nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias
Common::Vec3<f32> console_six_axis_sensor_angular_velocity_bias; Common::Vec<f32, 3> console_six_axis_sensor_angular_velocity_bias;
// nn::settings::system::ConsoleSixAxisSensorAccelerationGain // nn::settings::system::ConsoleSixAxisSensorAccelerationGain
std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain; std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain;
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain // nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain; std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain;
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias // nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias
Common::Vec3<f32> console_six_axis_sensor_angular_velocity_time_bias; Common::Vec<f32, 3> console_six_axis_sensor_angular_velocity_time_bias;
// nn::settings::system::ConsoleSixAxisSensorAngularAcceleration // nn::settings::system::ConsoleSixAxisSensorAngularAcceleration
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_acceleration; std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_acceleration;
INSERT_PADDING_BYTES(0x70); // Reserved INSERT_PADDING_BYTES(0x70); // Reserved
+2 -2
View File
@@ -170,12 +170,12 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
auto& emulated = console.motion_values.emulated; auto& emulated = console.motion_values.emulated;
raw_status = TransformToMotion(callback); raw_status = TransformToMotion(callback);
emulated.SetAcceleration(Common::Vec3f{ emulated.SetAcceleration(Common::Vec<f32, 3>{
raw_status.accel.x.value, raw_status.accel.x.value,
raw_status.accel.y.value, raw_status.accel.y.value,
raw_status.accel.z.value, raw_status.accel.z.value,
}); });
emulated.SetGyroscope(Common::Vec3f{ emulated.SetGyroscope(Common::Vec<f32, 3>{
raw_status.gyro.x.value, raw_status.gyro.x.value,
raw_status.gyro.y.value, raw_status.gyro.y.value,
raw_status.gyro.z.value, raw_status.gyro.z.value,
+6 -7
View File
@@ -18,7 +18,6 @@
#include "common/input.h" #include "common/input.h"
#include "common/param_package.h" #include "common/param_package.h"
#include "common/point.h" #include "common/point.h"
#include "common/quaternion.h"
#include "common/vector_math.h" #include "common/vector_math.h"
#include "hid_core/frontend/motion_input.h" #include "hid_core/frontend/motion_input.h"
#include "hid_core/hid_types.h" #include "hid_core/hid_types.h"
@@ -43,12 +42,12 @@ using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>;
// Contains all motion related data that is used on the services // Contains all motion related data that is used on the services
struct ConsoleMotion { struct ConsoleMotion {
Common::Vec3f accel{}; Common::Vec<f32, 3> accel{};
Common::Vec3f gyro{}; Common::Vec<f32, 3> gyro{};
Common::Vec3f rotation{}; Common::Vec<f32, 3> rotation{};
std::array<Common::Vec3f, 3> orientation{}; std::array<Common::Vec<f32, 3>, 3> orientation{};
Common::Quaternion<f32> quaternion{}; Common::Vec<f32, 4> quaternion{};
Common::Vec3f gyro_bias{}; Common::Vec<f32, 3> gyro_bias{};
f32 verticalization_error{}; f32 verticalization_error{};
bool is_at_rest{}; bool is_at_rest{};
}; };
@@ -1051,12 +1051,12 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
auto& emulated = controller.motion_values[index].emulated; auto& emulated = controller.motion_values[index].emulated;
raw_status = TransformToMotion(callback); raw_status = TransformToMotion(callback);
emulated.SetAcceleration(Common::Vec3f{ emulated.SetAcceleration(Common::Vec<f32, 3>{
raw_status.accel.x.value, raw_status.accel.x.value,
raw_status.accel.y.value, raw_status.accel.y.value,
raw_status.accel.z.value, raw_status.accel.z.value,
}); });
emulated.SetGyroscope(Common::Vec3f{ emulated.SetGyroscope(Common::Vec<f32, 3>{
raw_status.gyro.x.value, raw_status.gyro.x.value,
raw_status.gyro.y.value, raw_status.gyro.y.value,
raw_status.gyro.z.value, raw_status.gyro.z.value,
+5 -5
View File
@@ -107,11 +107,11 @@ struct RingSensorForce {
using NfcState = Common::Input::NfcStatus; using NfcState = Common::Input::NfcStatus;
struct ControllerMotion { struct ControllerMotion {
Common::Vec3f accel{}; Common::Vec<f32, 3> accel{};
Common::Vec3f gyro{}; Common::Vec<f32, 3> gyro{};
Common::Vec3f rotation{}; Common::Vec<f32, 3> rotation{};
Common::Vec3f euler{}; Common::Vec<f32, 3> euler{};
std::array<Common::Vec3f, 3> orientation{}; std::array<Common::Vec<f32, 3>, 3> orientation{};
bool is_at_rest{}; bool is_at_rest{};
}; };
+90 -90
View File
@@ -26,20 +26,19 @@ void MotionInput::SetPID(f32 new_kp, f32 new_ki, f32 new_kd) {
kd = new_kd; kd = new_kd;
} }
void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) { void MotionInput::SetAcceleration(const Common::Vec<f32, 3>& acceleration) {
accel = acceleration; accel = acceleration;
accel[0] = std::clamp(accel[0], -AccelMaxValue, AccelMaxValue);
accel.x = std::clamp(accel.x, -AccelMaxValue, AccelMaxValue); accel[1] = std::clamp(accel[1], -AccelMaxValue, AccelMaxValue);
accel.y = std::clamp(accel.y, -AccelMaxValue, AccelMaxValue); accel[2] = std::clamp(accel[2], -AccelMaxValue, AccelMaxValue);
accel.z = std::clamp(accel.z, -AccelMaxValue, AccelMaxValue);
} }
void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) { void MotionInput::SetGyroscope(const Common::Vec<f32, 3>& gyroscope) {
gyro = gyroscope - gyro_bias; gyro = gyroscope - gyro_bias;
gyro.x = std::clamp(gyro.x, -GyroMaxValue, GyroMaxValue); gyro[0] = std::clamp(gyro[0], -GyroMaxValue, GyroMaxValue);
gyro.y = std::clamp(gyro.y, -GyroMaxValue, GyroMaxValue); gyro[1] = std::clamp(gyro[1], -GyroMaxValue, GyroMaxValue);
gyro.z = std::clamp(gyro.z, -GyroMaxValue, GyroMaxValue); gyro[2] = std::clamp(gyro[2], -GyroMaxValue, GyroMaxValue);
// Auto adjust gyro_bias to minimize drift // Auto adjust gyro_bias to minimize drift
if (!IsMoving(IsAtRestRelaxed)) { if (!IsMoving(IsAtRestRelaxed)) {
@@ -59,25 +58,25 @@ void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) {
} }
} }
void MotionInput::SetQuaternion(const Common::Quaternion<f32>& quaternion) { void MotionInput::SetQuaternion(const Common::Vec<f32, 4>& quaternion) {
quat = quaternion; quat = quaternion;
} }
void MotionInput::SetEulerAngles(const Common::Vec3f& euler_angles) { void MotionInput::SetEulerAngles(const Common::Vec<f32, 3>& euler_angles) {
const float cr = std::cos(euler_angles.x * 0.5f); const float cr = std::cos(euler_angles[0] * 0.5f);
const float sr = std::sin(euler_angles.x * 0.5f); const float sr = std::sin(euler_angles[0] * 0.5f);
const float cp = std::cos(euler_angles.y * 0.5f); const float cp = std::cos(euler_angles[1] * 0.5f);
const float sp = std::sin(euler_angles.y * 0.5f); const float sp = std::sin(euler_angles[1] * 0.5f);
const float cy = std::cos(euler_angles.z * 0.5f); const float cy = std::cos(euler_angles[2] * 0.5f);
const float sy = std::sin(euler_angles.z * 0.5f); const float sy = std::sin(euler_angles[2] * 0.5f);
quat.w = cr * cp * cy + sr * sp * sy; quat[3] = cr * cp * cy + sr * sp * sy;
quat.xyz.x = sr * cp * cy - cr * sp * sy; quat[0] = sr * cp * cy - cr * sp * sy;
quat.xyz.y = cr * sp * cy + sr * cp * sy; quat[1] = cr * sp * cy + sr * cp * sy;
quat.xyz.z = cr * cp * sy - sr * sp * cy; quat[2] = cr * cp * sy - sr * sp * cy;
} }
void MotionInput::SetGyroBias(const Common::Vec3f& bias) { void MotionInput::SetGyroBias(const Common::Vec<f32, 3>& bias) {
gyro_bias = bias; gyro_bias = bias;
} }
@@ -98,7 +97,7 @@ void MotionInput::ResetRotations() {
} }
void MotionInput::ResetQuaternion() { void MotionInput::ResetQuaternion() {
quat = {{0.0f, 0.0f, -1.0f}, 0.0f}; quat = Common::Vec<f32, 4>{0.0f, 0.0f, -1.0f, 0.0f};
} }
bool MotionInput::IsMoving(f32 sensitivity) const { bool MotionInput::IsMoving(f32 sensitivity) const {
@@ -137,10 +136,10 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) {
ResetOrientation(); ResetOrientation();
} }
// Short name local variable for readability // Short name local variable for readability
f32 q1 = quat.w; f32 q1 = quat[3];
f32 q2 = quat.xyz[0]; f32 q2 = quat[0];
f32 q3 = quat.xyz[1]; f32 q3 = quat[1];
f32 q4 = quat.xyz[2]; f32 q4 = quat[2];
const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f; const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f;
// Ignore invalid elapsed time // Ignore invalid elapsed time
@@ -150,23 +149,23 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) {
const auto normal_accel = accel.Normalized(); const auto normal_accel = accel.Normalized();
auto rad_gyro = gyro * std::numbers::pi_v<float> * 2.f; auto rad_gyro = gyro * std::numbers::pi_v<float> * 2.f;
const f32 swap = rad_gyro.x; const f32 swap = rad_gyro[0];
rad_gyro.x = rad_gyro.y; rad_gyro[0] = rad_gyro[1];
rad_gyro.y = -swap; rad_gyro[1] = -swap;
rad_gyro.z = -rad_gyro.z; rad_gyro[2] = -rad_gyro[2];
// Clear gyro values if there is no gyro present // Clear gyro values if there is no gyro present
if (only_accelerometer) { if (only_accelerometer) {
rad_gyro.x = 0; rad_gyro[0] = 0;
rad_gyro.y = 0; rad_gyro[1] = 0;
rad_gyro.z = 0; rad_gyro[2] = 0;
} }
// Ignore drift correction if acceleration is not reliable // Ignore drift correction if acceleration is not reliable
if (accel.Length() >= 0.75f && accel.Length() <= 1.25f) { if (accel.Length() >= 0.75f && accel.Length() <= 1.25f) {
const f32 ax = -normal_accel.x; const f32 ax = -normal_accel[0];
const f32 ay = normal_accel.y; const f32 ay = normal_accel[1];
const f32 az = -normal_accel.z; const f32 az = -normal_accel[2];
// Estimated direction of gravity // Estimated direction of gravity
const f32 vx = 2.0f * (q2 * q4 - q1 * q3); const f32 vx = 2.0f * (q2 * q4 - q1 * q3);
@@ -174,7 +173,7 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) {
const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4; const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
// Error is cross product between estimated direction and measured direction of gravity // Error is cross product between estimated direction and measured direction of gravity
const Common::Vec3f new_real_error = { const Common::Vec<f32, 3> new_real_error{
az * vx - ax * vz, az * vx - ax * vz,
ay * vz - az * vy, ay * vz - az * vy,
ax * vy - ay * vx, ax * vy - ay * vx,
@@ -202,16 +201,16 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) {
rad_gyro += 10.0f * kd * derivative_error; rad_gyro += 10.0f * kd * derivative_error;
// Emulate gyro values for games that need them // Emulate gyro values for games that need them
gyro.x = -rad_gyro.y; gyro[0] = -rad_gyro[1];
gyro.y = rad_gyro.x; gyro[1] = rad_gyro[0];
gyro.z = -rad_gyro.z; gyro[2] = -rad_gyro[2];
UpdateRotation(elapsed_time); UpdateRotation(elapsed_time);
} }
} }
const f32 gx = rad_gyro.y; const f32 gx = rad_gyro[1];
const f32 gy = rad_gyro.x; const f32 gy = rad_gyro[0];
const f32 gz = rad_gyro.z; const f32 gz = rad_gyro[2];
// Integrate rate of change of quaternion // Integrate rate of change of quaternion
const f32 pa = q2; const f32 pa = q2;
@@ -222,57 +221,58 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) {
q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period); q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period);
q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period); q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period);
quat.w = q1; quat[3] = q1;
quat.xyz[0] = q2; quat[0] = q2;
quat.xyz[1] = q3; quat[1] = q3;
quat.xyz[2] = q4; quat[2] = q4;
quat = quat.Normalized(); quat = quat.Normalized();
} }
std::array<Common::Vec3f, 3> MotionInput::GetOrientation() const { std::array<Common::Vec<f32, 3>, 3> MotionInput::GetOrientation() const {
const Common::Quaternion<float> quad{ const Common::Vec<f32, 4> quad{
.xyz = {-quat.xyz[1], -quat.xyz[0], -quat.w}, -quat[1],
.w = -quat.xyz[2], -quat[0],
-quat[3],
-quat[2],
}; };
const std::array<float, 16> matrix4x4 = quad.ToMatrix(); const std::array<f32, 16> matrix4x4 = quad.ToMatrix();
return {Common::Vec<f32, 3>(matrix4x4[0], matrix4x4[1], -matrix4x4[2]),
return {Common::Vec3f(matrix4x4[0], matrix4x4[1], -matrix4x4[2]), Common::Vec<f32, 3>(matrix4x4[4], matrix4x4[5], -matrix4x4[6]),
Common::Vec3f(matrix4x4[4], matrix4x4[5], -matrix4x4[6]), Common::Vec<f32, 3>(-matrix4x4[8], -matrix4x4[9], matrix4x4[10])};
Common::Vec3f(-matrix4x4[8], -matrix4x4[9], matrix4x4[10])};
} }
Common::Vec3f MotionInput::GetAcceleration() const { Common::Vec<f32, 3> MotionInput::GetAcceleration() const {
return accel; return accel;
} }
Common::Vec3f MotionInput::GetGyroscope() const { Common::Vec<f32, 3> MotionInput::GetGyroscope() const {
return gyro; return gyro;
} }
Common::Vec3f MotionInput::GetGyroBias() const { Common::Vec<f32, 3> MotionInput::GetGyroBias() const {
return gyro_bias; return gyro_bias;
} }
Common::Quaternion<f32> MotionInput::GetQuaternion() const { Common::Vec<f32, 4> MotionInput::GetQuaternion() const {
return quat; return quat;
} }
Common::Vec3f MotionInput::GetRotations() const { Common::Vec<f32, 3> MotionInput::GetRotations() const {
return rotations; return rotations;
} }
Common::Vec3f MotionInput::GetEulerAngles() const { Common::Vec<f32, 3> MotionInput::GetEulerAngles() const {
// roll (x-axis rotation) // roll (x-axis rotation)
const float sinr_cosp = 2 * (quat.w * quat.xyz.x + quat.xyz.y * quat.xyz.z); const float sinr_cosp = 2 * (quat[3] * quat[0] + quat[1] * quat[2]);
const float cosr_cosp = 1 - 2 * (quat.xyz.x * quat.xyz.x + quat.xyz.y * quat.xyz.y); const float cosr_cosp = 1 - 2 * (quat[0] * quat[0] + quat[1] * quat[1]);
// pitch (y-axis rotation) // pitch (y-axis rotation)
const float sinp = std::sqrt(1 + 2 * (quat.w * quat.xyz.y - quat.xyz.x * quat.xyz.z)); const float sinp = std::sqrt(1 + 2 * (quat[3] * quat[1] - quat[0] * quat[2]));
const float cosp = std::sqrt(1 - 2 * (quat.w * quat.xyz.y - quat.xyz.x * quat.xyz.z)); const float cosp = std::sqrt(1 - 2 * (quat[3] * quat[1] - quat[0] * quat[2]));
// yaw (z-axis rotation) // yaw (z-axis rotation)
const float siny_cosp = 2 * (quat.w * quat.xyz.z + quat.xyz.x * quat.xyz.y); const float siny_cosp = 2 * (quat[3] * quat[2] + quat[0] * quat[1]);
const float cosy_cosp = 1 - 2 * (quat.xyz.y * quat.xyz.y + quat.xyz.z * quat.xyz.z); const float cosy_cosp = 1 - 2 * (quat[1] * quat[1] + quat[2] * quat[2]);
return { return {
std::atan2(sinr_cosp, cosr_cosp), std::atan2(sinr_cosp, cosr_cosp),
@@ -285,13 +285,13 @@ void MotionInput::ResetOrientation() {
if (!reset_enabled || only_accelerometer) { if (!reset_enabled || only_accelerometer) {
return; return;
} }
if (!IsMoving(IsAtRestRelaxed) && accel.z <= -0.9f) { if (!IsMoving(IsAtRestRelaxed) && accel[2] <= -0.9f) {
++reset_counter; ++reset_counter;
if (reset_counter > 900) { if (reset_counter > 900) {
quat.w = 0; quat[3] = 0;
quat.xyz[0] = 0; quat[0] = 0;
quat.xyz[1] = 0; quat[1] = 0;
quat.xyz[2] = -1; quat[2] = -1;
SetOrientationFromAccelerometer(); SetOrientationFromAccelerometer();
integral_error = {}; integral_error = {};
reset_counter = 0; reset_counter = 0;
@@ -309,15 +309,15 @@ void MotionInput::SetOrientationFromAccelerometer() {
while (!IsCalibrated(0.01f) && ++iterations < 100) { while (!IsCalibrated(0.01f) && ++iterations < 100) {
// Short name local variable for readability // Short name local variable for readability
f32 q1 = quat.w; f32 q1 = quat[3];
f32 q2 = quat.xyz[0]; f32 q2 = quat[0];
f32 q3 = quat.xyz[1]; f32 q3 = quat[1];
f32 q4 = quat.xyz[2]; f32 q4 = quat[2];
Common::Vec3f rad_gyro; Common::Vec<f32, 3> rad_gyro;
const f32 ax = -normal_accel.x; const f32 ax = -normal_accel[0];
const f32 ay = normal_accel.y; const f32 ay = normal_accel[1];
const f32 az = -normal_accel.z; const f32 az = -normal_accel[2];
// Estimated direction of gravity // Estimated direction of gravity
const f32 vx = 2.0f * (q2 * q4 - q1 * q3); const f32 vx = 2.0f * (q2 * q4 - q1 * q3);
@@ -325,7 +325,7 @@ void MotionInput::SetOrientationFromAccelerometer() {
const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4; const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
// Error is cross product between estimated direction and measured direction of gravity // Error is cross product between estimated direction and measured direction of gravity
const Common::Vec3f new_real_error = { const Common::Vec<f32, 3> new_real_error = {
az * vx - ax * vz, az * vx - ax * vz,
ay * vz - az * vy, ay * vz - az * vy,
ax * vy - ay * vx, ax * vy - ay * vx,
@@ -338,9 +338,9 @@ void MotionInput::SetOrientationFromAccelerometer() {
rad_gyro += 5.0f * ki * integral_error; rad_gyro += 5.0f * ki * integral_error;
rad_gyro += 10.0f * kd * derivative_error; rad_gyro += 10.0f * kd * derivative_error;
const f32 gx = rad_gyro.y; const f32 gx = rad_gyro[1];
const f32 gy = rad_gyro.x; const f32 gy = rad_gyro[0];
const f32 gz = rad_gyro.z; const f32 gz = rad_gyro[2];
// Integrate rate of change of quaternion // Integrate rate of change of quaternion
const f32 pa = q2; const f32 pa = q2;
@@ -351,10 +351,10 @@ void MotionInput::SetOrientationFromAccelerometer() {
q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period); q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period);
q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period); q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period);
quat.w = q1; quat[3] = q1;
quat.xyz[0] = q2; quat[0] = q2;
quat.xyz[1] = q3; quat[1] = q3;
quat.xyz[2] = q4; quat[2] = q4;
quat = quat.Normalized(); quat = quat.Normalized();
} }
} }
+23 -21
View File
@@ -1,10 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include "common/common_types.h" #include "common/common_types.h"
#include "common/quaternion.h"
#include "common/vector_math.h" #include "common/vector_math.h"
namespace Core::HID { namespace Core::HID {
@@ -34,11 +36,11 @@ public:
MotionInput& operator=(MotionInput&&) = default; MotionInput& operator=(MotionInput&&) = default;
void SetPID(f32 new_kp, f32 new_ki, f32 new_kd); void SetPID(f32 new_kp, f32 new_ki, f32 new_kd);
void SetAcceleration(const Common::Vec3f& acceleration); void SetAcceleration(const Common::Vec<f32, 3>& acceleration);
void SetGyroscope(const Common::Vec3f& gyroscope); void SetGyroscope(const Common::Vec<f32, 3>& gyroscope);
void SetQuaternion(const Common::Quaternion<f32>& quaternion); void SetQuaternion(const Common::Vec<f32, 4>& quaternion);
void SetEulerAngles(const Common::Vec3f& euler_angles); void SetEulerAngles(const Common::Vec<f32, 3>& euler_angles);
void SetGyroBias(const Common::Vec3f& bias); void SetGyroBias(const Common::Vec<f32, 3>& bias);
void SetGyroThreshold(f32 threshold); void SetGyroThreshold(f32 threshold);
/// Applies a modifier on top of the normal gyro threshold /// Applies a modifier on top of the normal gyro threshold
@@ -53,13 +55,13 @@ public:
void Calibrate(); void Calibrate();
[[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const; [[nodiscard]] std::array<Common::Vec<f32, 3>, 3> GetOrientation() const;
[[nodiscard]] Common::Vec3f GetAcceleration() const; [[nodiscard]] Common::Vec<f32, 3> GetAcceleration() const;
[[nodiscard]] Common::Vec3f GetGyroscope() const; [[nodiscard]] Common::Vec<f32, 3> GetGyroscope() const;
[[nodiscard]] Common::Vec3f GetGyroBias() const; [[nodiscard]] Common::Vec<f32, 3> GetGyroBias() const;
[[nodiscard]] Common::Vec3f GetRotations() const; [[nodiscard]] Common::Vec<f32, 3> GetRotations() const;
[[nodiscard]] Common::Quaternion<f32> GetQuaternion() const; [[nodiscard]] Common::Vec<f32, 4> GetQuaternion() const;
[[nodiscard]] Common::Vec3f GetEulerAngles() const; [[nodiscard]] Common::Vec<f32, 3> GetEulerAngles() const;
[[nodiscard]] bool IsMoving(f32 sensitivity) const; [[nodiscard]] bool IsMoving(f32 sensitivity) const;
[[nodiscard]] bool IsCalibrated(f32 sensitivity) const; [[nodiscard]] bool IsCalibrated(f32 sensitivity) const;
@@ -75,24 +77,24 @@ private:
f32 kd; f32 kd;
// PID errors // PID errors
Common::Vec3f real_error; Common::Vec<f32, 3> real_error;
Common::Vec3f integral_error; Common::Vec<f32, 3> integral_error;
Common::Vec3f derivative_error; Common::Vec<f32, 3> derivative_error;
// Quaternion containing the device orientation // Quaternion containing the device orientation
Common::Quaternion<f32> quat; Common::Vec<f32, 4> quat;
// Number of full rotations in each axis // Number of full rotations in each axis
Common::Vec3f rotations; Common::Vec<f32, 3> rotations;
// Acceleration vector measurement in G force // Acceleration vector measurement in G force
Common::Vec3f accel; Common::Vec<f32, 3> accel;
// Gyroscope vector measurement in radians/s. // Gyroscope vector measurement in radians/s.
Common::Vec3f gyro; Common::Vec<f32, 3> gyro;
// Vector to be subtracted from gyro measurements // Vector to be subtracted from gyro measurements
Common::Vec3f gyro_bias; Common::Vec<f32, 3> gyro_bias;
// Minimum gyro amplitude to detect if the device is moving // Minimum gyro amplitude to detect if the device is moving
f32 gyro_threshold = 0.0f; f32 gyro_threshold = 0.0f;
+7 -4
View File
@@ -1,3 +1,6 @@
// 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 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -602,10 +605,10 @@ static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an
struct SixAxisSensorState { struct SixAxisSensorState {
s64 delta_time{}; s64 delta_time{};
s64 sampling_number{}; s64 sampling_number{};
Common::Vec3f accel{}; Common::Vec<f32, 3> accel{};
Common::Vec3f gyro{}; Common::Vec<f32, 3> gyro{};
Common::Vec3f rotation{}; Common::Vec<f32, 3> rotation{};
std::array<Common::Vec3f, 3> orientation{}; std::array<Common::Vec<f32, 3>, 3> orientation{};
SixAxisSensorAttribute attribute{}; SixAxisSensorAttribute attribute{};
INSERT_PADDING_BYTES(4); // Reserved INSERT_PADDING_BYTES(4); // Reserved
}; };
@@ -28,10 +28,7 @@ enum class NpadMcuState : u32 {
struct NpadMcuHolder { struct NpadMcuHolder {
NpadMcuState state; NpadMcuState state;
INSERT_PADDING_BYTES(0x4); INSERT_PADDING_BYTES(0x4);
union { IAbstractedPad* abstracted_pad;
IAbstractedPad* abstracted_pad;
u64 abstracted_pad_raw;
};
}; };
static_assert(sizeof(NpadMcuHolder) == 0x10, "NpadMcuHolder is an invalid size"); static_assert(sizeof(NpadMcuHolder) == 0x10, "NpadMcuHolder is an invalid size");
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -196,7 +196,7 @@ struct ConsoleSixAxisSensorSharedMemoryFormat {
bool is_seven_six_axis_sensor_at_rest{}; bool is_seven_six_axis_sensor_at_rest{};
INSERT_PADDING_BYTES(3); // padding INSERT_PADDING_BYTES(3); // padding
f32 verticalization_error{}; f32 verticalization_error{};
Common::Vec3f gyro_bias{}; Common::Vec<f32, 3> gyro_bias{};
INSERT_PADDING_BYTES(4); // padding INSERT_PADDING_BYTES(4); // padding
}; };
static_assert(sizeof(ConsoleSixAxisSensorSharedMemoryFormat) == 0x20, static_assert(sizeof(ConsoleSixAxisSensorSharedMemoryFormat) == 0x20,
@@ -33,15 +33,9 @@ public:
bool is_created{}; bool is_created{};
bool is_mapped{}; bool is_mapped{};
INSERT_PADDING_BYTES(0x5); INSERT_PADDING_BYTES(0x5);
union { Kernel::KSharedMemory* shared_memory;
Kernel::KSharedMemory* shared_memory = nullptr;
u64 shared_memory_raw;
};
INSERT_PADDING_BYTES(0x38); INSERT_PADDING_BYTES(0x38);
union { SharedMemoryFormat* address = nullptr;
SharedMemoryFormat* address = nullptr;
u64 address_raw;
};
}; };
// Correct size is 0x50 bytes // Correct size is 0x50 bytes
static_assert(sizeof(SharedMemoryHolder) == 0x50, "SharedMemoryHolder is an invalid size"); static_assert(sizeof(SharedMemoryHolder) == 0x50, "SharedMemoryHolder is an invalid size");
@@ -46,14 +46,11 @@ void SevenSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
next_seven_sixaxis_state.accel = motion_status.accel; next_seven_sixaxis_state.accel = motion_status.accel;
next_seven_sixaxis_state.gyro = motion_status.gyro; next_seven_sixaxis_state.gyro = motion_status.gyro;
next_seven_sixaxis_state.quaternion = { next_seven_sixaxis_state.quaternion = {
{ motion_status.quaternion[1],
motion_status.quaternion.xyz.y, motion_status.quaternion[0],
motion_status.quaternion.xyz.x, -motion_status.quaternion[3],
-motion_status.quaternion.w, -motion_status.quaternion[2],
},
-motion_status.quaternion.xyz.z,
}; };
seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state); seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state);
transfer_memory_owner->GetMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo, transfer_memory_owner->GetMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo,
sizeof(seven_sixaxis_lifo)); sizeof(seven_sixaxis_lifo));
@@ -7,7 +7,7 @@
#pragma once #pragma once
#include "common/common_types.h" #include "common/common_types.h"
#include "common/quaternion.h" #include "common/vector_math.h"
#include "common/typed_address.h" #include "common/typed_address.h"
#include "hid_core/resources/controller_base.h" #include "hid_core/resources/controller_base.h"
#include "hid_core/resources/ring_lifo.h" #include "hid_core/resources/ring_lifo.h"
@@ -51,9 +51,9 @@ private:
u64 timestamp{}; u64 timestamp{};
u64 sampling_number{}; u64 sampling_number{};
u64 unknown{}; u64 unknown{};
Common::Vec3f accel{}; Common::Vec<f32, 3> accel{};
Common::Vec3f gyro{}; Common::Vec<f32, 3> gyro{};
Common::Quaternion<f32> quaternion{}; Common::Vec<f32, 4> quaternion{};
}; };
static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size"); static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size");
+6 -3
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
@@ -93,9 +96,9 @@ void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
.accel = {0, 0, -1.0f}, .accel = {0, 0, -1.0f},
.orientation = .orientation =
{ {
Common::Vec3f{1.0f, 0, 0}, Common::Vec<f32, 3>{1.0f, 0, 0},
Common::Vec3f{0, 1.0f, 0}, Common::Vec<f32, 3>{0, 1.0f, 0},
Common::Vec3f{0, 0, 1.0f}, Common::Vec<f32, 3>{0, 0, 1.0f},
}, },
.attribute = {1}, .attribute = {1},
}; };
+4 -6
View File
@@ -15,6 +15,8 @@ add_library(input_common STATIC
drivers/tas_input.h drivers/tas_input.h
drivers/touch_screen.cpp drivers/touch_screen.cpp
drivers/touch_screen.h drivers/touch_screen.h
drivers/udp_client.cpp
drivers/udp_client.h
drivers/virtual_amiibo.cpp drivers/virtual_amiibo.cpp
drivers/virtual_amiibo.h drivers/virtual_amiibo.h
drivers/virtual_gamepad.cpp drivers/virtual_gamepad.cpp
@@ -32,12 +34,8 @@ add_library(input_common STATIC
input_poller.cpp input_poller.cpp
input_poller.h input_poller.h
main.cpp main.cpp
main.h) main.h
if (NOT EMSCRIPTEN) )
target_sources(input_common PRIVATE
drivers/udp_client.cpp
drivers/udp_client.h)
endif()
if (MSVC) if (MSVC)
target_compile_options(input_common PRIVATE target_compile_options(input_common PRIVATE
+1 -1
View File
@@ -114,7 +114,7 @@ std::set<s32> Android::GetDeviceAxes(JNIEnv* env, jobject& j_device) const {
std::set<s32> axes; std::set<s32> axes;
for (int i = 0; i < env->GetArrayLength(j_axes); ++i) { for (int i = 0; i < env->GetArrayLength(j_axes); ++i) {
jobject axis = env->GetObjectArrayElement(j_axes, i); jobject axis = env->GetObjectArrayElement(j_axes, i);
axes.insert(env->GetIntField(axis, Common::Android::GetIntegerValueField())); axes.insert(env->CallIntMethod(axis, Common::Android::GetIntegerValueMethod()));
} }
return axes; return axes;
} }
+36 -43
View File
@@ -88,8 +88,8 @@ void Mouse::UpdateStickInput() {
last_mouse_change *= maximum_stick_range; last_mouse_change *= maximum_stick_range;
} }
SetAxis(identifier, mouse_axis_x, last_mouse_change.x); SetAxis(identifier, mouse_axis_x, last_mouse_change[0]);
SetAxis(identifier, mouse_axis_y, -last_mouse_change.y); SetAxis(identifier, mouse_axis_y, -last_mouse_change[1]);
// Decay input over time // Decay input over time
const float clamped_length = (std::min)(1.0f, length); const float clamped_length = (std::min)(1.0f, length);
@@ -104,20 +104,20 @@ void Mouse::UpdateMotionInput() {
const float sensitivity = const float sensitivity =
IsMousePanningEnabled() ? default_motion_panning_sensitivity : default_motion_sensitivity; IsMousePanningEnabled() ? default_motion_panning_sensitivity : default_motion_sensitivity;
const float rotation_velocity = std::sqrt(last_motion_change.x * last_motion_change.x + const float rotation_velocity = std::sqrt(last_motion_change[0] * last_motion_change[0] +
last_motion_change.y * last_motion_change.y); last_motion_change[1] * last_motion_change[1]);
// Clamp rotation speed // Clamp rotation speed
if (rotation_velocity > maximum_rotation_speed / sensitivity) { if (rotation_velocity > maximum_rotation_speed / sensitivity) {
const float multiplier = maximum_rotation_speed / rotation_velocity / sensitivity; const float multiplier = maximum_rotation_speed / rotation_velocity / sensitivity;
last_motion_change.x = last_motion_change.x * multiplier; last_motion_change[0] = last_motion_change[0] * multiplier;
last_motion_change.y = last_motion_change.y * multiplier; last_motion_change[1] = last_motion_change[1] * multiplier;
} }
const BasicMotion motion_data{ const BasicMotion motion_data{
.gyro_x = last_motion_change.x * sensitivity, .gyro_x = last_motion_change[0] * sensitivity,
.gyro_y = last_motion_change.y * sensitivity, .gyro_y = last_motion_change[1] * sensitivity,
.gyro_z = last_motion_change.z * sensitivity, .gyro_z = last_motion_change[2] * sensitivity,
.accel_x = 0, .accel_x = 0,
.accel_y = 0, .accel_y = 0,
.accel_z = 0, .accel_z = 0,
@@ -125,53 +125,46 @@ void Mouse::UpdateMotionInput() {
}; };
if (IsMousePanningEnabled()) { if (IsMousePanningEnabled()) {
last_motion_change.x = 0; last_motion_change[0] = 0;
last_motion_change.y = 0; last_motion_change[1] = 0;
} }
last_motion_change.z = 0; last_motion_change[2] = 0;
SetMotion(motion_identifier, 0, motion_data); SetMotion(motion_identifier, 0, motion_data);
} }
void Mouse::Move(int x, int y, int center_x, int center_y) { void Mouse::Move(int x, int y, int center_x, int center_y) {
if (IsMousePanningEnabled()) { if (IsMousePanningEnabled()) {
const auto mouse_change = auto const mouse_change_int = Common::Vec<int, 2>(x, y) - Common::Vec<int, 2>(center_x, center_y);
(Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast<float>(); auto const mouse_change = Common::Vec<float, 2>(float(mouse_change_int[0]), float(mouse_change_int[1]));
const float x_sensitivity = auto const x_sensitivity = Settings::values.mouse_panning_x_sensitivity.GetValue() * default_panning_sensitivity;
Settings::values.mouse_panning_x_sensitivity.GetValue() * default_panning_sensitivity; auto const y_sensitivity = Settings::values.mouse_panning_y_sensitivity.GetValue() * default_panning_sensitivity;
const float y_sensitivity = auto const deadzone_cw = Settings::values.mouse_panning_deadzone_counterweight.GetValue() * default_deadzone_counterweight;
Settings::values.mouse_panning_y_sensitivity.GetValue() * default_panning_sensitivity; last_motion_change += {-mouse_change[1] * x_sensitivity, -mouse_change[0] * y_sensitivity, 0};
const float deadzone_counterweight = last_mouse_change[0] += mouse_change[0] * x_sensitivity;
Settings::values.mouse_panning_deadzone_counterweight.GetValue() * last_mouse_change[1] += mouse_change[1] * y_sensitivity;
default_deadzone_counterweight; // Bind the mouse change to [0 <= deadzone_cw <= 1.0]
last_motion_change += {-mouse_change.y * x_sensitivity, -mouse_change.x * y_sensitivity, 0};
last_mouse_change.x += mouse_change.x * x_sensitivity;
last_mouse_change.y += mouse_change.y * y_sensitivity;
// Bind the mouse change to [0 <= deadzone_counterweight <= 1.0]
const float length = last_mouse_change.Length(); const float length = last_mouse_change.Length();
if (length < deadzone_counterweight && length != 0.0f) { if (length < deadzone_cw && length != 0.0f) {
last_mouse_change /= length; last_mouse_change /= length;
last_mouse_change *= deadzone_counterweight; last_mouse_change *= deadzone_cw;
} }
return; return;
} }
if (button_pressed) { if (button_pressed) {
const auto mouse_move = Common::MakeVec<int>(x, y) - mouse_origin; const auto mouse_move = Common::Vec<int, 2>(x, y) - mouse_origin;
const float x_sensitivity = const float x_sensitivity =
Settings::values.mouse_panning_x_sensitivity.GetValue() * default_stick_sensitivity; Settings::values.mouse_panning_x_sensitivity.GetValue() * default_stick_sensitivity;
const float y_sensitivity = const float y_sensitivity =
Settings::values.mouse_panning_y_sensitivity.GetValue() * default_stick_sensitivity; Settings::values.mouse_panning_y_sensitivity.GetValue() * default_stick_sensitivity;
SetAxis(identifier, mouse_axis_x, static_cast<float>(mouse_move.x) * x_sensitivity); SetAxis(identifier, mouse_axis_x, float(mouse_move[0]) * x_sensitivity);
SetAxis(identifier, mouse_axis_y, static_cast<float>(-mouse_move.y) * y_sensitivity); SetAxis(identifier, mouse_axis_y, float(-mouse_move[1]) * y_sensitivity);
last_motion_change = { last_motion_change = {
static_cast<float>(-mouse_move.y) * x_sensitivity, float(-mouse_move[1]) * x_sensitivity,
static_cast<float>(-mouse_move.x) * y_sensitivity, float(-mouse_move[0]) * y_sensitivity,
last_motion_change.z, last_motion_change[2],
}; };
} }
} }
@@ -220,18 +213,18 @@ void Mouse::ReleaseButton(MouseButton button) {
SetAxis(identifier, mouse_axis_y, 0); SetAxis(identifier, mouse_axis_y, 0);
} }
last_motion_change.x = 0; last_motion_change[0] = 0;
last_motion_change.y = 0; last_motion_change[1] = 0;
button_pressed = false; button_pressed = false;
} }
void Mouse::MouseWheelChange(int x, int y) { void Mouse::MouseWheelChange(int x, int y) {
wheel_position.x += x; wheel_position[0] += x;
wheel_position.y += y; wheel_position[1] += y;
last_motion_change.z += static_cast<f32>(y); last_motion_change[2] += static_cast<f32>(y);
SetAxis(identifier, wheel_axis_x, static_cast<f32>(wheel_position.x)); SetAxis(identifier, wheel_axis_x, static_cast<f32>(wheel_position[0]));
SetAxis(identifier, wheel_axis_y, static_cast<f32>(wheel_position.y)); SetAxis(identifier, wheel_axis_y, static_cast<f32>(wheel_position[1]));
} }
void Mouse::ReleaseAllButtons() { void Mouse::ReleaseAllButtons() {
+5 -5
View File
@@ -107,11 +107,11 @@ private:
Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const; Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
Common::Vec2<int> mouse_origin; Common::Vec<int, 2> mouse_origin;
Common::Vec2<int> last_mouse_position; Common::Vec<int, 2> last_mouse_position;
Common::Vec2<float> last_mouse_change; Common::Vec<float, 2> last_mouse_change;
Common::Vec3<float> last_motion_change; Common::Vec<float, 3> last_motion_change;
Common::Vec2<int> wheel_position; Common::Vec<int, 2> wheel_position;
bool button_pressed = false; bool button_pressed = false;
}; };
+1 -21
View File
@@ -12,6 +12,7 @@
#include "input_common/drivers/mouse.h" #include "input_common/drivers/mouse.h"
#include "input_common/drivers/tas_input.h" #include "input_common/drivers/tas_input.h"
#include "input_common/drivers/touch_screen.h" #include "input_common/drivers/touch_screen.h"
#include "input_common/drivers/udp_client.h"
#include "input_common/drivers/virtual_amiibo.h" #include "input_common/drivers/virtual_amiibo.h"
#include "input_common/drivers/virtual_gamepad.h" #include "input_common/drivers/virtual_gamepad.h"
#include "input_common/helpers/stick_from_buttons.h" #include "input_common/helpers/stick_from_buttons.h"
@@ -20,9 +21,6 @@
#include "input_common/input_mapping.h" #include "input_common/input_mapping.h"
#include "input_common/input_poller.h" #include "input_common/input_poller.h"
#include "input_common/main.h" #include "input_common/main.h"
#ifndef __EMSCRIPTEN__
#include "input_common/drivers/udp_client.h"
#endif
#ifdef ENABLE_LIBUSB #ifdef ENABLE_LIBUSB
#include "input_common/drivers/gc_adapter.h" #include "input_common/drivers/gc_adapter.h"
@@ -84,9 +82,7 @@ struct InputSubsystem::Impl {
#ifdef ENABLE_LIBUSB #ifdef ENABLE_LIBUSB
RegisterEngine("gcpad", gcadapter); RegisterEngine("gcpad", gcadapter);
#endif #endif
#ifndef __EMSCRIPTEN__
RegisterEngine("cemuhookudp", udp_client); RegisterEngine("cemuhookudp", udp_client);
#endif
RegisterEngine("tas", tas_input); RegisterEngine("tas", tas_input);
RegisterEngine("camera", camera); RegisterEngine("camera", camera);
#ifdef __ANDROID__ #ifdef __ANDROID__
@@ -120,9 +116,7 @@ struct InputSubsystem::Impl {
#ifdef ENABLE_LIBUSB #ifdef ENABLE_LIBUSB
UnregisterEngine(gcadapter); UnregisterEngine(gcadapter);
#endif #endif
#ifndef __EMSCRIPTEN__
UnregisterEngine(udp_client); UnregisterEngine(udp_client);
#endif
UnregisterEngine(tas_input); UnregisterEngine(tas_input);
UnregisterEngine(camera); UnregisterEngine(camera);
#ifdef __ANDROID__ #ifdef __ANDROID__
@@ -158,10 +152,8 @@ struct InputSubsystem::Impl {
auto gcadapter_devices = gcadapter->GetInputDevices(); auto gcadapter_devices = gcadapter->GetInputDevices();
devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end()); devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end());
#endif #endif
#ifndef __EMSCRIPTEN__
auto udp_devices = udp_client->GetInputDevices(); auto udp_devices = udp_client->GetInputDevices();
devices.insert(devices.end(), udp_devices.begin(), udp_devices.end()); devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
#endif
#ifdef HAVE_SDL3 #ifdef HAVE_SDL3
auto joycon_devices = joycon->GetInputDevices(); auto joycon_devices = joycon->GetInputDevices();
devices.insert(devices.end(), joycon_devices.begin(), joycon_devices.end()); devices.insert(devices.end(), joycon_devices.begin(), joycon_devices.end());
@@ -194,11 +186,9 @@ struct InputSubsystem::Impl {
return gcadapter; return gcadapter;
} }
#endif #endif
#ifndef __EMSCRIPTEN__
if (engine == udp_client->GetEngineName()) { if (engine == udp_client->GetEngineName()) {
return udp_client; return udp_client;
} }
#endif
#ifdef HAVE_SDL3 #ifdef HAVE_SDL3
if (engine == sdl->GetEngineName()) { if (engine == sdl->GetEngineName()) {
return sdl; return sdl;
@@ -281,11 +271,9 @@ struct InputSubsystem::Impl {
return true; return true;
} }
#endif #endif
#ifndef __EMSCRIPTEN__
if (engine == udp_client->GetEngineName()) { if (engine == udp_client->GetEngineName()) {
return true; return true;
} }
#endif
if (engine == tas_input->GetEngineName()) { if (engine == tas_input->GetEngineName()) {
return true; return true;
} }
@@ -312,9 +300,7 @@ struct InputSubsystem::Impl {
#ifdef ENABLE_LIBUSB #ifdef ENABLE_LIBUSB
gcadapter->BeginConfiguration(); gcadapter->BeginConfiguration();
#endif #endif
#ifndef __EMSCRIPTEN__
udp_client->BeginConfiguration(); udp_client->BeginConfiguration();
#endif
#ifdef HAVE_SDL3 #ifdef HAVE_SDL3
sdl->BeginConfiguration(); sdl->BeginConfiguration();
joycon->BeginConfiguration(); joycon->BeginConfiguration();
@@ -330,9 +316,7 @@ struct InputSubsystem::Impl {
#ifdef ENABLE_LIBUSB #ifdef ENABLE_LIBUSB
gcadapter->EndConfiguration(); gcadapter->EndConfiguration();
#endif #endif
#ifndef __EMSCRIPTEN__
udp_client->EndConfiguration(); udp_client->EndConfiguration();
#endif
#ifdef HAVE_SDL3 #ifdef HAVE_SDL3
sdl->EndConfiguration(); sdl->EndConfiguration();
joycon->EndConfiguration(); joycon->EndConfiguration();
@@ -357,9 +341,7 @@ struct InputSubsystem::Impl {
std::shared_ptr<Mouse> mouse; std::shared_ptr<Mouse> mouse;
std::shared_ptr<TouchScreen> touch_screen; std::shared_ptr<TouchScreen> touch_screen;
std::shared_ptr<TasInput::Tas> tas_input; std::shared_ptr<TasInput::Tas> tas_input;
#ifndef __EMSCRIPTEN__
std::shared_ptr<CemuhookUDP::UDPClient> udp_client; std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
#endif
std::shared_ptr<Camera> camera; std::shared_ptr<Camera> camera;
std::shared_ptr<VirtualAmiibo> virtual_amiibo; std::shared_ptr<VirtualAmiibo> virtual_amiibo;
std::shared_ptr<VirtualGamepad> virtual_gamepad; std::shared_ptr<VirtualGamepad> virtual_gamepad;
@@ -488,9 +470,7 @@ bool InputSubsystem::IsStickInverted(const Common::ParamPackage& params) const {
} }
void InputSubsystem::ReloadInputDevices() { void InputSubsystem::ReloadInputDevices() {
#ifndef __EMSCRIPTEN__
impl->udp_client.get()->ReloadSockets(); impl->udp_client.get()->ReloadSockets();
#endif
} }
void InputSubsystem::BeginMapping(Polling::InputType type) { void InputSubsystem::BeginMapping(Polling::InputType type) {
+2 -1
View File
@@ -257,7 +257,8 @@ else()
# Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6. # Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6.
# And this in turns limits the size of a std::array. # And this in turns limits the size of a std::array.
$<$<CXX_COMPILER_ID:Clang>:-fbracket-depth=1024> $<$<CXX_COMPILER_ID:Clang>:-fbracket-depth=1024>
$<$<CXX_COMPILER_ID:AppleClang>:-fbracket-depth=1024>) $<$<CXX_COMPILER_ID:AppleClang>:-fbracket-depth=1024>
)
endif() endif()
create_target_directory_groups(shader_recompiler) create_target_directory_groups(shader_recompiler)
+1 -1
View File
@@ -31,7 +31,7 @@ struct CbufWordKey {
struct CbufWordKeyHash { struct CbufWordKeyHash {
constexpr size_t operator()(const CbufWordKey& k) const noexcept { constexpr size_t operator()(const CbufWordKey& k) const noexcept {
return size_t((u64(k.index) << 32) ^ u64(k.offset)); return (size_t(k.index) << 32) ^ k.offset;
} }
}; };
@@ -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 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -160,14 +157,16 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
unsigned Swizzle(u64 insn) { unsigned Swizzle(u64 insn) {
const Encoding texs{insn}; const Encoding texs{insn};
u8 const encoding = u8(texs.swizzle); const size_t encoding{texs.swizzle};
if (texs.dest_reg_b == IR::Reg::RZ) { if (texs.dest_reg_b == IR::Reg::RZ) {
if (encoding >= RG_LUT.size()) if (encoding >= RG_LUT.size()) {
throw NotImplementedException("Illegal RG encoding {}", encoding); throw NotImplementedException("Illegal RG encoding {}", encoding);
}
return RG_LUT[encoding]; return RG_LUT[encoding];
} else { } else {
if (encoding >= RGBA_LUT.size()) if (encoding >= RGBA_LUT.size()) {
throw NotImplementedException("Illegal RGBA encoding {}", encoding); throw NotImplementedException("Illegal RGBA encoding {}", encoding);
}
return RGBA_LUT[encoding]; return RGBA_LUT[encoding];
} }
} }
@@ -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 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -145,14 +142,16 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
unsigned Swizzle(u64 insn) { unsigned Swizzle(u64 insn) {
const Encoding tlds{insn}; const Encoding tlds{insn};
u8 const encoding = u8(tlds.swizzle); const size_t encoding{tlds.swizzle};
if (tlds.dest_reg_b == IR::Reg::RZ) { if (tlds.dest_reg_b == IR::Reg::RZ) {
if (encoding >= RG_LUT.size()) if (encoding >= RG_LUT.size()) {
throw NotImplementedException("Illegal RG encoding {}", encoding); throw NotImplementedException("Illegal RG encoding {}", encoding);
}
return RG_LUT[encoding]; return RG_LUT[encoding];
} else { } else {
if (encoding >= RGBA_LUT.size()) if (encoding >= RGBA_LUT.size()) {
throw NotImplementedException("Illegal RGBA encoding {}", encoding); throw NotImplementedException("Illegal RGBA encoding {}", encoding);
}
return RGBA_LUT[encoding]; return RGBA_LUT[encoding];
} }
} }
@@ -2936,10 +2936,10 @@ void PlayerControlPreview::DrawArrow(QPainter& p, const QPointF center, const Di
} }
// Draw motion functions // Draw motion functions
void PlayerControlPreview::Draw3dCube(QPainter& p, QPointF center, const Common::Vec3f& euler, void PlayerControlPreview::Draw3dCube(QPainter& p, QPointF center, const Common::Vec<f32, 3>& euler,
float size) { float size) {
std::array<Common::Vec3f, 8> cube{ std::array<Common::Vec<f32, 3>, 8> cube{
Common::Vec3f{-0.7f, -1, -0.5f}, Common::Vec<f32, 3>{-0.7f, -1, -0.5f},
{-0.7f, 1, -0.5f}, {-0.7f, 1, -0.5f},
{0.7f, 1, -0.5f}, {0.7f, 1, -0.5f},
{0.7f, -1, -0.5f}, {0.7f, -1, -0.5f},
@@ -2949,30 +2949,38 @@ void PlayerControlPreview::Draw3dCube(QPainter& p, QPointF center, const Common:
{0.7f, -1, 0.5f}, {0.7f, -1, 0.5f},
}; };
for (Common::Vec3f& point : cube) { for (Common::Vec<f32, 3>& point : cube) {
point.RotateFromOrigin(euler.x, euler.y, euler.z); float temp = point[1];
point[1] = std::cos(euler[0]) * point[1] - std::sin(euler[0]) * point[2];
point[2] = std::sin(euler[0]) * temp + std::cos(euler[0]) * point[2];
temp = point[0];
point[0] = std::cos(euler[1]) * point[0] + std::sin(euler[1]) * point[2];
point[2] = -std::sin(euler[1]) * temp + std::cos(euler[1]) * point[2];
temp = point[0];
point[0] = std::cos(euler[2]) * point[0] - std::sin(euler[2]) * point[1];
point[1] = std::sin(euler[2]) * temp + std::cos(euler[2]) * point[1];
point *= size; point *= size;
} }
const std::array<QPointF, 4> front_face{ const std::array<QPointF, 4> front_face{
center + QPointF{cube[0].x, cube[0].y}, center + QPointF{cube[0][0], cube[0][1]},
center + QPointF{cube[1].x, cube[1].y}, center + QPointF{cube[1][0], cube[1][1]},
center + QPointF{cube[2].x, cube[2].y}, center + QPointF{cube[2][0], cube[2][1]},
center + QPointF{cube[3].x, cube[3].y}, center + QPointF{cube[3][0], cube[3][1]},
}; };
const std::array<QPointF, 4> back_face{ const std::array<QPointF, 4> back_face{
center + QPointF{cube[4].x, cube[4].y}, center + QPointF{cube[4][0], cube[4][1]},
center + QPointF{cube[5].x, cube[5].y}, center + QPointF{cube[5][0], cube[5][1]},
center + QPointF{cube[6].x, cube[6].y}, center + QPointF{cube[6][0], cube[6][1]},
center + QPointF{cube[7].x, cube[7].y}, center + QPointF{cube[7][0], cube[7][1]},
}; };
DrawPolygon(p, front_face); DrawPolygon(p, front_face);
DrawPolygon(p, back_face); DrawPolygon(p, back_face);
p.drawLine(center + QPointF{cube[0].x, cube[0].y}, center + QPointF{cube[4].x, cube[4].y}); p.drawLine(center + QPointF{cube[0][0], cube[0][1]}, center + QPointF{cube[4][0], cube[4][1]});
p.drawLine(center + QPointF{cube[1].x, cube[1].y}, center + QPointF{cube[5].x, cube[5].y}); p.drawLine(center + QPointF{cube[1][0], cube[1][1]}, center + QPointF{cube[5][0], cube[5][1]});
p.drawLine(center + QPointF{cube[2].x, cube[2].y}, center + QPointF{cube[6].x, cube[6].y}); p.drawLine(center + QPointF{cube[2][0], cube[2][1]}, center + QPointF{cube[6][0], cube[6][1]});
p.drawLine(center + QPointF{cube[3].x, cube[3].y}, center + QPointF{cube[7].x, cube[7].y}); p.drawLine(center + QPointF{cube[3][0], cube[3][1]}, center + QPointF{cube[7][0], cube[7][1]});
} }
template <size_t N> template <size_t N>
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -198,7 +198,7 @@ private:
void DrawArrow(QPainter& p, QPointF center, Direction direction, float size); void DrawArrow(QPainter& p, QPointF center, Direction direction, float size);
// Draw motion functions // Draw motion functions
void Draw3dCube(QPainter& p, QPointF center, const Common::Vec3f& euler, float size); void Draw3dCube(QPainter& p, QPointF center, const Common::Vec<f32, 3>& euler, float size);
// Draw primitive types // Draw primitive types
template <size_t N> template <size_t N>
-12
View File
@@ -75,15 +75,3 @@ if (NOT MSVC)
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-unused-parameter> $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-unused-parameter>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-missing-field-initializers>) $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-missing-field-initializers>)
endif() endif()
if (EMSCRIPTEN)
# 10GB is required at max... yikes!
target_link_options(yuzu-cmd PRIVATE
-sALLOW_MEMORY_GROWTH=1
-sINITIAL_MEMORY=33554432
-sMAXIMUM_MEMORY=10737418240
-sGLOBAL_BASE=16777216
-sEXPORTED_RUNTIME_METHODS=['FS']
-sPTHREAD_POOL_SIZE_STRICT=0
-sPTHREAD_POOL_SIZE=navigator.hardwareConcurrency)
endif()
-1
View File
@@ -30,7 +30,6 @@ Tools for Eden and other subprojects. When adding new scripts please use `#!/bin
- `find-unused-strings.sh`: Find any unused strings in the Android app (XML -> Kotlin). - `find-unused-strings.sh`: Find any unused strings in the Android app (XML -> Kotlin).
- `cpp-lint.sh`: Homemade dumb C++ linter. - `cpp-lint.sh`: Homemade dumb C++ linter.
- `fuzzsettings.cpp`: Fuzz settings files. - `fuzzsettings.cpp`: Fuzz settings files.
- `miniserver.js`: Make a quick server that serves a page with the WASM on it, takes a single argument which is the path to the build directory containing *both* `eden-cli.js` and `eden-cli.wasm`. Run via `node.js`, `wasmtime` isn't supported.
## Android ## Android
-127
View File
@@ -1,127 +0,0 @@
#!/usr/bin/env node
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
import { createServer } from 'http';
import { readFile } from 'fs';
import { join } from 'path';
console.log(`dont forget to run: "npm --global install @jdmichaud/dwarf-2-sourcemap" for better debugging!`);
const server = createServer((req, res) => {
console.log(`get ${req.url}`);
if (req.url === '/') {
// https://developer.mozilla.org/en-US/docs/WebAssembly/Guides/Loading_and_running
// If your browser doesn't support fetch... HAHA GET FUCKED
res.writeHead(200, {
'Content-Type': 'text/html',
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp'
});
res.end(`<!DOCTYPE html>
<html>
<head>
<title>eden-cli</title>
</head>
<body style="margin:0;padding:0;background-color:black;color:white;font-family:Monospace,Tahoma,Arial;">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:2px;width:100%;height:100vh;">
<canvas id="canvas" oncontextmenu="event.preventDefault()" style="width:100%;height:100%;background-color:gray;"></canvas>
<div id="tty-stdout"></div>
</div>
<script>
var Module = { //do not prepend var
mainScriptUrlOrBlob: 'eden-cli.js',
arguments: ['--null-render', '--singlecore', '--filter', '*:Trace', '/home/web_user/game.nro'],
canvas: document.getElementById('canvas'),
print: (e) => {
e = e.replace('[1;31m', '<span style="color:red;font-weight:bold;">');
e = e.replace('[0;37m', '<span style="color:white;font-weight:bold;">');
e = e.replace('[1;35m', '<span style="color:pink;font-weight:bold;">');
e = e.replace('[1;33m', '<span style="color:yellow;font-weight:bold;">');
e = e.replace('[0;36m', '<span style="color:white;font-weight:bold;">');
e = e.replace('[0m', '</span>');
document.getElementById('tty-stdout').innerHTML += \`\${e}</br>\`;
},
printErr: (e) => {
document.getElementById('tty-stdout').innerHTML += \`<span style="color:red">\${e}</span></br>\`;
},
// not a wasm func but idc
printInternal: (e) => {
document.getElementById('tty-stdout').innerHTML += \`<span style="color:white">Internal WASM: \${e}</span></br>\`;
},
preInit: [
() => {
// copy just most relevant :)
Module.FS.mkdir('/home/web_user/.local');
Module.FS.mkdir('/home/web_user/.local/share');
Module.FS.mkdir('/home/web_user/.local/share/eden');
Module.FS.mkdir('/home/web_user/.config');
Module.FS.mkdir('/home/web_user/.config/eden');
Module.FS.createDataFile('/home/web_user', 'game.nro', gameNroFileBuffer, true, false, true);
}
],
onRuntimeInitialized: () => { Module.printInternal("runtime ok"); },
setStatus: (e) => { Module.printInternal(e); },
monitorRunDependencies: (e) => { Module.printInternal("monitor deps: " + e); },
__wasm_call_ctors: () => { Module.printInternal("ctors beep"); },
};
var gameNroFileBuffer = {};
Module.printInternal(\`Atomics: \${window.Atomics}, SharedArrayBuffer: \${window.SharedArrayBuffer}\`);
Module.printInternal("trying to load script (if it hangs here check console)");
fetch('game.nro').then((resp) => {
if (!resp.ok)
throw Error(\`\${resp.status}\`);
return resp.bytes();
}).then((buffer) => {
gameNroFileBuffer = buffer;
// load the thingy AFTER loading the nro
Module.printInternal(\`loading from ${build_dir}/\${Module.mainScriptUrlOrBlob}\`);
var script = document.createElement('script');
script.src = '/eden-cli.js';
script.onload = (e) => Module.printInternal(\`loaded WASMy script \${e}!!\`);
document.head.appendChild(script);
}).catch(Module.printErr);
</script>
</body>
</html>`);
} else if (req.url === '/eden-cli.js') {
readFile(join(build_dir, 'eden-cli.js'), (err, content) => {
res.writeHead(200, {
'Content-Type': 'application/javascript',
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp'
});
res.end(content, 'utf-8');
});
} else if (req.url === '/eden-cli.wasm') {
readFile(join(build_dir, 'eden-cli.wasm'), (err, content) => {
res.writeHead(200, {
'Content-Type': 'application/wasm',
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp'
});
res.end(content);
});
} else if (req.url === '/game.nro') {
readFile(nro_file, (err, content) => {
res.writeHead(200, {
'Content-Type': 'application/octet-stream',
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp'
});
res.end(content);
});
} else {
res.writeHead(404, {});
res.end('', 'utf-8');
}
});
const build_dir = process.argv[2];
const nro_file = process.argv[3];
if (typeof build_dir == "undefined" || typeof nro_file == "undefined") {
console.log(`Usage: ${process.argv[0]} ${process.argv[1]} [build directory] [NRO file]`);
} else {
server.listen(2210, () => {
console.log(`${process.argv[0]} ${process.argv[1]} http://localhost:2210`);
console.log(`build dir = ${build_dir}`);
});
}