mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-21 07:18:33 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 16b55f5802 | |||
| 44f6229b26 | |||
| bb66fbea51 | |||
| 97cc97b933 | |||
| 5d6897edac | |||
| 8217304b60 | |||
| 689f4d3214 | |||
| bdbdfe8e1d | |||
| d25d460116 | |||
| 06c8f9806b | |||
| aa012961b8 | |||
| d628737b48 | |||
| d5cc29a2f7 | |||
| 894add43f3 | |||
| d142b5dd6a | |||
| 0c2894eabf |
@@ -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
|
||||
@@ -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}
|
||||
@@ -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)
|
||||
+5
-22
@@ -79,7 +79,6 @@ set(YUZU_QT_MIRROR "" CACHE STRING "What mirror to use for downloading the bundl
|
||||
cmake_dependent_option(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSVC}" "ENABLE_QT" OFF)
|
||||
|
||||
option(ENABLE_DEBUG_TOOLS "Enable debugging tools (maxwell disassembler, SPIRV translator, etc)" OFF)
|
||||
option(ENABLE_WERROR "Enable -Werror diagnostics" OFF)
|
||||
|
||||
# non-linux bundled qt are static
|
||||
if (YUZU_USE_BUNDLED_QT AND (APPLE OR NOT UNIX))
|
||||
@@ -381,15 +380,6 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||
# Prefer the -pthread flag on Linux.
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
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 (PLATFORM_EMSCRIPTEN)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-pthread>)
|
||||
add_link_options($<$<COMPILE_LANGUAGE:C,CXX>:-pthread>)
|
||||
endif()
|
||||
|
||||
find_package(RenderDoc MODULE)
|
||||
|
||||
@@ -418,12 +408,7 @@ set(BUILD_TESTING OFF)
|
||||
set(ENABLE_TESTING OFF)
|
||||
|
||||
# boost
|
||||
if (PLATFORM_EMSCRIPTEN)
|
||||
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()
|
||||
set(BOOST_INCLUDE_LIBRARIES algorithm icl pool container heap asio headers process filesystem crc variant)
|
||||
|
||||
AddJsonPackage(boost)
|
||||
|
||||
@@ -444,12 +429,10 @@ if (Boost_ADDED)
|
||||
|
||||
target_compile_options(boost_heap 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)
|
||||
if (TARGET boost_asio)
|
||||
target_compile_options(boost_asio INTERFACE
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-conversion>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-implicit-fallthrough>)
|
||||
endif()
|
||||
target_compile_options(boost_asio INTERFACE
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-conversion>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-implicit-fallthrough>
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
+3
-5
@@ -121,7 +121,7 @@
|
||||
"find_args": "MODULE GLOBAL",
|
||||
"hash": "159ed94965018f2a371d45a3bfc1961e5fb1549e501ded70a6b4532d7fe99d0579c18b5195aff6e35f96f399b426cea2650ec9fb75ef80d4c9edeccb51f2e6c9",
|
||||
"options": [
|
||||
"HTTPLIB_REQUIRE_OPENSSL OFF",
|
||||
"HTTPLIB_REQUIRE_OPENSSL ON",
|
||||
"HTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES ON"
|
||||
],
|
||||
"patches": [
|
||||
@@ -207,8 +207,7 @@
|
||||
"min_version": "3.0.0",
|
||||
"package": "OpenSSL",
|
||||
"patches": [
|
||||
"0001-add-bundled-cert.patch",
|
||||
"0002-wasm-support.patch"
|
||||
"0001-add-bundled-cert.patch"
|
||||
],
|
||||
"repo": "openssl/openssl",
|
||||
"tag": "openssl-%VERSION%",
|
||||
@@ -232,8 +231,7 @@
|
||||
"0001-cpmutil-compat.patch",
|
||||
"0002-use-ccache.patch",
|
||||
"0003-use-cmake-compiler-flags.patch",
|
||||
"0004-use-shell-wrapper.patch",
|
||||
"0005-wasm-support.patch"
|
||||
"0004-use-shell-wrapper.patch"
|
||||
],
|
||||
"repo": "jimmy-park/openssl-cmake",
|
||||
"tag": "%VERSION%",
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
- [NetBSD](#netbsd)
|
||||
- [MSYS2](#msys2)
|
||||
- [RedoxOS](#redoxos)
|
||||
- [WebAssembly](#webassembly)
|
||||
- [Windows](#windows)
|
||||
- [Windows 7, Windows 8 and Windows 8.1](#windows-7-windows-8-and-windows-81)
|
||||
- [Windows Vista and below](#windows-vista-and-below)
|
||||
@@ -246,22 +245,6 @@ The package install may randomly hang at times, in which case it has to be resta
|
||||
|
||||
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.
|
||||
|
||||
## Windows
|
||||
|
||||
### Windows 7, Windows 8 and Windows 8.1
|
||||
|
||||
@@ -359,14 +359,6 @@ pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel b
|
||||
|
||||
[Caveats](./Caveats.md#haikuos).
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>WebAssembly</summary>
|
||||
|
||||
Emscripten: The default installation should provide enough.
|
||||
|
||||
[Caveats](./Caveats.md#wasm).
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>RedoxOS</summary>
|
||||
|
||||
@@ -27,6 +27,3 @@ There are two main applications, an SDL-based app (`eden-cli`) and a Qt based ap
|
||||
- `--user/-u`: Specify the user index.
|
||||
- `--version/-v`: Display version and quit.
|
||||
- `--input-profile/-i`: Specifies input profile name to use (for player #0 only).
|
||||
- `--null-render/-n`: Forces the usage of the "Null" render backend irrespective of settings.
|
||||
- `--filter/-x`: Sets the debug log filter irrespective of settings.
|
||||
- `--singlecore/-s`: Forces single-core regardless of settings.
|
||||
|
||||
+5
-9
@@ -94,10 +94,6 @@ function(detect_architecture_symbols)
|
||||
endfunction()
|
||||
|
||||
# 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
|
||||
# mips is pretty popular in embedded
|
||||
# ppc64 is pretty popular in supercomputing
|
||||
@@ -105,11 +101,6 @@ endfunction()
|
||||
# ia64 exists
|
||||
# the rest exist, but are probably less popular than ia64
|
||||
|
||||
detect_architecture_symbols(
|
||||
ARCH wasm
|
||||
SYMBOLS
|
||||
"__EMSCRIPTEN__")
|
||||
|
||||
detect_architecture_symbols(
|
||||
ARCH arm64
|
||||
SYMBOLS
|
||||
@@ -212,6 +203,11 @@ detect_architecture_symbols(
|
||||
"__loongarch__"
|
||||
"__loongarch64")
|
||||
|
||||
detect_architecture_symbols(
|
||||
ARCH wasm
|
||||
SYMBOLS
|
||||
"__EMSCRIPTEN__")
|
||||
|
||||
# "generic" target
|
||||
# If you have reached this point, you're on some as-of-yet unsupported architecture.
|
||||
# See the docs up above for known unsupported architectures
|
||||
|
||||
+2
-4
@@ -33,9 +33,6 @@ elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Haiku")
|
||||
set(PLATFORM_HAIKU ON)
|
||||
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(PLATFORM_LINUX ON)
|
||||
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten")
|
||||
set(PLATFORM_EMSCRIPTEN ON)
|
||||
message(WARNING "${CMAKE_LIBRARY_ARCHITECTURE} support is highly experimental!!!")
|
||||
endif()
|
||||
|
||||
# dumb heuristic to detect msys2
|
||||
@@ -154,5 +151,6 @@ endif()
|
||||
|
||||
# awesome
|
||||
if (PLATFORM_FREEBSD OR PLATFORM_DRAGONFLYBSD)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/local/lib")
|
||||
set(CMAKE_EXE_LINKER_FLAGS
|
||||
"${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/local/lib")
|
||||
endif()
|
||||
|
||||
Vendored
+26
-58
@@ -37,39 +37,26 @@ if (NOT YUZU_USE_BUNDLED_FFMPEG)
|
||||
elseif (NOT (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES CMAKE_SYSTEM_PROCESSOR
|
||||
AND CMAKE_HOST_SYSTEM_NAME MATCHES CMAKE_SYSTEM_NAME))
|
||||
string(TOLOWER "${CMAKE_SYSTEM_NAME}" FFmpeg_SYSTEM_NAME)
|
||||
# All of these platforms are supported by ffmpeg as native build OSes
|
||||
# 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 (PLATFORM_NETBSD OR PLATFORM_SUN OR PLATFORM_FREEBSD
|
||||
OR PLATFORM_OPENBSD OR PLATFORM_DRAGONFLYBSD OR PLATFORM_HAIKU
|
||||
OR PLATFORM_LINUX OR PLATFORM_MSYS OR WIN32 OR ANDROID OR APPLE))
|
||||
if (FFmpeg_SYSTEM_NAME STREQUAL "openorbis" OR FFmpeg_SYSTEM_NAME STREQUAL "managarm")
|
||||
set(FFmpeg_SYSTEM_NAME "none")
|
||||
endif()
|
||||
# TODO: Can we really do better? Auto-detection? Something clever?
|
||||
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS
|
||||
--enable-cross-compile
|
||||
--arch="${CMAKE_SYSTEM_PROCESSOR}"
|
||||
--target-os="${FFmpeg_SYSTEM_NAME}")
|
||||
if (PLATFORM_EMSCRIPTEN)
|
||||
# funniest trolling from emscripten, such a classic!
|
||||
# maybe I should PR so they use CMAKE_SYSROOT... y'know?
|
||||
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --sysroot="${EMSCRIPTEN_SYSROOT}")
|
||||
else()
|
||||
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --sysroot="${CMAKE_SYSROOT}")
|
||||
endif()
|
||||
--target-os="${FFmpeg_SYSTEM_NAME}"
|
||||
--sysroot="${CMAKE_SYSROOT}"
|
||||
)
|
||||
if (DEFINED FFmpeg_CROSS_PREFIX)
|
||||
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --cross-prefix="${FFmpeg_CROSS_PREFIX}")
|
||||
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()
|
||||
set(FFmpeg_IS_CROSS_COMPILING TRUE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (PLATFORM_PS4 OR PLATFORM_MANAGARM OR PLATFORM_EMSCRIPTEN)
|
||||
if (PLATFORM_PS4 OR PLATFORM_MANAGARM)
|
||||
# Doesn't support VA-API, don't go thru the embarrassment of trying to enable it
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vaapi)
|
||||
elseif (UNIX AND NOT DEFINED FFmpeg_IS_CROSS_COMPILING AND NOT ANDROID)
|
||||
@@ -172,28 +159,20 @@ if (PLATFORM_PS4)
|
||||
-lSceUserService
|
||||
-lSceSysmodule
|
||||
-lSceNet
|
||||
-lSceLibcInternal)
|
||||
-lSceLibcInternal
|
||||
)
|
||||
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS
|
||||
--disable-pthreads
|
||||
--extra-cflags=${CMAKE_SYSROOT}/usr/include
|
||||
--extra-cxxflags=${CMAKE_SYSROOT}/usr/include
|
||||
--extra-libs="${FFmpeg_CROSS_COMPILE_LIBS}")
|
||||
--extra-libs="${FFmpeg_CROSS_COMPILE_LIBS}"
|
||||
)
|
||||
elseif (PLATFORM_MANAGARM)
|
||||
# Required for proper stuff
|
||||
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS
|
||||
--disable-pthreads
|
||||
--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})
|
||||
--extra-libs="${FFmpeg_CROSS_COMPILE_LIBS}"
|
||||
)
|
||||
endif()
|
||||
|
||||
if (YUZU_USE_BUNDLED_FFMPEG)
|
||||
@@ -223,7 +202,6 @@ else()
|
||||
# Build FFmpeg from externals
|
||||
message(STATUS "Using FFmpeg from externals")
|
||||
|
||||
set(FFmpeg_LD ${CMAKE_LINKER})
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86_64|amd64)")
|
||||
# FFmpeg has source that requires one of nasm or yasm to assemble it.
|
||||
# REQUIRED throws an error if not found here during configuration rather than during compilation.
|
||||
@@ -232,12 +210,6 @@ else()
|
||||
message(FATAL_ERROR "One of either `nasm` or `yasm` not found but is required.")
|
||||
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)
|
||||
if ("${AUTOCONF}" STREQUAL "AUTOCONF-NOTFOUND")
|
||||
@@ -271,13 +243,7 @@ else()
|
||||
CACHE PATH "Paths to FFmpeg libraries" FORCE)
|
||||
endforeach()
|
||||
|
||||
# Some SDKs (especially wasm) require us, actually, WANT us to use their "emconfigure"
|
||||
# otherwise they will cry a billion tears
|
||||
if (PLATFORM_EMSCRIPTEN)
|
||||
find_program(FFmpeg_CONFIGURE_WRAPPER emconfigure REQUIRED)
|
||||
else()
|
||||
find_program(FFmpeg_CONFIGURE_WRAPPER bash REQUIRED)
|
||||
endif()
|
||||
find_program(BASH_PROGRAM bash REQUIRED)
|
||||
|
||||
# `configure` parameters builds only exactly what yuzu needs from FFmpeg
|
||||
# `--disable-vdpau` is needed to avoid linking issues
|
||||
@@ -287,7 +253,7 @@ else()
|
||||
OUTPUT
|
||||
${FFmpeg_MAKEFILE}
|
||||
COMMAND
|
||||
${FFmpeg_CONFIGURE_WRAPPER} ${FFmpeg_PREFIX}/configure
|
||||
${BASH_PROGRAM} ${FFmpeg_PREFIX}/configure
|
||||
--disable-avdevice
|
||||
--disable-avformat
|
||||
--disable-doc
|
||||
@@ -296,10 +262,6 @@ else()
|
||||
--disable-ffprobe
|
||||
--disable-network
|
||||
--disable-swresample
|
||||
--disable-autodetect
|
||||
--disable-runtime-cpudetect
|
||||
--disable-debug
|
||||
--disable-programs
|
||||
--enable-decoder=h264
|
||||
--enable-decoder=vp8
|
||||
--enable-decoder=vp9
|
||||
@@ -307,7 +269,7 @@ else()
|
||||
--enable-pic
|
||||
--cc=${FFmpeg_CC}
|
||||
--cxx=${FFmpeg_CXX}
|
||||
--ld=${FFmpeg_LD}
|
||||
--ld=${CMAKE_LINKER}
|
||||
--extra-cflags=${CMAKE_C_FLAGS}
|
||||
--extra-cxxflags=${CMAKE_CXX_FLAGS}
|
||||
--extra-ldflags=${CMAKE_C_LINK_FLAGS}
|
||||
@@ -323,20 +285,26 @@ else()
|
||||
|
||||
# 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.
|
||||
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})
|
||||
|
||||
find_program(MAKE NAMES gmake make REQUIRED)
|
||||
if (SYSTEM_THREADS GREATER 1)
|
||||
set(FFmpeg_MAKE_ARGS -j ${SYSTEM_THREADS})
|
||||
# BSD make or Solaris make don't support ffmpeg make-j8
|
||||
if (PLATFORM_LINUX OR ANDROID OR APPLE OR WIN32 OR PLATFORM_FREEBSD)
|
||||
set(FFmpeg_MAKE_ARGS -j${SYSTEM_THREADS})
|
||||
else()
|
||||
set(FFmpeg_MAKE_ARGS "")
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${FFmpeg_BUILD_LIBRARIES}
|
||||
COMMAND
|
||||
${MAKE} ${FFmpeg_MAKE_ARGS}
|
||||
gmake ${FFmpeg_MAKE_ARGS}
|
||||
WORKING_DIRECTORY
|
||||
${FFmpeg_BUILD_DIR}
|
||||
)
|
||||
|
||||
Vendored
-2
@@ -14,9 +14,7 @@
|
||||
namespace Tz {
|
||||
|
||||
namespace {
|
||||
#ifndef EINVAL
|
||||
#define EINVAL 22
|
||||
#endif
|
||||
|
||||
static Rule gmtmem{};
|
||||
static Rule* const gmtptr = &gmtmem;
|
||||
|
||||
+10
-16
@@ -155,25 +155,23 @@ else()
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-fno-rtti>)
|
||||
endif()
|
||||
|
||||
if (ENABLE_WERROR)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-Werror>)
|
||||
endif()
|
||||
add_compile_options(
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wall>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wextra>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wmissing-declarations>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wshadow>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wunused>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=all>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=extra>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=missing-declarations>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=shadow>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=unused>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-attributes>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-invalid-offsetof>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-unused-parameter>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-missing-field-initializers>)
|
||||
|
||||
if (CXX_CLANG OR CXX_ICC OR CXX_APPLE) # Clang, AppleClang, or Intel C++
|
||||
if (NOT MSVC)
|
||||
add_compile_options(
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wshadow-uncaptured-local>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wimplicit-fallthrough>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wtype-limits>)
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=shadow-uncaptured-local>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=implicit-fallthrough>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=type-limits>)
|
||||
endif()
|
||||
add_compile_options(
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-braced-scalar-init>
|
||||
@@ -181,11 +179,7 @@ else()
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-nullability-completeness>)
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_wasm)
|
||||
# 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)
|
||||
if (ARCHITECTURE_x86_64)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-mcx16>)
|
||||
if (PLATFORM_LINUX OR PLATFORM_FREEBSD)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-mtls-dialect=gnu2>)
|
||||
|
||||
+1
@@ -31,6 +31,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
|
||||
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
|
||||
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
|
||||
ENABLE_GPU_BUFFER_READBACK("enable_gpu_buffer_readback"),
|
||||
SYNC_MEMORY_OPERATIONS("sync_memory_operations"),
|
||||
BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
|
||||
RENDERER_DEBUG("debug"),
|
||||
|
||||
+7
@@ -806,6 +806,13 @@ abstract class SettingsItem(
|
||||
descriptionId = R.string.enable_buffer_history_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.ENABLE_GPU_BUFFER_READBACK,
|
||||
titleId = R.string.enable_gpu_buffer_readback,
|
||||
descriptionId = R.string.enable_gpu_buffer_readback_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS,
|
||||
|
||||
+1
@@ -292,6 +292,7 @@ class SettingsFragmentPresenter(
|
||||
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
|
||||
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
|
||||
add(BooleanSetting.ENABLE_BUFFER_HISTORY.key)
|
||||
add(BooleanSetting.ENABLE_GPU_BUFFER_READBACK.key)
|
||||
add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key)
|
||||
|
||||
add(HeaderSetting(R.string.hacks))
|
||||
|
||||
@@ -503,6 +503,8 @@
|
||||
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
|
||||
<string name="enable_buffer_history">Enable buffer history</string>
|
||||
<string name="enable_buffer_history_description">Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games.</string>
|
||||
<string name="enable_gpu_buffer_readback">Enable GPU Buffer Readback</string>
|
||||
<string name="enable_gpu_buffer_readback_description">Preserves GPU-modified buffer data by reading it back before uploads. Some games require this to render certain effects properly. May cause issues if the hardware cannot handle the additional workload.</string>
|
||||
<string name="use_optimized_vertex_buffers">Optimized Vertex Buffers</string>
|
||||
<string name="use_optimized_vertex_buffers_description">Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers/ QCOM drivers. Will crash on older Turnip drivers (25.3 and below).</string>
|
||||
|
||||
|
||||
@@ -222,7 +222,7 @@ if (MSVC)
|
||||
)
|
||||
else()
|
||||
target_compile_options(audio_core PRIVATE
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-sign-conversion>)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ endif()
|
||||
if(CXX_CLANG)
|
||||
target_compile_options(common PRIVATE
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-fsized-deallocation>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wunreachable-code-aggressive>)
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=unreachable-code-aggressive>)
|
||||
target_compile_definitions(
|
||||
common
|
||||
PRIVATE
|
||||
@@ -235,11 +235,7 @@ else()
|
||||
target_link_libraries(common PUBLIC Boost::headers)
|
||||
endif()
|
||||
target_link_libraries(common PRIVATE OpenSSL::SSL)
|
||||
target_link_libraries(common PUBLIC Boost::filesystem httplib::httplib nlohmann_json::nlohmann_json)
|
||||
if (NOT PLATFORM_EMSCRIPTEN)
|
||||
# Emscripten is: "bring your own implementation", boost doesn't add upstream support sadly
|
||||
target_link_libraries(common PRIVATE Boost::context)
|
||||
endif()
|
||||
target_link_libraries(common PUBLIC Boost::filesystem Boost::context httplib::httplib nlohmann_json::nlohmann_json)
|
||||
|
||||
if (lz4_ADDED)
|
||||
target_include_directories(common PRIVATE ${lz4_SOURCE_DIR}/lib)
|
||||
|
||||
+11
-84
@@ -11,12 +11,7 @@
|
||||
#include "common/fiber.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#include <emscripten/fiber.h>
|
||||
#else
|
||||
#include <boost/context/detail/fcontext.hpp>
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
@@ -27,103 +22,36 @@ constexpr size_t DEFAULT_STACK_SIZE = 512 * 4096;
|
||||
#endif
|
||||
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 {
|
||||
FiberImpl() {}
|
||||
|
||||
u32 canary_1 = CANARY_VALUE;
|
||||
std::array<u8, DEFAULT_STACK_SIZE> stack{};
|
||||
std::array<u8, DEFAULT_STACK_SIZE> rewind_stack{};
|
||||
u32 canary_2 = CANARY_VALUE;
|
||||
|
||||
boost::context::detail::fcontext_t context{};
|
||||
boost::context::detail::fcontext_t rewind_context{};
|
||||
|
||||
std::mutex guard;
|
||||
std::function<void()> entry_point;
|
||||
std::function<void()> rewind_point;
|
||||
std::shared_ptr<Fiber> previous_fiber;
|
||||
|
||||
u8* stack_limit = nullptr;
|
||||
u8* rewind_stack_limit = nullptr;
|
||||
bool is_thread_fiber = 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>()} {
|
||||
impl->entry_point = std::move(entry_point_func);
|
||||
impl->stack_limit = impl->stack.data();
|
||||
impl->rewind_stack_limit = impl->rewind_stack.data();
|
||||
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 {
|
||||
auto* fiber = static_cast<Fiber*>(transfer.data);
|
||||
@@ -144,7 +72,7 @@ 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");
|
||||
ASSERT_MSG(locked, "Destroying a fiber that's still running");
|
||||
if (locked) {
|
||||
impl->guard.unlock();
|
||||
}
|
||||
@@ -152,7 +80,7 @@ Fiber::~Fiber() {
|
||||
}
|
||||
|
||||
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) {
|
||||
impl->guard.unlock();
|
||||
impl->released = true;
|
||||
@@ -182,6 +110,5 @@ std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
|
||||
fiber->impl->is_thread_fiber = true;
|
||||
return fiber;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Common
|
||||
|
||||
+2
-1
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
@@ -45,6 +45,7 @@ public:
|
||||
/// Fiber 'from' must be the currently running fiber.
|
||||
static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to);
|
||||
[[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
|
||||
void SetRewindPoint(std::function<void()>&& rewind_func);
|
||||
/// Only call from main thread's fiber
|
||||
void Exit();
|
||||
private:
|
||||
|
||||
@@ -136,11 +136,6 @@ public:
|
||||
eden_path = GetDataDirectory("XDG_DATA_HOME") / EDEN_DIR;
|
||||
eden_path_cache = GetDataDirectory("XDG_CACHE_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 {
|
||||
eden_path_cache = eden_path / CACHE_DIR;
|
||||
eden_path_config = eden_path / CONFIG_DIR;
|
||||
|
||||
@@ -394,7 +394,7 @@ private:
|
||||
ankerl::unordered_dense::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
|
||||
// For managarm: see https://github.com/managarm/managarm/issues/1370
|
||||
#else // ^^^ Windows ^^^ vvv POSIX vvv
|
||||
@@ -689,7 +689,7 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
|
||||
: backing_size(backing_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");
|
||||
fallback_buffer.emplace(backing_size);
|
||||
backing_base = fallback_buffer->data();
|
||||
@@ -723,7 +723,7 @@ HostMemory::HostMemory(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) {
|
||||
#if !(defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__))
|
||||
#if !(defined(__OPENORBIS__) || defined(__managarm__))
|
||||
ASSERT(virtual_offset % PageAlignment == 0);
|
||||
ASSERT(host_offset % 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) {
|
||||
#if !(defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__))
|
||||
#if !(defined(__OPENORBIS__) || defined(__managarm__))
|
||||
ASSERT(virtual_offset % PageAlignment == 0);
|
||||
ASSERT(length % PageAlignment == 0);
|
||||
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) {
|
||||
#if !(defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__))
|
||||
#if !(defined(__OPENORBIS__) || defined(__managarm__))
|
||||
ASSERT(virtual_offset % PageAlignment == 0);
|
||||
ASSERT(length % PageAlignment == 0);
|
||||
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() {
|
||||
#if !(defined(__OPENORBIS__) || defined(__managarm__) || defined(__wasi__) || defined(__EMSCRIPTEN__))
|
||||
#if !(defined(__OPENORBIS__) || defined(__managarm__))
|
||||
if (impl) {
|
||||
impl->EnableDirectMappedAddress();
|
||||
virtual_size += reinterpret_cast<uintptr_t>(virtual_base);
|
||||
|
||||
@@ -77,7 +77,7 @@ private:
|
||||
size_t backing_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
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <class Traits>
|
||||
class LeastRecentlyUsedCache {
|
||||
using ObjectType = typename Traits::ObjectType;
|
||||
using TickType = typename Traits::TickType;
|
||||
|
||||
struct Item {
|
||||
ObjectType obj;
|
||||
TickType tick;
|
||||
Item* next{};
|
||||
Item* prev{};
|
||||
};
|
||||
|
||||
public:
|
||||
LeastRecentlyUsedCache() : first_item{}, last_item{} {}
|
||||
~LeastRecentlyUsedCache() = default;
|
||||
|
||||
size_t Insert(ObjectType obj, TickType tick) {
|
||||
const auto new_id = Build();
|
||||
auto& item = item_pool[new_id];
|
||||
item.obj = obj;
|
||||
item.tick = tick;
|
||||
Attach(item);
|
||||
return new_id;
|
||||
}
|
||||
|
||||
void Touch(size_t id, TickType tick) {
|
||||
auto& item = item_pool[id];
|
||||
if (item.tick >= tick) {
|
||||
return;
|
||||
}
|
||||
item.tick = tick;
|
||||
if (&item == last_item) {
|
||||
return;
|
||||
}
|
||||
Detach(item);
|
||||
Attach(item);
|
||||
}
|
||||
|
||||
void Free(size_t id) {
|
||||
auto& item = item_pool[id];
|
||||
Detach(item);
|
||||
item.prev = nullptr;
|
||||
item.next = nullptr;
|
||||
free_items.push_back(id);
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
void ForEachItemBelow(TickType tick, Func&& func) {
|
||||
static constexpr bool RETURNS_BOOL =
|
||||
std::is_same_v<std::invoke_result_t<Func, ObjectType>, bool>;
|
||||
Item* iterator = first_item;
|
||||
while (iterator) {
|
||||
if (static_cast<s64>(tick) - static_cast<s64>(iterator->tick) < 0) {
|
||||
return;
|
||||
}
|
||||
Item* next = iterator->next;
|
||||
if constexpr (RETURNS_BOOL) {
|
||||
if (func(iterator->obj)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
func(iterator->obj);
|
||||
}
|
||||
iterator = next;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
size_t Build() {
|
||||
if (free_items.empty()) {
|
||||
const size_t item_id = item_pool.size();
|
||||
auto& item = item_pool.emplace_back();
|
||||
item.next = nullptr;
|
||||
item.prev = nullptr;
|
||||
return item_id;
|
||||
}
|
||||
const size_t item_id = free_items.front();
|
||||
free_items.pop_front();
|
||||
auto& item = item_pool[item_id];
|
||||
item.next = nullptr;
|
||||
item.prev = nullptr;
|
||||
return item_id;
|
||||
}
|
||||
|
||||
void Attach(Item& item) {
|
||||
if (!first_item) {
|
||||
first_item = &item;
|
||||
}
|
||||
if (!last_item) {
|
||||
last_item = &item;
|
||||
} else {
|
||||
item.prev = last_item;
|
||||
last_item->next = &item;
|
||||
item.next = nullptr;
|
||||
last_item = &item;
|
||||
}
|
||||
}
|
||||
|
||||
void Detach(Item& item) {
|
||||
if (item.prev) {
|
||||
item.prev->next = item.next;
|
||||
}
|
||||
if (item.next) {
|
||||
item.next->prev = item.prev;
|
||||
}
|
||||
if (&item == first_item) {
|
||||
first_item = item.next;
|
||||
if (first_item) {
|
||||
first_item->prev = nullptr;
|
||||
}
|
||||
}
|
||||
if (&item == last_item) {
|
||||
last_item = item.prev;
|
||||
if (last_item) {
|
||||
last_item->next = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::deque<Item> item_pool;
|
||||
std::deque<size_t> free_items;
|
||||
Item* first_item{};
|
||||
Item* last_item{};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -78,8 +78,6 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
|
||||
}
|
||||
}();
|
||||
set_thread_priority(find_thread(NULL), priority);
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
// TODO: set priority?
|
||||
#else
|
||||
pthread_t this_thread = pthread_self();
|
||||
const auto scheduling_type = SCHED_OTHER;
|
||||
@@ -129,8 +127,6 @@ void SetCurrentThreadName(const char* name) {
|
||||
// See for reference
|
||||
// https://gitlab.freedesktop.org/mesa/mesa/-/blame/main/src/util/u_thread.c?ref_type=heads#L75
|
||||
(void)name;
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
// TODO: set thread name?
|
||||
#else
|
||||
pthread_setname_np(pthread_self(), name);
|
||||
#endif
|
||||
|
||||
+6
-13
@@ -38,6 +38,10 @@ add_library(core STATIC
|
||||
debugger/debugger.cpp
|
||||
debugger/debugger.h
|
||||
debugger/debugger_interface.h
|
||||
debugger/gdbstub.cpp
|
||||
debugger/gdbstub.h
|
||||
debugger/gdbstub_arch.cpp
|
||||
debugger/gdbstub_arch.h
|
||||
device_memory.cpp
|
||||
device_memory.h
|
||||
device_memory_manager.h
|
||||
@@ -1166,14 +1170,6 @@ add_library(core STATIC
|
||||
tools/freezer.h
|
||||
tools/renderdoc.cpp
|
||||
tools/renderdoc.h)
|
||||
if (NOT PLATFORM_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)
|
||||
target_sources(core PRIVATE internal_network/wifi_scanner.cpp)
|
||||
@@ -1203,7 +1199,7 @@ if (MSVC)
|
||||
)
|
||||
else()
|
||||
target_compile_options(core PRIVATE
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-sign-conversion>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-cast-function-type>
|
||||
$<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>)
|
||||
@@ -1217,10 +1213,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)
|
||||
|
||||
if (BOOST_NO_HEADERS)
|
||||
target_link_libraries(core PUBLIC Boost::container Boost::heap Boost::crc)
|
||||
if (NOT PLATFORM_EMSCRIPTEN)
|
||||
target_link_libraries(core PUBLIC Boost::asio Boost::process)
|
||||
endif()
|
||||
target_link_libraries(core PUBLIC Boost::container Boost::heap Boost::asio Boost::process Boost::crc)
|
||||
else()
|
||||
target_link_libraries(core PUBLIC Boost::headers)
|
||||
endif()
|
||||
|
||||
@@ -6,20 +6,19 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#include <dynarmic/interface/halt_reason.h>
|
||||
#endif
|
||||
|
||||
#include "core/arm/arm_interface.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
constexpr Dynarmic::HaltReason StepThread = Dynarmic::HaltReason::Step;
|
||||
constexpr Dynarmic::HaltReason DataAbort = Dynarmic::HaltReason::MemoryAbort;
|
||||
constexpr Dynarmic::HaltReason BreakLoop = Dynarmic::HaltReason::UserDefined2;
|
||||
constexpr Dynarmic::HaltReason SupervisorCall = Dynarmic::HaltReason::UserDefined3;
|
||||
constexpr Dynarmic::HaltReason InstructionBreakpoint = Dynarmic::HaltReason::UserDefined4;
|
||||
constexpr Dynarmic::HaltReason PrefetchAbort = Dynarmic::HaltReason::UserDefined6;
|
||||
|
||||
constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) {
|
||||
static_assert(u64(HaltReason::StepThread) == u64(StepThread));
|
||||
static_assert(u64(HaltReason::DataAbort) == u64(DataAbort));
|
||||
@@ -29,6 +28,5 @@ constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) {
|
||||
static_assert(u64(HaltReason::PrefetchAbort) == u64(PrefetchAbort));
|
||||
return HaltReason(hr);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Core
|
||||
|
||||
+1
-1
@@ -350,7 +350,7 @@ struct System::Impl {
|
||||
|
||||
// Register with applet manager
|
||||
// All threads are started, begin main process execution, now that we're in the clear
|
||||
applet_manager.CreateAndInsertByFrontendAppletParameters(std::make_unique<Service::Process>(*std::move(process)), params);
|
||||
applet_manager.CreateAndInsertByFrontendAppletParameters(std::move(process), params);
|
||||
|
||||
if (Settings::values.gamecard_inserted) {
|
||||
if (Settings::values.gamecard_current_game) {
|
||||
|
||||
@@ -6,54 +6,30 @@
|
||||
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
|
||||
#if defined(__EMSCRIPTEN__) || defined(__wasi__)
|
||||
// TODO: gdb stub compat with emscripten?
|
||||
#else
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/version.hpp>
|
||||
|
||||
#if BOOST_VERSION > 108400 && (!defined(_WINDOWS) && !defined(__ANDROID__)) || defined(YUZU_BOOST_v1)
|
||||
#define USE_BOOST_v1
|
||||
#endif
|
||||
|
||||
#ifdef USE_BOOST_v1
|
||||
#include <boost/process/v1/async_pipe.hpp>
|
||||
#else
|
||||
#include <boost/process/async_pipe.hpp>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "common/logging.h"
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/core.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/gdbstub.h"
|
||||
#endif
|
||||
#include "core/hle/kernel/global_scheduler_context.h"
|
||||
#include "core/hle/kernel/k_process.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>
|
||||
static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
|
||||
static_assert(std::is_trivial_v<Buffer>);
|
||||
@@ -424,4 +400,3 @@ void Debugger::NotifyShutdown() {
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
#endif
|
||||
|
||||
@@ -7,14 +7,8 @@
|
||||
#include <random>
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/settings.h"
|
||||
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#include "core/arm/dynarmic/arm_dynarmic.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/hle/kernel/k_process.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_worker_task_manager.h"
|
||||
|
||||
#include "core/arm/dynarmic/arm_dynarmic_32.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic_64.h"
|
||||
#ifdef HAS_NCE
|
||||
#include "core/arm/nce/arm_nce.h"
|
||||
#endif
|
||||
@@ -1304,11 +1300,9 @@ void KProcess::LoadModule(KernelCore& kernel, CodeSet code_set, KProcessAddress
|
||||
}
|
||||
|
||||
void KProcess::InitializeInterfaces(KernelCore& kernel) {
|
||||
#ifdef __EMSCRIPTEN__
|
||||
ASSERT(false && "unimplemented");
|
||||
#else
|
||||
m_exclusive_monitor =
|
||||
Core::MakeExclusiveMonitor(this->GetMemory(), Core::Hardware::NUM_CPU_CORES);
|
||||
|
||||
#ifdef HAS_NCE
|
||||
if (this->IsApplication() && Settings::IsNceEnabled()) {
|
||||
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++)
|
||||
@@ -1328,7 +1322,6 @@ void KProcess::InitializeInterfaces(KernelCore& kernel) {
|
||||
static_cast<Core::DynarmicExclusiveMonitor&>(*m_exclusive_monitor), i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool KProcess::InsertWatchpoint(KernelCore& kernel, KProcessAddress addr, u64 size, DebugWatchpointType type) {
|
||||
|
||||
@@ -109,6 +109,10 @@ struct Applet {
|
||||
std::list<std::shared_ptr<Applet>> child_applets{};
|
||||
bool is_completed{};
|
||||
|
||||
std::shared_ptr<Applet> reserved_applet{};
|
||||
bool unwind_after_reserved{};
|
||||
bool is_winding{};
|
||||
|
||||
// Self state
|
||||
bool exit_locked{};
|
||||
s32 fatal_section_count{};
|
||||
|
||||
@@ -25,6 +25,13 @@ void AppletStorageChannel::Push(Kernel::KernelCore& kernel, std::shared_ptr<ISto
|
||||
m_event.Signal(kernel);
|
||||
}
|
||||
|
||||
void AppletStorageChannel::Unpop(Kernel::KernelCore& kernel, std::shared_ptr<IStorage> storage) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
m_data.emplace_front(std::move(storage));
|
||||
m_event.Signal(kernel);
|
||||
}
|
||||
|
||||
Result AppletStorageChannel::Pop(Kernel::KernelCore& kernel, std::shared_ptr<IStorage>* out_storage) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ public:
|
||||
~AppletStorageChannel();
|
||||
|
||||
void Push(Kernel::KernelCore& kernel, std::shared_ptr<IStorage> storage);
|
||||
void Unpop(Kernel::KernelCore& kernel, std::shared_ptr<IStorage> storage);
|
||||
Result Pop(Kernel::KernelCore& kernel, std::shared_ptr<IStorage>* out_storage);
|
||||
Kernel::KReadableEvent* GetEvent();
|
||||
|
||||
|
||||
@@ -267,7 +267,7 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) {
|
||||
|
||||
if (Settings::values.enable_overlay && m_window_system->GetOverlayDisplayApplet() == nullptr) {
|
||||
if (auto overlay_process = CreateProcess(m_system, static_cast<u64>(AppletProgramId::OverlayDisplay), 0, 0)) {
|
||||
auto overlay_applet = std::make_shared<Applet>(m_system, std::make_unique<Service::Process>(*std::move(overlay_process)), false);
|
||||
auto overlay_applet = std::make_shared<Applet>(m_system, std::move(overlay_process), false);
|
||||
overlay_applet->program_id = static_cast<u64>(AppletProgramId::OverlayDisplay);
|
||||
overlay_applet->applet_id = AppletId::OverlayDisplay;
|
||||
overlay_applet->type = AppletType::OverlayApplet;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -12,7 +15,10 @@ namespace Service::AM {
|
||||
|
||||
HidRegistration::HidRegistration(Core::System& system, Process& process) : m_process(process) {
|
||||
m_hid_server = system.ServiceManager().GetService<HID::IHidServer>("hid", true);
|
||||
this->RegisterCurrentProcess();
|
||||
}
|
||||
|
||||
void HidRegistration::RegisterCurrentProcess() {
|
||||
if (m_process.IsInitialized()) {
|
||||
m_hid_server->GetResourceManager()->RegisterAppletResourceUserId(m_process.GetProcessId(),
|
||||
true);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -24,6 +27,7 @@ public:
|
||||
explicit HidRegistration(Core::System& system, Process& process);
|
||||
~HidRegistration();
|
||||
|
||||
void RegisterCurrentProcess();
|
||||
void EnableAppletToGetInput(bool enable);
|
||||
|
||||
private:
|
||||
|
||||
@@ -171,6 +171,18 @@ void LifecycleManager::SignalSystemEventIfNeeded(Kernel::KernelCore& kernel) {
|
||||
}
|
||||
}
|
||||
|
||||
void LifecycleManager::ResetForRelaunch() {
|
||||
m_unordered_messages.clear();
|
||||
|
||||
m_activity_state = ActivityState::BackgroundVisible;
|
||||
m_requested_focus_state = FocusState{};
|
||||
m_acknowledged_focus_state = FocusState{};
|
||||
m_has_focus_state_changed = true;
|
||||
|
||||
m_suspend_mode = SuspendMode::NoOverride;
|
||||
m_forced_suspend = false;
|
||||
}
|
||||
|
||||
bool LifecycleManager::PopMessage(Kernel::KernelCore& kernel, AppletMessage* out_message) {
|
||||
const auto message = this->PopMessageInOrderOfPriority();
|
||||
this->SignalSystemEventIfNeeded(kernel);
|
||||
|
||||
@@ -138,6 +138,8 @@ public:
|
||||
void PushUnorderedMessage(Kernel::KernelCore& kernel, AppletMessage message);
|
||||
bool PopMessage(Kernel::KernelCore& kernel, AppletMessage* out_message);
|
||||
|
||||
void ResetForRelaunch();
|
||||
|
||||
private:
|
||||
FocusState GetFocusStateWhileForegroundObscured() const;
|
||||
FocusState GetFocusStateWhileBackground(bool is_obscured) const;
|
||||
|
||||
@@ -40,23 +40,23 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] inline std::optional<Process> CreateProcessImpl(std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) {
|
||||
[[nodiscard]] inline std::unique_ptr<Process> CreateProcessImpl(std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) {
|
||||
// Get the appropriate loader to parse this NCA.
|
||||
out_loader = Loader::GetLoader(system, file, program_id, program_index);
|
||||
// Ensure we have a loader which can parse the NCA.
|
||||
if (out_loader) {
|
||||
// Try to load the process.
|
||||
auto process = std::make_optional<Process>(system);
|
||||
auto process = std::make_unique<Process>(system);
|
||||
if (process->Initialize(*out_loader, out_load_result)) {
|
||||
return process;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) {
|
||||
std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) {
|
||||
// Attempt to load program NCA.
|
||||
FileSys::VirtualFile nca_raw{};
|
||||
|
||||
@@ -66,7 +66,7 @@ std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 mi
|
||||
|
||||
// Ensure we retrieved a program NCA.
|
||||
if (!nca_raw) {
|
||||
return std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Ensure we have a suitable version.
|
||||
@@ -76,7 +76,7 @@ std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 mi
|
||||
(nca.GetKeyGeneration() < minimum_key_generation ||
|
||||
nca.GetKeyGeneration() > maximum_key_generation)) {
|
||||
LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id, nca.GetKeyGeneration());
|
||||
return std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 mi
|
||||
return CreateProcessImpl(loader, status, system, nca_raw, program_id, 0);
|
||||
}
|
||||
|
||||
std::optional<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) {
|
||||
std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) {
|
||||
if (auto process = CreateProcessImpl(out_loader, out_load_result, system, file, program_id, program_index); process) {
|
||||
FileSys::NACP nacp;
|
||||
if (out_loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
|
||||
@@ -110,7 +110,23 @@ std::optional<Process> CreateApplicationProcess(std::vector<u8>& out_control, st
|
||||
system.GetARPManager().Register(launch.title_id, launch, out_control);
|
||||
return process;
|
||||
}
|
||||
return std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ReinitializeProcess(Core::System& system, Process& process, u64 program_id) {
|
||||
auto& storage = system.GetContentProviderUnion();
|
||||
const auto nca_raw = storage.GetEntryRaw(program_id, FileSys::ContentRecordType::Program);
|
||||
if (!nca_raw) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto loader = Loader::GetLoader(system, nca_raw, program_id, 0);
|
||||
if (!loader) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Loader::ResultStatus status{};
|
||||
return process.Initialize(*loader, status);
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -27,7 +27,9 @@ class Process;
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation);
|
||||
std::optional<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index);
|
||||
std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation);
|
||||
std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index);
|
||||
|
||||
bool ReinitializeProcess(Core::System& system, Process& process, u64 program_id);
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -35,9 +35,9 @@ Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_applicati
|
||||
std::unique_ptr<Loader::AppLoader> loader;
|
||||
Loader::ResultStatus result;
|
||||
auto process = CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0);
|
||||
R_UNLESS(process != std::nullopt, ResultUnknown);
|
||||
R_UNLESS(process != nullptr, ResultUnknown);
|
||||
|
||||
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), true);
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), true);
|
||||
applet->program_id = program_id;
|
||||
applet->applet_id = AppletId::Application;
|
||||
applet->type = AppletType::Application;
|
||||
@@ -88,9 +88,9 @@ Result IApplicationCreator::CreateSystemApplication(
|
||||
std::vector<u8> control;
|
||||
std::unique_ptr<Loader::AppLoader> loader;
|
||||
auto process = CreateProcess(system, application_id, 1, 22);
|
||||
R_UNLESS(process != std::nullopt, ResultUnknown);
|
||||
R_UNLESS(process != nullptr, ResultUnknown);
|
||||
|
||||
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), true);
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), true);
|
||||
applet->program_id = application_id;
|
||||
applet->applet_id = AppletId::Starter;
|
||||
applet->type = AppletType::LibraryApplet;
|
||||
|
||||
@@ -75,7 +75,7 @@ ILibraryAppletAccessor::ILibraryAppletAccessor(Core::System& system_,
|
||||
{105, D<&ILibraryAppletAccessor::GetPopOutDataEvent>, "GetPopOutDataEvent"},
|
||||
{106, D<&ILibraryAppletAccessor::GetPopInteractiveOutDataEvent>, "GetPopInteractiveOutDataEvent"},
|
||||
{110, nullptr, "NeedsToExitProcess"},
|
||||
{120, nullptr, "GetLibraryAppletInfo"},
|
||||
{120, D<&ILibraryAppletAccessor::GetLibraryAppletInfo>, "GetLibraryAppletInfo"},
|
||||
{150, nullptr, "RequestForAppletToGetForeground"},
|
||||
{160, D<&ILibraryAppletAccessor::GetIndirectLayerConsumerHandle>, "GetIndirectLayerConsumerHandle"}, //2.0.0+
|
||||
{170, D<&ILibraryAppletAccessor::Unknown170>, "Unknown170"}, //22.0.0+
|
||||
@@ -218,6 +218,16 @@ Result ILibraryAppletAccessor::GetIndirectLayerConsumerHandle(Out<u64> out_handl
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ILibraryAppletAccessor::GetLibraryAppletInfo(
|
||||
Out<LibraryAppletInfo> out_library_applet_info) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
*out_library_applet_info = {
|
||||
.applet_id = m_applet->applet_id,
|
||||
.library_applet_mode = m_applet->library_applet_mode,
|
||||
};
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ILibraryAppletAccessor::Unknown170(OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
*out_event = m_applet->unknown_event.GetHandle();
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/am/service/library_applet_self_accessor.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
@@ -21,6 +22,10 @@ public:
|
||||
std::shared_ptr<Applet> applet);
|
||||
~ILibraryAppletAccessor();
|
||||
|
||||
std::shared_ptr<Applet> GetApplet() const {
|
||||
return m_applet;
|
||||
}
|
||||
|
||||
private:
|
||||
Result GetAppletStateChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result IsCompleted(Out<bool> out_is_completed);
|
||||
@@ -37,6 +42,7 @@ private:
|
||||
Result GetPopOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result GetPopInteractiveOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result GetIndirectLayerConsumerHandle(Out<u64> out_handle);
|
||||
Result GetLibraryAppletInfo(Out<LibraryAppletInfo> out_library_applet_info);
|
||||
Result Unknown170(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
|
||||
void FrontendExecute();
|
||||
|
||||
@@ -123,7 +123,7 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
|
||||
|
||||
auto process = CreateProcess(system, program_id, Firmware1400, Firmware2200);
|
||||
if (process) {
|
||||
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), false);
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), false);
|
||||
applet->program_id = program_id;
|
||||
applet->applet_id = applet_id;
|
||||
applet->type = AppletType::LibraryApplet;
|
||||
|
||||
@@ -233,8 +233,9 @@ Result ILibraryAppletSelfAccessor::ReportVisibleErrorWithErrorContext(
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ILibraryAppletSelfAccessor::UnpopInData() {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
Result ILibraryAppletSelfAccessor::UnpopInData(SharedPointer<IStorage> storage) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
m_broker->GetInData().Unpop(system.Kernel(), storage);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ private:
|
||||
Result ReportVisibleError(ErrorCode error_code);
|
||||
Result ReportVisibleErrorWithErrorContext(
|
||||
ErrorCode error_code, InLargeData<ErrorContext, BufferAttr_HipcMapAlias> error_context);
|
||||
Result UnpopInData();
|
||||
Result UnpopInData(SharedPointer<IStorage> storage);
|
||||
Result GetMainAppletApplicationDesiredLanguage(Out<u64> out_desired_language);
|
||||
Result GetCurrentApplicationId(Out<u64> out_application_id);
|
||||
Result GetMainAppletAvailableUsers(Out<bool> out_can_select_any_user, Out<s32> out_users_count,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/frontend/applets.h"
|
||||
#include "core/hle/service/am/service/library_applet_accessor.h"
|
||||
#include "core/hle/service/am/service/process_winding_controller.h"
|
||||
@@ -43,6 +45,18 @@ Result IProcessWindingController::OpenCallingLibraryApplet(
|
||||
Out<SharedPointer<ILibraryAppletAccessor>> out_calling_library_applet) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
|
||||
std::shared_ptr<Applet> reserved_applet;
|
||||
{
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
reserved_applet = std::move(m_applet->reserved_applet);
|
||||
}
|
||||
|
||||
if (reserved_applet != nullptr) {
|
||||
*out_calling_library_applet = std::make_shared<ILibraryAppletAccessor>(
|
||||
system, reserved_applet->caller_applet_broker, reserved_applet);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
const auto caller_applet = m_applet->caller_applet.lock();
|
||||
if (caller_applet == nullptr) {
|
||||
LOG_ERROR(Service_AM, "No caller applet available");
|
||||
@@ -74,22 +88,82 @@ Result IProcessWindingController::PopContext(Out<SharedPointer<IStorage>> out_co
|
||||
}
|
||||
|
||||
Result IProcessWindingController::CancelWindingReservation() {
|
||||
LOG_WARNING(Service_AM, "STUBBED");
|
||||
LOG_INFO(Service_AM, "called");
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->reserved_applet.reset();
|
||||
m_applet->unwind_after_reserved = false;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IProcessWindingController::WindAndDoReserved() {
|
||||
LOG_WARNING(Service_AM, "STUBBED");
|
||||
LOG_INFO(Service_AM, "called");
|
||||
|
||||
std::shared_ptr<Applet> reserved_applet;
|
||||
{
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
|
||||
reserved_applet = m_applet->reserved_applet;
|
||||
m_applet->display_layer_manager.SetWindowVisibility(false);
|
||||
m_applet->exit_locked = false;
|
||||
system.SetExitLocked(false);
|
||||
}
|
||||
|
||||
if (reserved_applet) {
|
||||
{
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->is_winding = true;
|
||||
}
|
||||
|
||||
{
|
||||
std::scoped_lock lk{reserved_applet->lock};
|
||||
reserved_applet->window_visible = true;
|
||||
reserved_applet->process->Run();
|
||||
}
|
||||
|
||||
if (reserved_applet->frontend) {
|
||||
reserved_applet->frontend->Initialize();
|
||||
reserved_applet->frontend->Execute();
|
||||
}
|
||||
} else {
|
||||
LOG_WARNING(Service_AM, "called without a reserved applet to start");
|
||||
}
|
||||
|
||||
m_applet->process->Terminate();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IProcessWindingController::ReserveToStartAndWaitAndUnwindThis() {
|
||||
LOG_WARNING(Service_AM, "STUBBED");
|
||||
Result IProcessWindingController::ReserveToStartAndWaitAndUnwindThis(
|
||||
SharedPointer<ILibraryAppletAccessor> reserved_applet_accessor) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
|
||||
if (reserved_applet_accessor == nullptr) {
|
||||
LOG_ERROR(Service_AM, "No applet accessor provided");
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->reserved_applet = reserved_applet_accessor->GetApplet();
|
||||
m_applet->unwind_after_reserved = true;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IProcessWindingController::ReserveToStartAndWait() {
|
||||
LOG_WARNING(Service_AM, "STUBBED");
|
||||
Result IProcessWindingController::ReserveToStartAndWait(
|
||||
SharedPointer<ILibraryAppletAccessor> reserved_applet_accessor) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
|
||||
if (reserved_applet_accessor == nullptr) {
|
||||
LOG_ERROR(Service_AM, "No applet accessor provided");
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->reserved_applet = reserved_applet_accessor->GetApplet();
|
||||
m_applet->unwind_after_reserved = false;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -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-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -29,8 +29,9 @@ private:
|
||||
Result PopContext(Out<SharedPointer<IStorage>> out_context);
|
||||
Result CancelWindingReservation();
|
||||
Result WindAndDoReserved();
|
||||
Result ReserveToStartAndWaitAndUnwindThis();
|
||||
Result ReserveToStartAndWait();
|
||||
Result ReserveToStartAndWaitAndUnwindThis(
|
||||
SharedPointer<ILibraryAppletAccessor> reserved_applet_accessor);
|
||||
Result ReserveToStartAndWait(SharedPointer<ILibraryAppletAccessor> reserved_applet_accessor);
|
||||
|
||||
const std::shared_ptr<Applet> m_applet;
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/applet_manager.h"
|
||||
#include "core/hle/service/am/event_observer.h"
|
||||
#include "core/hle/service/am/process_creation.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
|
||||
namespace Service::AM {
|
||||
@@ -240,6 +241,37 @@ void WindowSystem::PruneTerminatedAppletsLocked() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// A winding applet has had its own process killed but is kept alive as a transparent slot
|
||||
// while the reserved applet running in its place finishes (see WindAndDoReserved()).
|
||||
if (applet->is_winding) {
|
||||
if (!applet->child_applets.empty()) {
|
||||
it = std::next(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool unwind = applet->unwind_after_reserved;
|
||||
applet->is_winding = false;
|
||||
applet->unwind_after_reserved = false;
|
||||
|
||||
if (unwind && this->RestartAppletProcessLocked(applet.get())) {
|
||||
const u64 new_aruid = applet->aruid.pid;
|
||||
const auto next = std::next(it);
|
||||
|
||||
if (new_aruid != aruid) {
|
||||
auto node = m_applets.extract(it);
|
||||
node.key() = new_aruid;
|
||||
m_applets.insert(std::move(node));
|
||||
}
|
||||
|
||||
applet->process->Run();
|
||||
m_event_observer->RequestUpdate();
|
||||
it = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
applet->reserved_applet.reset();
|
||||
}
|
||||
|
||||
// Terminated, so ensure all child applets are terminated.
|
||||
if (!applet->child_applets.empty()) {
|
||||
this->TerminateChildAppletsLocked(applet.get());
|
||||
@@ -301,6 +333,28 @@ void WindowSystem::PruneTerminatedAppletsLocked() {
|
||||
}
|
||||
}
|
||||
|
||||
bool WindowSystem::RestartAppletProcessLocked(Applet* applet) {
|
||||
if (!ReinitializeProcess(m_system, *applet->process, applet->program_id)) {
|
||||
LOG_ERROR(Service_AM, "Failed to restart winding applet_id={}",
|
||||
static_cast<u32>(applet->applet_id));
|
||||
return false;
|
||||
}
|
||||
|
||||
applet->aruid.pid = applet->process->GetProcessId();
|
||||
applet->is_process_running = false;
|
||||
applet->is_completed = false;
|
||||
|
||||
applet->hid_registration.RegisterCurrentProcess();
|
||||
|
||||
applet->lifecycle_manager.ResetForRelaunch();
|
||||
applet->is_activity_runnable = false;
|
||||
|
||||
applet->launch_reason.flag = 1;
|
||||
|
||||
m_event_observer->TrackAppletProcess(*applet);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WindowSystem::LockHomeMenuIntoForegroundLocked() {
|
||||
// If the home menu is not locked into foreground, then there's nothing to do.
|
||||
if (m_home_menu == nullptr || !m_home_menu_foreground_locked) {
|
||||
@@ -352,6 +406,9 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
|
||||
const bool has_obscuring_child_applets = [&] {
|
||||
for (const auto& child_applet : applet->child_applets) {
|
||||
std::scoped_lock lk2{child_applet->lock};
|
||||
if (child_applet->is_winding) {
|
||||
return true;
|
||||
}
|
||||
const auto mode = child_applet->library_applet_mode;
|
||||
if (child_applet->is_process_running && child_applet->window_visible &&
|
||||
(mode == LibraryAppletMode::AllForeground ||
|
||||
|
||||
@@ -61,6 +61,7 @@ public:
|
||||
|
||||
private:
|
||||
void PruneTerminatedAppletsLocked();
|
||||
bool RestartAppletProcessLocked(Applet* applet);
|
||||
bool LockHomeMenuIntoForegroundLocked();
|
||||
void TerminateChildAppletsLocked(Applet* applet);
|
||||
void UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking = false);
|
||||
|
||||
@@ -86,27 +86,17 @@ 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
|
||||
// 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},
|
||||
{"FS", &FileSystem::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},
|
||||
#endif
|
||||
{"ldn", &LDN::LoopProcess},
|
||||
{"Loader", &LDR::LoopProcess},
|
||||
{"nvservices", &Nvidia::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("vi", [&, token] { VI::LoopProcess(system, token); }).detach();
|
||||
#endif
|
||||
kernel.RunOnHostCoreProcess("vi", [&, token] { VI::LoopProcess(system, token); }).detach();
|
||||
// Avoid cold clones of lambdas -- succintly
|
||||
for (auto const& e : std::vector<std::pair<std::string_view, void (*)(Core::System&)>>{
|
||||
{"sm", &SM::LoopProcess},
|
||||
|
||||
@@ -151,7 +151,7 @@ if (MSVC)
|
||||
)
|
||||
else()
|
||||
target_compile_options(hid_core PRIVATE
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-sign-conversion>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-cast-function-type>
|
||||
$<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>)
|
||||
|
||||
@@ -28,10 +28,7 @@ enum class NpadMcuState : u32 {
|
||||
struct NpadMcuHolder {
|
||||
NpadMcuState state;
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
union {
|
||||
IAbstractedPad* abstracted_pad;
|
||||
u64 abstracted_pad_raw;
|
||||
};
|
||||
IAbstractedPad* abstracted_pad;
|
||||
};
|
||||
static_assert(sizeof(NpadMcuHolder) == 0x10, "NpadMcuHolder is an invalid size");
|
||||
|
||||
|
||||
@@ -33,15 +33,9 @@ public:
|
||||
bool is_created{};
|
||||
bool is_mapped{};
|
||||
INSERT_PADDING_BYTES(0x5);
|
||||
union {
|
||||
Kernel::KSharedMemory* shared_memory = nullptr;
|
||||
u64 shared_memory_raw;
|
||||
};
|
||||
Kernel::KSharedMemory* shared_memory;
|
||||
INSERT_PADDING_BYTES(0x38);
|
||||
union {
|
||||
SharedMemoryFormat* address = nullptr;
|
||||
u64 address_raw;
|
||||
};
|
||||
SharedMemoryFormat* address = nullptr;
|
||||
};
|
||||
// Correct size is 0x50 bytes
|
||||
static_assert(sizeof(SharedMemoryHolder) == 0x50, "SharedMemoryHolder is an invalid size");
|
||||
|
||||
@@ -15,6 +15,8 @@ add_library(input_common STATIC
|
||||
drivers/tas_input.h
|
||||
drivers/touch_screen.cpp
|
||||
drivers/touch_screen.h
|
||||
drivers/udp_client.cpp
|
||||
drivers/udp_client.h
|
||||
drivers/virtual_amiibo.cpp
|
||||
drivers/virtual_amiibo.h
|
||||
drivers/virtual_gamepad.cpp
|
||||
@@ -32,12 +34,8 @@ add_library(input_common STATIC
|
||||
input_poller.cpp
|
||||
input_poller.h
|
||||
main.cpp
|
||||
main.h)
|
||||
if (NOT PLATFORM_EMSCRIPTEN)
|
||||
target_sources(input_common PRIVATE
|
||||
drivers/udp_client.cpp
|
||||
drivers/udp_client.h)
|
||||
endif()
|
||||
main.h
|
||||
)
|
||||
|
||||
if (MSVC)
|
||||
target_compile_options(input_common PRIVATE
|
||||
@@ -46,7 +44,7 @@ if (MSVC)
|
||||
/we4800 # Implicit conversion from 'type' to bool. Possible information loss
|
||||
)
|
||||
else()
|
||||
target_compile_options(input_common PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>)
|
||||
target_compile_options(input_common PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>)
|
||||
endif()
|
||||
|
||||
if (ANDROID)
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "input_common/drivers/mouse.h"
|
||||
#include "input_common/drivers/tas_input.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_gamepad.h"
|
||||
#include "input_common/helpers/stick_from_buttons.h"
|
||||
@@ -20,9 +21,6 @@
|
||||
#include "input_common/input_mapping.h"
|
||||
#include "input_common/input_poller.h"
|
||||
#include "input_common/main.h"
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#include "input_common/drivers/udp_client.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_LIBUSB
|
||||
#include "input_common/drivers/gc_adapter.h"
|
||||
@@ -84,9 +82,7 @@ struct InputSubsystem::Impl {
|
||||
#ifdef ENABLE_LIBUSB
|
||||
RegisterEngine("gcpad", gcadapter);
|
||||
#endif
|
||||
#ifndef __EMSCRIPTEN__
|
||||
RegisterEngine("cemuhookudp", udp_client);
|
||||
#endif
|
||||
RegisterEngine("tas", tas_input);
|
||||
RegisterEngine("camera", camera);
|
||||
#ifdef __ANDROID__
|
||||
@@ -120,9 +116,7 @@ struct InputSubsystem::Impl {
|
||||
#ifdef ENABLE_LIBUSB
|
||||
UnregisterEngine(gcadapter);
|
||||
#endif
|
||||
#ifndef __EMSCRIPTEN__
|
||||
UnregisterEngine(udp_client);
|
||||
#endif
|
||||
UnregisterEngine(tas_input);
|
||||
UnregisterEngine(camera);
|
||||
#ifdef __ANDROID__
|
||||
@@ -158,10 +152,8 @@ struct InputSubsystem::Impl {
|
||||
auto gcadapter_devices = gcadapter->GetInputDevices();
|
||||
devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end());
|
||||
#endif
|
||||
#ifndef __EMSCRIPTEN__
|
||||
auto udp_devices = udp_client->GetInputDevices();
|
||||
devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
|
||||
#endif
|
||||
#ifdef HAVE_SDL3
|
||||
auto joycon_devices = joycon->GetInputDevices();
|
||||
devices.insert(devices.end(), joycon_devices.begin(), joycon_devices.end());
|
||||
@@ -194,11 +186,9 @@ struct InputSubsystem::Impl {
|
||||
return gcadapter;
|
||||
}
|
||||
#endif
|
||||
#ifndef __EMSCRIPTEN__
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return udp_client;
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_SDL3
|
||||
if (engine == sdl->GetEngineName()) {
|
||||
return sdl;
|
||||
@@ -281,11 +271,9 @@ struct InputSubsystem::Impl {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#ifndef __EMSCRIPTEN__
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
if (engine == tas_input->GetEngineName()) {
|
||||
return true;
|
||||
}
|
||||
@@ -312,9 +300,7 @@ struct InputSubsystem::Impl {
|
||||
#ifdef ENABLE_LIBUSB
|
||||
gcadapter->BeginConfiguration();
|
||||
#endif
|
||||
#ifndef __EMSCRIPTEN__
|
||||
udp_client->BeginConfiguration();
|
||||
#endif
|
||||
#ifdef HAVE_SDL3
|
||||
sdl->BeginConfiguration();
|
||||
joycon->BeginConfiguration();
|
||||
@@ -330,9 +316,7 @@ struct InputSubsystem::Impl {
|
||||
#ifdef ENABLE_LIBUSB
|
||||
gcadapter->EndConfiguration();
|
||||
#endif
|
||||
#ifndef __EMSCRIPTEN__
|
||||
udp_client->EndConfiguration();
|
||||
#endif
|
||||
#ifdef HAVE_SDL3
|
||||
sdl->EndConfiguration();
|
||||
joycon->EndConfiguration();
|
||||
@@ -357,9 +341,7 @@ struct InputSubsystem::Impl {
|
||||
std::shared_ptr<Mouse> mouse;
|
||||
std::shared_ptr<TouchScreen> touch_screen;
|
||||
std::shared_ptr<TasInput::Tas> tas_input;
|
||||
#ifndef __EMSCRIPTEN__
|
||||
std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
|
||||
#endif
|
||||
std::shared_ptr<Camera> camera;
|
||||
std::shared_ptr<VirtualAmiibo> virtual_amiibo;
|
||||
std::shared_ptr<VirtualGamepad> virtual_gamepad;
|
||||
@@ -488,9 +470,7 @@ bool InputSubsystem::IsStickInverted(const Common::ParamPackage& params) const {
|
||||
}
|
||||
|
||||
void InputSubsystem::ReloadInputDevices() {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
impl->udp_client.get()->ReloadSockets();
|
||||
#endif
|
||||
}
|
||||
|
||||
void InputSubsystem::BeginMapping(Polling::InputType type) {
|
||||
|
||||
@@ -253,11 +253,12 @@ if (MSVC)
|
||||
)
|
||||
else()
|
||||
target_compile_options(shader_recompiler PRIVATE
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>
|
||||
# Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6.
|
||||
# And this in turns limits the size of a std::array.
|
||||
$<$<CXX_COMPILER_ID:Clang>:-fbracket-depth=1024>
|
||||
$<$<CXX_COMPILER_ID:AppleClang>:-fbracket-depth=1024>)
|
||||
$<$<CXX_COMPILER_ID:AppleClang>:-fbracket-depth=1024>
|
||||
)
|
||||
endif()
|
||||
|
||||
create_target_directory_groups(shader_recompiler)
|
||||
|
||||
@@ -31,7 +31,7 @@ struct CbufWordKey {
|
||||
|
||||
struct CbufWordKeyHash {
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -46,12 +46,12 @@ struct HandleKey {
|
||||
};
|
||||
struct HandleKeyHash {
|
||||
constexpr size_t operator()(const HandleKey& k) const noexcept {
|
||||
u64 h = (u64(k.index) << 32) ^ k.offset;
|
||||
h ^= (u64(k.shift_left) << 1);
|
||||
h ^= (u64(k.sec_index) << 33) ^ (u64(k.sec_offset) << 2);
|
||||
h ^= (u64(k.sec_shift_left) << 3);
|
||||
size_t h = (size_t(k.index) << 32) ^ k.offset;
|
||||
h ^= (size_t(k.shift_left) << 1);
|
||||
h ^= (size_t(k.sec_index) << 33) ^ (size_t(k.sec_offset) << 2);
|
||||
h ^= (size_t(k.sec_shift_left) << 3);
|
||||
h ^= k.has_secondary ? 0x9e3779b97f4a7c15ULL : 0ULL;
|
||||
return size_t(h);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -160,14 +157,16 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
|
||||
|
||||
unsigned Swizzle(u64 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 (encoding >= RG_LUT.size())
|
||||
if (encoding >= RG_LUT.size()) {
|
||||
throw NotImplementedException("Illegal RG encoding {}", encoding);
|
||||
}
|
||||
return RG_LUT[encoding];
|
||||
} else {
|
||||
if (encoding >= RGBA_LUT.size())
|
||||
if (encoding >= RGBA_LUT.size()) {
|
||||
throw NotImplementedException("Illegal RGBA encoding {}", 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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -145,14 +142,16 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
|
||||
|
||||
unsigned Swizzle(u64 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 (encoding >= RG_LUT.size())
|
||||
if (encoding >= RG_LUT.size()) {
|
||||
throw NotImplementedException("Illegal RG encoding {}", encoding);
|
||||
}
|
||||
return RG_LUT[encoding];
|
||||
} else {
|
||||
if (encoding >= RGBA_LUT.size())
|
||||
if (encoding >= RGBA_LUT.size()) {
|
||||
throw NotImplementedException("Illegal RGBA encoding {}", encoding);
|
||||
}
|
||||
return RGBA_LUT[encoding];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,7 +371,7 @@ else()
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-shadow>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-unused-local-typedef>)
|
||||
else()
|
||||
target_compile_options(video_core PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>)
|
||||
target_compile_options(video_core PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>)
|
||||
endif()
|
||||
|
||||
target_compile_options(video_core PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-sign-conversion>)
|
||||
|
||||
@@ -109,12 +109,12 @@ public:
|
||||
return static_cast<u32>(other_cpu_addr - cpu_addr);
|
||||
}
|
||||
|
||||
size_t getLRUID() const noexcept {
|
||||
return lru_id;
|
||||
u64 GetFrameTick() const noexcept {
|
||||
return frame_tick;
|
||||
}
|
||||
|
||||
void setLRUID(size_t lru_id_) {
|
||||
lru_id = lru_id_;
|
||||
void SetFrameTick(u64 tick) noexcept {
|
||||
frame_tick = tick;
|
||||
}
|
||||
|
||||
size_t SizeBytes() const {
|
||||
@@ -125,7 +125,7 @@ private:
|
||||
VAddr cpu_addr = 0;
|
||||
BufferFlagBits flags{};
|
||||
int stream_score = 0;
|
||||
size_t lru_id = SIZE_MAX;
|
||||
u64 frame_tick = 0;
|
||||
size_t size_bytes = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -58,17 +58,22 @@ void BufferCache<P>::RunGarbageCollector() {
|
||||
const bool aggressive_gc = total_used_memory >= critical_memory;
|
||||
const u64 ticks_to_destroy = aggressive_gc ? 60 : 120;
|
||||
int num_iterations = aggressive_gc ? 64 : 32;
|
||||
const auto clean_up = [this, &num_iterations](BufferId buffer_id) {
|
||||
const u64 threshold = frame_tick - ticks_to_destroy;
|
||||
boost::container::small_vector<BufferId, 64> expired;
|
||||
for (auto [id, buffer] : slot_buffers) {
|
||||
if (buffer->GetFrameTick() < threshold) {
|
||||
expired.push_back(id);
|
||||
}
|
||||
}
|
||||
for (const auto buffer_id : expired) {
|
||||
if (num_iterations == 0) {
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
--num_iterations;
|
||||
auto& buffer = slot_buffers[buffer_id];
|
||||
DownloadBufferMemory(buffer);
|
||||
DeleteBuffer(buffer_id);
|
||||
return false;
|
||||
};
|
||||
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up);
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
@@ -1591,10 +1596,9 @@ void BufferCache<P>::ChangeRegister(BufferId buffer_id) {
|
||||
const auto size = buffer.SizeBytes();
|
||||
if (insert) {
|
||||
total_used_memory += Common::AlignUp(size, 1024);
|
||||
buffer.setLRUID(lru_cache.Insert(buffer_id, frame_tick));
|
||||
buffer.SetFrameTick(frame_tick);
|
||||
} else {
|
||||
total_used_memory -= Common::AlignUp(size, 1024);
|
||||
lru_cache.Free(buffer.getLRUID());
|
||||
}
|
||||
const DAddr device_addr_begin = buffer.CpuAddr();
|
||||
const DAddr device_addr_end = device_addr_begin + size;
|
||||
@@ -1612,7 +1616,7 @@ void BufferCache<P>::ChangeRegister(BufferId buffer_id) {
|
||||
template <class P>
|
||||
void BufferCache<P>::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept {
|
||||
if (buffer_id != NULL_BUFFER_ID) {
|
||||
lru_cache.Touch(buffer.getLRUID(), frame_tick);
|
||||
buffer.SetFrameTick(frame_tick);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/div_ceil.h"
|
||||
#include "common/literals.h"
|
||||
#include "common/lru_cache.h"
|
||||
#include "common/range_sets.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/settings.h"
|
||||
@@ -506,11 +505,6 @@ private:
|
||||
size_t immediate_buffer_capacity = 0;
|
||||
Common::ScratchBuffer<u8> immediate_buffer_alloc;
|
||||
|
||||
struct LRUItemParams {
|
||||
using ObjectType = BufferId;
|
||||
using TickType = u64;
|
||||
};
|
||||
Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache;
|
||||
u64 frame_tick = 0;
|
||||
u64 total_used_memory = 0;
|
||||
u64 minimum_memory = 0;
|
||||
|
||||
@@ -117,7 +117,35 @@ void DmaPusher::ProcessCommands(std::span<const CommandHeader> commands) {
|
||||
dma_state.is_last_call = true;
|
||||
index += max_write;
|
||||
} else if (dma_state.method_count) {
|
||||
auto const command_header = commands[index]; //can copy
|
||||
if (!dma_state.non_incrementing && !dma_increment_once &&
|
||||
dma_state.method >= non_puller_methods) {
|
||||
auto subchannel = subchannels[dma_state.subchannel];
|
||||
const u32 available = u32(std::min<size_t>(
|
||||
index + dma_state.method_count, commands.size()) - index);
|
||||
u32 batch = 0;
|
||||
u32 method = dma_state.method;
|
||||
while (batch < available) {
|
||||
const bool needs_exec =
|
||||
(method < Engines::EngineInterface::EXECUTION_MASK_TABLE_SIZE)
|
||||
? subchannel->execution_mask[method]
|
||||
: subchannel->execution_mask_default;
|
||||
if (needs_exec) break;
|
||||
batch++;
|
||||
method++;
|
||||
}
|
||||
if (batch > 0) {
|
||||
auto& sink = subchannel->method_sink;
|
||||
sink.reserve(sink.size() + batch);
|
||||
for (u32 j = 0; j < batch; j++) {
|
||||
sink.emplace_back(dma_state.method + j, commands[index + j].argument);
|
||||
}
|
||||
dma_state.method += batch;
|
||||
dma_state.method_count -= batch;
|
||||
index += batch;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
auto const command_header = commands[index];
|
||||
dma_state.dma_word_offset = u32(index * sizeof(u32));
|
||||
dma_state.is_last_call = dma_state.method_count <= 1;
|
||||
CallMethod(command_header.argument);
|
||||
@@ -176,7 +204,11 @@ void DmaPusher::CallMethod(u32 argument) {
|
||||
});
|
||||
} else {
|
||||
auto subchannel = subchannels[dma_state.subchannel];
|
||||
if (!subchannel->execution_mask[dma_state.method]) {
|
||||
const bool needs_execution =
|
||||
(dma_state.method < Engines::EngineInterface::EXECUTION_MASK_TABLE_SIZE)
|
||||
? subchannel->execution_mask[dma_state.method]
|
||||
: subchannel->execution_mask_default;
|
||||
if (!needs_execution) {
|
||||
subchannel->method_sink.emplace_back(dma_state.method, argument);
|
||||
} else {
|
||||
subchannel->ConsumeSink(system);
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bitset>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
@@ -43,10 +42,15 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
std::bitset<(std::numeric_limits<u16>::max)()> execution_mask{};
|
||||
std::vector<std::pair<u32, u32>> method_sink{};
|
||||
static constexpr size_t EXECUTION_MASK_TABLE_SIZE = 0xE00;
|
||||
|
||||
std::array<u8, EXECUTION_MASK_TABLE_SIZE> execution_mask{};
|
||||
bool execution_mask_default{};
|
||||
boost::container::small_vector<std::pair<u32, u32>, 64> method_sink{};
|
||||
GPUVAddr current_dma_segment;
|
||||
/// @brief Indicates whether the current DMA segment is dirty.
|
||||
bool current_dirty{};
|
||||
|
||||
protected:
|
||||
virtual void ConsumeSinkImpl(Core::System& system) {
|
||||
for (auto [method, value] : method_sink) {
|
||||
|
||||
@@ -26,7 +26,7 @@ Fermi2D::Fermi2D(MemoryManager& memory_manager_) : memory_manager{memory_manager
|
||||
regs.src.depth = 1;
|
||||
regs.dst.depth = 1;
|
||||
|
||||
execution_mask.reset();
|
||||
execution_mask.fill(0);
|
||||
execution_mask[FERMI2D_REG_INDEX(pixels_from_memory.src_y0) + 1] = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ KeplerCompute::KeplerCompute(MemoryManager& memory_manager_)
|
||||
: memory_manager{memory_manager_}
|
||||
, upload_state{memory_manager, regs.upload}
|
||||
{
|
||||
execution_mask.reset();
|
||||
execution_mask.fill(0);
|
||||
execution_mask[KEPLER_COMPUTE_REG_INDEX(exec_upload)] = true;
|
||||
execution_mask[KEPLER_COMPUTE_REG_INDEX(data_upload)] = true;
|
||||
execution_mask[KEPLER_COMPUTE_REG_INDEX(launch)] = true;
|
||||
|
||||
@@ -23,7 +23,7 @@ KeplerMemory::~KeplerMemory() = default;
|
||||
void KeplerMemory::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
|
||||
upload_state.BindRasterizer(rasterizer_);
|
||||
|
||||
execution_mask.reset();
|
||||
execution_mask.fill(0);
|
||||
execution_mask[KEPLERMEMORY_REG_INDEX(exec)] = true;
|
||||
execution_mask[KEPLERMEMORY_REG_INDEX(data)] = true;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,14 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/bit_util.h"
|
||||
#include "common/scope_exit.h"
|
||||
@@ -22,6 +28,16 @@
|
||||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
namespace {
|
||||
inline void PrefetchLine(const void* addr) {
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
_mm_prefetch(static_cast<const char*>(addr), _MM_HINT_T0);
|
||||
#else
|
||||
__builtin_prefetch(addr, 0, 1);
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
|
||||
/// First register id that is actually a Macro call.
|
||||
constexpr u32 MacroRegistersStart = 0xE00;
|
||||
|
||||
@@ -37,9 +53,10 @@ Maxwell3D::Maxwell3D(MemoryManager& memory_manager_)
|
||||
{
|
||||
dirty.flags.flip();
|
||||
InitializeRegisterDefaults();
|
||||
execution_mask.reset();
|
||||
for (size_t i = 0; i < execution_mask.size(); i++)
|
||||
execution_mask.fill(0);
|
||||
for (size_t i = 0; i < EXECUTION_MASK_TABLE_SIZE; i++)
|
||||
execution_mask[i] = IsMethodExecutable(u32(i));
|
||||
execution_mask_default = true;
|
||||
}
|
||||
|
||||
Maxwell3D::~Maxwell3D() = default;
|
||||
@@ -282,18 +299,44 @@ u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) {
|
||||
}
|
||||
|
||||
void Maxwell3D::ConsumeSinkImpl(Core::System& system) {
|
||||
std::stable_sort(method_sink.begin(), method_sink.end(),
|
||||
[](const auto& a, const auto& b) { return a.first < b.first; });
|
||||
|
||||
const auto sink_size = method_sink.size();
|
||||
const auto control = shadow_state.shadow_ram_control;
|
||||
if (control == Regs::ShadowRamControl::Track || control == Regs::ShadowRamControl::TrackWithFilter) {
|
||||
for (auto [method, value] : method_sink) {
|
||||
for (size_t i = 0; i < sink_size; ++i) {
|
||||
const auto [method, value] = method_sink[i];
|
||||
if (i + 1 < sink_size) {
|
||||
const u32 next = method_sink[i + 1].first;
|
||||
PrefetchLine(®s.reg_array[next]);
|
||||
PrefetchLine(&shadow_state.reg_array[next]);
|
||||
PrefetchLine(&dirty.tables[0][next]);
|
||||
}
|
||||
shadow_state.reg_array[method] = value;
|
||||
ProcessDirtyRegisters(method, value);
|
||||
}
|
||||
} else if (control == Regs::ShadowRamControl::Replay) {
|
||||
for (auto [method, value] : method_sink)
|
||||
for (size_t i = 0; i < sink_size; ++i) {
|
||||
const auto [method, value] = method_sink[i];
|
||||
if (i + 1 < sink_size) {
|
||||
const u32 next = method_sink[i + 1].first;
|
||||
PrefetchLine(®s.reg_array[next]);
|
||||
PrefetchLine(&shadow_state.reg_array[next]);
|
||||
PrefetchLine(&dirty.tables[0][next]);
|
||||
}
|
||||
ProcessDirtyRegisters(method, shadow_state.reg_array[method]);
|
||||
}
|
||||
} else {
|
||||
for (auto [method, value] : method_sink)
|
||||
for (size_t i = 0; i < sink_size; ++i) {
|
||||
const auto [method, value] = method_sink[i];
|
||||
if (i + 1 < sink_size) {
|
||||
const u32 next = method_sink[i + 1].first;
|
||||
PrefetchLine(®s.reg_array[next]);
|
||||
PrefetchLine(&dirty.tables[0][next]);
|
||||
}
|
||||
ProcessDirtyRegisters(method, value);
|
||||
}
|
||||
}
|
||||
method_sink.clear();
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ using namespace Texture;
|
||||
MaxwellDMA::MaxwellDMA(MemoryManager& memory_manager_)
|
||||
: memory_manager{memory_manager_}
|
||||
{
|
||||
execution_mask.reset();
|
||||
execution_mask.fill(0);
|
||||
execution_mask[offsetof(Regs, launch_dma) / sizeof(u32)] = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -287,6 +287,7 @@ void QueryCacheBase<Traits>::CounterReport(GPUVAddr addr, QueryType counter_type
|
||||
u32 value = static_cast<u32>(query_base->value);
|
||||
std::memcpy(pointer, &value, sizeof(value));
|
||||
}
|
||||
query_base->flags |= QueryFlagBits::IsGuestSynced;
|
||||
if (!is_synced) [[likely]] {
|
||||
impl->pending_unregister.push_back(query_location);
|
||||
}
|
||||
@@ -569,10 +570,12 @@ bool QueryCacheBase<Traits>::SemiFlushQueryDirty(QueryCacheBase<Traits>::QueryLo
|
||||
auto* ptr = impl->device_memory.template GetPointer<u8>(query_base->guest_address);
|
||||
if (True(query_base->flags & QueryFlagBits::HasTimestamp)) {
|
||||
std::memcpy(ptr, &query_base->value, sizeof(query_base->value));
|
||||
query_base->flags |= QueryFlagBits::IsGuestSynced;
|
||||
return false;
|
||||
}
|
||||
u32 value_l = static_cast<u32>(query_base->value);
|
||||
std::memcpy(ptr, &value_l, sizeof(value_l));
|
||||
query_base->flags |= QueryFlagBits::IsGuestSynced;
|
||||
return false;
|
||||
}
|
||||
return True(query_base->flags & QueryFlagBits::IsHostManaged) &&
|
||||
|
||||
@@ -62,6 +62,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
|
||||
extended_dynamic_state_2_logic_op.Assign(features.has_extended_dynamic_state_2_logic_op ? 1 : 0);
|
||||
extended_dynamic_state_3_blend.Assign(features.has_extended_dynamic_state_3_blend ? 1 : 0);
|
||||
extended_dynamic_state_3_enables.Assign(features.has_extended_dynamic_state_3_enables ? 1 : 0);
|
||||
dynamic_state3_depth_clamp_enable.Assign(features.has_dynamic_state3_depth_clamp_enable ? 1 : 0);
|
||||
dynamic_vertex_input.Assign(features.has_dynamic_vertex_input ? 1 : 0);
|
||||
xfb_enabled.Assign(regs.transform_feedback_enabled != 0);
|
||||
ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
|
||||
|
||||
@@ -208,6 +208,7 @@ struct FixedPipelineState {
|
||||
BitField<12, 2, u32> tessellation_spacing;
|
||||
BitField<14, 1, u32> tessellation_clockwise;
|
||||
BitField<15, 5, u32> patch_control_points_minus_one;
|
||||
BitField<20, 1, u32> dynamic_state3_depth_clamp_enable;
|
||||
|
||||
BitField<24, 4, Maxwell::PrimitiveTopology> topology;
|
||||
BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode;
|
||||
|
||||
@@ -907,7 +907,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
|
||||
// EDS3 - Enables (composite: per-feature)
|
||||
if (key.state.extended_dynamic_state_3_enables) {
|
||||
if (device.SupportsDynamicState3DepthClampEnable()) {
|
||||
if (key.state.dynamic_state3_depth_clamp_enable != 0) {
|
||||
dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT);
|
||||
}
|
||||
if (device.SupportsDynamicState3LogicOpEnable()) {
|
||||
|
||||
@@ -492,7 +492,9 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||
device.IsExtExtendedDynamicState3BlendingSupported();
|
||||
dynamic_features.has_extended_dynamic_state_3_enables =
|
||||
device.IsExtExtendedDynamicState3EnablesSupported();
|
||||
dynamic_features.has_dynamic_state3_depth_clamp_enable = false;
|
||||
dynamic_features.has_dynamic_state3_depth_clamp_enable =
|
||||
dynamic_features.has_extended_dynamic_state_3_enables &&
|
||||
device.SupportsDynamicState3DepthClampEnable();
|
||||
dynamic_features.has_dynamic_state3_logic_op_enable =
|
||||
device.SupportsDynamicState3LogicOpEnable();
|
||||
dynamic_features.has_dynamic_state3_line_stipple_enable =
|
||||
|
||||
@@ -113,6 +113,10 @@ public:
|
||||
|
||||
[[nodiscard]] ComputePipeline* CurrentComputePipeline();
|
||||
|
||||
[[nodiscard]] bool SupportsDynamicState3DepthClampEnable() const {
|
||||
return dynamic_features.has_dynamic_state3_depth_clamp_enable;
|
||||
}
|
||||
|
||||
void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
|
||||
const VideoCore::DiskResourceLoadCallback& callback);
|
||||
|
||||
|
||||
@@ -218,8 +218,7 @@ public:
|
||||
}
|
||||
PauseCounter();
|
||||
const auto driver_id = device.GetDriverID();
|
||||
if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
|
||||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
|
||||
if (driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
|
||||
pending_sync.clear();
|
||||
sync_values_stash.clear();
|
||||
return;
|
||||
|
||||
@@ -1578,7 +1578,7 @@ void RasterizerVulkan::UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& r
|
||||
if (!state_tracker.TouchDepthClampEnable()) {
|
||||
return;
|
||||
}
|
||||
if (!device.SupportsDynamicState3DepthClampEnable()) {
|
||||
if (!pipeline_cache.SupportsDynamicState3DepthClampEnable()) {
|
||||
return;
|
||||
}
|
||||
bool is_enabled = !(regs.viewport_clip_control.geometry_clip ==
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
@@ -92,10 +93,12 @@ public:
|
||||
requires std::is_invocable_v<T, vk::CommandBuffer, vk::CommandBuffer>
|
||||
void RecordWithUploadBuffer(T&& command) {
|
||||
if (chunk->Record(command)) {
|
||||
record_serial.fetch_add(1, std::memory_order_relaxed);
|
||||
return;
|
||||
}
|
||||
DispatchWork();
|
||||
(void)chunk->Record(command);
|
||||
record_serial.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -117,6 +120,11 @@ public:
|
||||
return master_semaphore->IsFree(tick);
|
||||
}
|
||||
|
||||
/// Returns a monotonic serial incremented for every recorded command callback.
|
||||
[[nodiscard]] u64 CurrentRecordSerial() const noexcept {
|
||||
return record_serial.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
/// Waits for the given GPU tick, optionally pacing frames.
|
||||
void Wait(u64 tick, double target_fps = 0.0) {
|
||||
if (tick > 0) {
|
||||
@@ -298,6 +306,7 @@ private:
|
||||
u64 frame_counter{};
|
||||
|
||||
u64 last_submitted_tick = 0;
|
||||
std::atomic<u64> record_serial{0};
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -102,7 +105,7 @@ struct ImageBase {
|
||||
VAddr cpu_addr_end = 0;
|
||||
|
||||
u64 modification_tick = 0;
|
||||
size_t lru_index = SIZE_MAX;
|
||||
u64 last_use_tick = 0;
|
||||
|
||||
std::array<u32, MAX_MIP_LEVELS> mip_level_offsets{};
|
||||
|
||||
|
||||
@@ -159,11 +159,54 @@ void TextureCache<P>::RunGarbageCollector() {
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const auto CollectBelow = [this](u64 threshold) {
|
||||
boost::container::small_vector<ImageId, 64> expired;
|
||||
for (auto [id, image] : slot_images) {
|
||||
if (image->last_use_tick < threshold) {
|
||||
expired.push_back(id);
|
||||
}
|
||||
}
|
||||
return expired;
|
||||
};
|
||||
|
||||
// Aggressively clear massive sparse textures
|
||||
if (total_used_memory >= expected_memory) {
|
||||
auto candidates = CollectBelow(frame_tick);
|
||||
for (const auto image_id : candidates) {
|
||||
auto& image = slot_images[image_id];
|
||||
if (image.info.is_sparse &&
|
||||
image.guest_size_bytes >= 256_MiB &&
|
||||
image.allocation_tick < frame_tick - 3) {
|
||||
LOG_DEBUG(HW_GPU, "GC targeting old sparse texture at 0x{:X} ({} MiB, age: {} frames)",
|
||||
image.gpu_addr, image.guest_size_bytes / (1024 * 1024),
|
||||
frame_tick - image.allocation_tick);
|
||||
if (Cleanup(image_id)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Configure(false);
|
||||
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, Cleanup);
|
||||
{
|
||||
auto expired = CollectBelow(frame_tick - ticks_to_destroy);
|
||||
for (const auto image_id : expired) {
|
||||
if (Cleanup(image_id)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If pressure is still too high, prune aggressively.
|
||||
if (total_used_memory >= critical_memory) {
|
||||
Configure(true);
|
||||
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, Cleanup);
|
||||
auto expired = CollectBelow(frame_tick - ticks_to_destroy);
|
||||
for (const auto image_id : expired) {
|
||||
if (Cleanup(image_id)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1865,7 +1908,7 @@ std::pair<u32, u32> TextureCache<P>::PrepareDmaImage(ImageId dst_id, GPUVAddr ba
|
||||
const auto base = image.TryFindBase(base_addr);
|
||||
PrepareImage(dst_id, mark_as_modified, false);
|
||||
const auto& new_image = slot_images[dst_id];
|
||||
lru_cache.Touch(new_image.lru_index, frame_tick);
|
||||
new_image.last_use_tick = frame_tick;
|
||||
return std::make_pair(base->level, base->layer);
|
||||
}
|
||||
|
||||
@@ -2192,7 +2235,7 @@ void TextureCache<P>::RegisterImage(ImageId image_id) {
|
||||
tentative_size = TranscodedAstcSize(tentative_size, image.info.format);
|
||||
}
|
||||
total_used_memory += Common::AlignUp(tentative_size, 1024);
|
||||
image.lru_index = lru_cache.Insert(image_id, frame_tick);
|
||||
image.last_use_tick = frame_tick;
|
||||
|
||||
ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, [this, image_id](u64 page) {
|
||||
(*channel_state->gpu_page_table)[page].push_back(image_id);
|
||||
@@ -2226,7 +2269,7 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
|
||||
"Trying to unregister an already registered image");
|
||||
image.flags &= ~ImageFlagBits::Registered;
|
||||
image.flags &= ~ImageFlagBits::BadOverlap;
|
||||
lru_cache.Free(image.lru_index);
|
||||
|
||||
const auto& clear_page_table =
|
||||
[image_id](u64 page, ankerl::unordered_dense::map<u64, std::vector<ImageId>, Common::IdentityHash<u64>>& selected_page_table) {
|
||||
const auto page_it = selected_page_table.find(page);
|
||||
@@ -2554,7 +2597,7 @@ void TextureCache<P>::PrepareImage(ImageId image_id, bool is_modification, bool
|
||||
if (is_modification) {
|
||||
MarkModification(image);
|
||||
}
|
||||
lru_cache.Touch(image.lru_index, frame_tick);
|
||||
image.last_use_tick = frame_tick;
|
||||
}
|
||||
|
||||
template <class P>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/hash.h"
|
||||
#include "common/literals.h"
|
||||
#include "common/lru_cache.h"
|
||||
|
||||
#include <ranges>
|
||||
#include "common/scratch_buffer.h"
|
||||
#include "common/slot_vector.h"
|
||||
@@ -485,11 +485,7 @@ private:
|
||||
std::deque<std::vector<AsyncBuffer>> async_buffers;
|
||||
std::deque<AsyncBuffer> async_buffers_death_ring;
|
||||
|
||||
struct LRUItemParams {
|
||||
using ObjectType = ImageId;
|
||||
using TickType = u64;
|
||||
};
|
||||
Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache;
|
||||
|
||||
|
||||
#ifdef YUZU_LEGACY
|
||||
static constexpr size_t TICKS_TO_DESTROY = 6;
|
||||
|
||||
@@ -75,15 +75,3 @@ if (NOT MSVC)
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-unused-parameter>
|
||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-missing-field-initializers>)
|
||||
endif()
|
||||
|
||||
if (PLATFORM_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()
|
||||
|
||||
@@ -5,9 +5,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
#include "common/logging.h"
|
||||
#include "common/scm_rev.h"
|
||||
@@ -166,57 +163,85 @@ void EmuWindow_SDL3::Fullscreen() {
|
||||
}
|
||||
}
|
||||
|
||||
void EmuWindow_SDL3::OnEvent(SDL_Event& event) {
|
||||
// Notice how we skip the "update title" aspect on most events
|
||||
// this is because some WMs do NOT like changing titles while resizing
|
||||
// so let's just... not do that, thanks :)
|
||||
// Afterall we don't really expect the user to pay attention to the titlebar
|
||||
// while they're moving a lot of shit around...
|
||||
void EmuWindow_SDL3::WaitEvent() {
|
||||
// Called on main thread
|
||||
SDL_Event event;
|
||||
|
||||
if (!SDL_WaitEvent(&event)) {
|
||||
const char* error = SDL_GetError();
|
||||
if (!error || strcmp(error, "") == 0) {
|
||||
// https://github.com/libsdl-org/SDL/issues/5780
|
||||
// Sometimes SDL will return without actually having hit an error condition;
|
||||
// just ignore it in this case.
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_CRITICAL(Frontend, "SDL_WaitEvent failed: {}", error);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
case SDL_EVENT_WINDOW_RESIZED:
|
||||
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
|
||||
case SDL_EVENT_WINDOW_MAXIMIZED:
|
||||
case SDL_EVENT_WINDOW_RESTORED:
|
||||
return OnResize();
|
||||
OnResize();
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_MINIMIZED:
|
||||
is_shown = false;
|
||||
return OnResize();
|
||||
OnResize();
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_EXPOSED:
|
||||
is_shown = true;
|
||||
return OnResize();
|
||||
OnResize();
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
||||
is_open = false;
|
||||
return;
|
||||
break;
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
case SDL_EVENT_KEY_UP:
|
||||
return OnKeyEvent(int(event.key.scancode), event.key.down ? 1 : 0);
|
||||
OnKeyEvent(static_cast<int>(event.key.scancode), event.key.down ? 1 : 0);
|
||||
break;
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
// ignore if it came from touch
|
||||
if (event.button.which != SDL_TOUCH_MOUSEID)
|
||||
OnMouseMotion(event.motion.x, event.motion.y);
|
||||
return;
|
||||
break;
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||
// ignore if it came from touch
|
||||
if (event.button.which != SDL_TOUCH_MOUSEID) {
|
||||
OnMouseButton(event.button.button, event.button.down ? 1 : 0, s32(event.button.x), s32(event.button.y));
|
||||
OnMouseButton(event.button.button, event.button.down ? 1 : 0,
|
||||
static_cast<s32>(event.button.x), static_cast<s32>(event.button.y));
|
||||
}
|
||||
return;
|
||||
break;
|
||||
case SDL_EVENT_FINGER_DOWN:
|
||||
return OnFingerDown(event.tfinger.x, event.tfinger.y, std::size_t(event.tfinger.touchID));
|
||||
OnFingerDown(event.tfinger.x, event.tfinger.y,
|
||||
static_cast<std::size_t>(event.tfinger.touchID));
|
||||
break;
|
||||
case SDL_EVENT_FINGER_MOTION:
|
||||
return OnFingerMotion(event.tfinger.x, event.tfinger.y, std::size_t(event.tfinger.touchID));
|
||||
OnFingerMotion(event.tfinger.x, event.tfinger.y,
|
||||
static_cast<std::size_t>(event.tfinger.touchID));
|
||||
break;
|
||||
case SDL_EVENT_FINGER_UP:
|
||||
return OnFingerUp();
|
||||
OnFingerUp();
|
||||
break;
|
||||
case SDL_EVENT_QUIT:
|
||||
is_open = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (auto const current_time = SDL_GetTicks(); current_time > last_time + 2000) {
|
||||
auto const results = system.GetAndResetPerfStats();
|
||||
auto const title = fmt::format("{} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc, results.average_game_fps, results.emulation_speed * 100.0);
|
||||
|
||||
const u32 current_time = SDL_GetTicks();
|
||||
if (current_time > last_time + 2000) {
|
||||
const auto results = system.GetAndResetPerfStats();
|
||||
const auto title = fmt::format("{} | {}-{} | FPS: {:.0f} ({:.0f}%)",
|
||||
Common::g_build_fullname,
|
||||
Common::g_scm_branch,
|
||||
Common::g_scm_desc,
|
||||
results.average_game_fps,
|
||||
results.emulation_speed * 100.0);
|
||||
SDL_SetWindowTitle(render_window, title.c_str());
|
||||
last_time = current_time;
|
||||
}
|
||||
@@ -224,9 +249,6 @@ void EmuWindow_SDL3::OnEvent(SDL_Event& event) {
|
||||
|
||||
// Credits to Samantas5855 and others for this function.
|
||||
void EmuWindow_SDL3::SetWindowIcon() {
|
||||
#if defined(__EMSCRIPTEN__) || defined(__wasi__)
|
||||
// Icons do not work yet
|
||||
#else
|
||||
SDL_IOStream* const yuzu_icon_stream = SDL_IOFromConstMem((void*)yuzu_icon, yuzu_icon_size);
|
||||
if (yuzu_icon_stream == nullptr) {
|
||||
LOG_WARNING(Frontend, "Failed to create Eden icon stream.");
|
||||
@@ -240,7 +262,6 @@ void EmuWindow_SDL3::SetWindowIcon() {
|
||||
// The icon is attached to the window pointer
|
||||
SDL_SetWindowIcon(render_window, window_icon);
|
||||
SDL_DestroySurface(window_icon);
|
||||
#endif
|
||||
}
|
||||
|
||||
void EmuWindow_SDL3::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) {
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "core/frontend/graphics_context.h"
|
||||
|
||||
struct SDL_Window;
|
||||
union SDL_Event;
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
@@ -36,7 +35,7 @@ public:
|
||||
bool IsShown() const override;
|
||||
|
||||
/// Wait for the next event on the main thread.
|
||||
void OnEvent(SDL_Event& event);
|
||||
void WaitEvent();
|
||||
|
||||
// Sets the window icon from yuzu.bmp
|
||||
void SetWindowIcon();
|
||||
|
||||
+73
-105
@@ -8,12 +8,6 @@
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include "common/settings_enums.h"
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
#define SDL_MAIN_USE_CALLBACKS 1
|
||||
#include <SDL3/SDL_main.h>
|
||||
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
@@ -46,7 +40,9 @@
|
||||
#ifdef _WIN32
|
||||
// windows.h needs to be included before shellapi.h
|
||||
#include <windows.h>
|
||||
|
||||
#include <shellapi.h>
|
||||
|
||||
#include "common/windows/timer_resolution.h"
|
||||
#endif
|
||||
|
||||
@@ -179,15 +175,8 @@ static void OnStatusMessageReceived(const Network::StatusMessageEntry& msg) {
|
||||
std::cout << std::endl << "* " << message << std::endl << std::endl;
|
||||
}
|
||||
|
||||
struct SdlState {
|
||||
Common::DetachedTasks detached_tasks{};
|
||||
Core::System system{};
|
||||
std::unique_ptr<EmuWindow_SDL3> emu_window;
|
||||
};
|
||||
|
||||
extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
||||
SdlState* state = new SdlState();
|
||||
|
||||
/// Application entry point
|
||||
int main(int argc, char** argv) {
|
||||
#ifdef _WIN32
|
||||
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||
freopen("CONOUT$", "wb", stdout);
|
||||
@@ -198,14 +187,16 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
||||
Common::Log::Initialize();
|
||||
Common::Log::SetColorConsoleBackendEnabled(true);
|
||||
Common::Log::Start();
|
||||
Common::DetachedTasks detached_tasks;
|
||||
|
||||
int option_index = 0;
|
||||
#ifdef _WIN32
|
||||
int argc_w;
|
||||
auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w);
|
||||
|
||||
if (argv_w == nullptr) {
|
||||
LOG_CRITICAL(Frontend, "Failed to get command line arguments");
|
||||
return SDL_APP_FAILURE;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
std::string filepath;
|
||||
@@ -215,13 +206,10 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
||||
std::optional<u16> override_gdb_port{};
|
||||
bool use_multiplayer = false;
|
||||
bool fullscreen = false;
|
||||
bool force_null_render = false;
|
||||
bool force_single_core = false;
|
||||
std::string nickname{};
|
||||
std::string password{};
|
||||
std::string address{};
|
||||
std::string input_profile{};
|
||||
std::optional<std::string> log_filter{};
|
||||
u16 port = Network::DefaultRoomPort;
|
||||
|
||||
static struct option long_options[] = {
|
||||
@@ -236,9 +224,6 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
||||
{"user", required_argument, 0, 'u'},
|
||||
{"version", no_argument, 0, 'v'},
|
||||
{"input-profile", no_argument, 0, 'i'},
|
||||
{"null-render", no_argument, 0, 'n'},
|
||||
{"singlecore", no_argument, 0, 's'},
|
||||
{"filter", no_argument, 0, 'x'},
|
||||
{0, 0, 0, 0},
|
||||
// clang-format on
|
||||
};
|
||||
@@ -259,7 +244,7 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
||||
break;
|
||||
case 'h':
|
||||
PrintHelp(argv[0]);
|
||||
return SDL_APP_FAILURE;
|
||||
return 0;
|
||||
case 'g':
|
||||
filepath = std::string(optarg);
|
||||
break;
|
||||
@@ -276,7 +261,7 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
||||
if (!std::regex_match(str_arg, re)) {
|
||||
std::cout << "Wrong format for option --multiplayer\n";
|
||||
PrintHelp(argv[0]);
|
||||
return SDL_APP_FAILURE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::smatch match;
|
||||
@@ -286,16 +271,17 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
||||
password = match[2];
|
||||
address = match[3];
|
||||
if (!match[4].str().empty()) {
|
||||
port = u16(std::strtoul(match[4].str().c_str(), nullptr, 0));
|
||||
port = static_cast<u16>(std::strtoul(match[4].str().c_str(), nullptr, 0));
|
||||
}
|
||||
std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$");
|
||||
if (!std::regex_match(nickname, nickname_re)) {
|
||||
LOG_ERROR(Frontend, "Nickname is not valid. Must be 4 to 20 alphanumeric characters");
|
||||
return SDL_APP_FAILURE;
|
||||
std::cout
|
||||
<< "Nickname is not valid. Must be 4 to 20 alphanumeric characters.\n";
|
||||
return 0;
|
||||
}
|
||||
if (address.empty()) {
|
||||
LOG_ERROR(Frontend, "Address to room must not be empty");
|
||||
return SDL_APP_FAILURE;
|
||||
std::cout << "Address to room must not be empty.\n";
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -308,17 +294,7 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
||||
break;
|
||||
case 'v':
|
||||
PrintVersion();
|
||||
return SDL_APP_FAILURE;
|
||||
case 'n':
|
||||
force_null_render = true;
|
||||
break;
|
||||
case 's':
|
||||
force_single_core = true;
|
||||
break;
|
||||
case 'x':
|
||||
log_filter = argv[optind];
|
||||
++optind;
|
||||
break;
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
#ifdef _WIN32
|
||||
@@ -335,7 +311,7 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
||||
// apply the log_filter setting
|
||||
// the logger was initialized before and doesn't pick up the filter on its own
|
||||
Common::Log::Filter filter;
|
||||
filter.ParseFilterString(log_filter.value_or(Settings::values.log_filter.GetValue()));
|
||||
filter.ParseFilterString(Settings::values.log_filter.GetValue());
|
||||
Common::Log::SetGlobalFilter(filter);
|
||||
|
||||
if (!program_args.empty()) {
|
||||
@@ -356,87 +332,85 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
||||
Settings::values.gdbstub_port = *override_gdb_port;
|
||||
}
|
||||
|
||||
if (force_single_core) {
|
||||
Settings::values.use_multi_core = false;
|
||||
}
|
||||
|
||||
if (force_null_render) {
|
||||
Settings::values.renderer_backend = Settings::RendererBackend::Null;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
LocalFree(argv_w);
|
||||
#endif
|
||||
|
||||
if (filepath.empty()) {
|
||||
LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified");
|
||||
return SDL_APP_FAILURE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
state->system.Initialize();
|
||||
Core::System system{};
|
||||
system.Initialize();
|
||||
|
||||
InputCommon::InputSubsystem input_subsystem{};
|
||||
|
||||
// Apply the command line arguments
|
||||
state->system.ApplySettings();
|
||||
system.ApplySettings();
|
||||
|
||||
std::unique_ptr<EmuWindow_SDL3> emu_window;
|
||||
switch (Settings::values.renderer_backend.GetValue()) {
|
||||
#ifdef HAS_OPENGL
|
||||
case Settings::RendererBackend::OpenGL_GLSL:
|
||||
case Settings::RendererBackend::OpenGL_GLASM:
|
||||
case Settings::RendererBackend::OpenGL_SPIRV:
|
||||
state->emu_window = std::make_unique<EmuWindow_SDL3_GL>(&input_subsystem, state->system, fullscreen);
|
||||
emu_window = std::make_unique<EmuWindow_SDL3_GL>(&input_subsystem, system, fullscreen);
|
||||
break;
|
||||
#endif
|
||||
case Settings::RendererBackend::Vulkan:
|
||||
state->emu_window = std::make_unique<EmuWindow_SDL3_VK>(&input_subsystem, state->system, fullscreen);
|
||||
emu_window = std::make_unique<EmuWindow_SDL3_VK>(&input_subsystem, system, fullscreen);
|
||||
break;
|
||||
case Settings::RendererBackend::Null:
|
||||
state->emu_window = std::make_unique<EmuWindow_SDL3_Null>(&input_subsystem, state->system, fullscreen);
|
||||
emu_window = std::make_unique<EmuWindow_SDL3_Null>(&input_subsystem, system, fullscreen);
|
||||
break;
|
||||
default:
|
||||
LOG_CRITICAL(Frontend, "Invalid renderer backend");
|
||||
return SDL_APP_FAILURE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
Common::Windows::SetCurrentTimerResolutionToMaximum();
|
||||
state->system.CoreTiming().SetTimerResolutionNs(Common::Windows::GetCurrentTimerResolution());
|
||||
system.CoreTiming().SetTimerResolutionNs(Common::Windows::GetCurrentTimerResolution());
|
||||
#endif
|
||||
|
||||
state->system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
|
||||
state->system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
|
||||
state->system.GetFileSystemController().CreateFactories(*state->system.GetFilesystem());
|
||||
state->system.GetUserChannel().clear();
|
||||
system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
|
||||
system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
|
||||
system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
|
||||
system.GetUserChannel().clear();
|
||||
|
||||
Service::AM::FrontendAppletParameters load_parameters{
|
||||
.applet_id = Service::AM::AppletId::Application,
|
||||
};
|
||||
const Core::SystemResultStatus load_result = state->system.Load(*state->emu_window, filepath, load_parameters);
|
||||
const Core::SystemResultStatus load_result{system.Load(*emu_window, filepath, load_parameters)};
|
||||
|
||||
switch (load_result) {
|
||||
case Core::SystemResultStatus::Success:
|
||||
break; // Expected case
|
||||
case Core::SystemResultStatus::ErrorGetLoader:
|
||||
LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", filepath);
|
||||
return SDL_APP_FAILURE;
|
||||
return -1;
|
||||
case Core::SystemResultStatus::ErrorLoader:
|
||||
LOG_CRITICAL(Frontend, "Failed to load ROM!");
|
||||
return SDL_APP_FAILURE;
|
||||
return -1;
|
||||
case Core::SystemResultStatus::ErrorNotInitialized:
|
||||
LOG_CRITICAL(Frontend, "CPUCore not initialized");
|
||||
return SDL_APP_FAILURE;
|
||||
return -1;
|
||||
case Core::SystemResultStatus::ErrorVideoCore:
|
||||
LOG_CRITICAL(Frontend, "Failed to initialize VideoCore!");
|
||||
return SDL_APP_FAILURE;
|
||||
return -1;
|
||||
case Core::SystemResultStatus::Success:
|
||||
break; // Expected case
|
||||
default:
|
||||
const u16 loader_id = u16(Core::SystemResultStatus::ErrorLoader);
|
||||
const u16 error_id = u16(load_result) - loader_id;
|
||||
LOG_CRITICAL(Frontend,
|
||||
"While attempting to load the ROM requested, an error occurred. Please "
|
||||
"refer to the Eden wiki for more information or the Eden discord for "
|
||||
"additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",
|
||||
loader_id, error_id, Loader::ResultStatus(error_id));
|
||||
return SDL_APP_FAILURE;
|
||||
if (static_cast<u32>(load_result) >
|
||||
static_cast<u32>(Core::SystemResultStatus::ErrorLoader)) {
|
||||
const u16 loader_id = static_cast<u16>(Core::SystemResultStatus::ErrorLoader);
|
||||
const u16 error_id = static_cast<u16>(load_result) - loader_id;
|
||||
LOG_CRITICAL(Frontend,
|
||||
"While attempting to load the ROM requested, an error occurred. Please "
|
||||
"refer to the Eden wiki for more information or the Eden discord for "
|
||||
"additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",
|
||||
loader_id, error_id, static_cast<Loader::ResultStatus>(error_id));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (use_multiplayer) {
|
||||
@@ -445,47 +419,41 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
||||
member->BindOnStatusMessageReceived(OnStatusMessageReceived);
|
||||
member->BindOnStateChanged(OnStateChanged);
|
||||
member->BindOnError(OnNetworkError);
|
||||
LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port, nickname);
|
||||
LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port,
|
||||
nickname);
|
||||
member->Join(nickname, address.c_str(), port, 0, Network::NoPreferredIP, password);
|
||||
} else {
|
||||
LOG_ERROR(Network, "Could not access RoomMember");
|
||||
return SDL_APP_FAILURE;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Core is loaded, start the GPU (makes the GPU contexts current to this thread)
|
||||
state->system.GPU().Start();
|
||||
state->system.GetCpuManager().OnGpuReady();
|
||||
system.GPU().Start();
|
||||
system.GetCpuManager().OnGpuReady();
|
||||
|
||||
if (Settings::values.use_disk_shader_cache.GetValue()) {
|
||||
state->system.Renderer().ReadRasterizer()->LoadDiskResources(
|
||||
state->system.GetApplicationProcessProgramID(), std::stop_token{},
|
||||
system.Renderer().ReadRasterizer()->LoadDiskResources(
|
||||
system.GetApplicationProcessProgramID(), std::stop_token{},
|
||||
[](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
|
||||
}
|
||||
|
||||
// don't do anything, SDL3 already exists for us :D
|
||||
state->system.RegisterExitCallback([] {});
|
||||
void(state->system.Run());
|
||||
if (state->system.DebuggerEnabled())
|
||||
state->system.InitializeDebugger();
|
||||
return SDL_APP_SUCCESS;
|
||||
}
|
||||
extern "C" SDL_AppResult SDL_AppIterate(void *appstate) {
|
||||
SdlState *state = (SdlState *)appstate;
|
||||
return state->emu_window->IsOpen() ? SDL_APP_CONTINUE : SDL_APP_SUCCESS;
|
||||
}
|
||||
extern "C" SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) {
|
||||
SdlState *state = (SdlState *)appstate;
|
||||
state->emu_window->OnEvent(*event);
|
||||
return SDL_APP_SUCCESS;
|
||||
}
|
||||
extern "C" void SDL_AppQuit(void *appstate, SDL_AppResult result) {
|
||||
SdlState *state = (SdlState *)appstate;
|
||||
state->system.DetachDebugger();
|
||||
void(state->system.Pause());
|
||||
state->system.ShutdownMainProcess();
|
||||
state->detached_tasks.WaitForAllTasks();
|
||||
delete state;
|
||||
system.RegisterExitCallback([&] {
|
||||
// Just exit right away.
|
||||
exit(0);
|
||||
});
|
||||
void(system.Run());
|
||||
if (system.DebuggerEnabled()) {
|
||||
system.InitializeDebugger();
|
||||
}
|
||||
while (emu_window->IsOpen()) {
|
||||
emu_window->WaitEvent();
|
||||
}
|
||||
system.DetachDebugger();
|
||||
void(system.Pause());
|
||||
system.ShutdownMainProcess();
|
||||
detached_tasks.WaitForAllTasks();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define VMA_IMPLEMENTATION
|
||||
|
||||
@@ -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).
|
||||
- `cpp-lint.sh`: Homemade dumb C++ linter.
|
||||
- `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
|
||||
It's recommended to run these scritps after almost any Android change, as they are relatively fast and important both for APK bloat and CI.
|
||||
|
||||
@@ -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}`);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user